From 1e7c4db769e0638d3c0833a941ffb475100a5c0d Mon Sep 17 00:00:00 2001 From: Anthony Drendel Date: Sat, 5 Jun 2021 00:54:41 +0200 Subject: [PATCH] Update to Wasm3 v0.5.0 (#11) Update to [v0.5.0 release](https://github.com/wasm3/wasm3/releases/tag/v0.5.0) of [Wasm3](https://github.com/wasm3/wasm3) --- README.md | 2 +- Sources/CWasm3/include/m3_api_defs.h | 48 - Sources/CWasm3/include/m3_compile.h | 92 +- Sources/CWasm3/include/m3_config.h | 17 +- Sources/CWasm3/include/m3_config_platforms.h | 273 +--- Sources/CWasm3/include/m3_core.h | 65 +- Sources/CWasm3/include/m3_emit.h | 2 +- Sources/CWasm3/include/m3_env.h | 84 +- Sources/CWasm3/include/m3_exception.h | 22 +- Sources/CWasm3/include/m3_exec.h | 164 +- Sources/CWasm3/include/m3_exec_defs.h | 52 +- Sources/CWasm3/include/m3_function.h | 102 ++ Sources/CWasm3/include/m3_info.h | 2 +- Sources/CWasm3/include/wasm3.h | 107 +- Sources/CWasm3/include/wasm3_defs.h | 282 ++++ Sources/CWasm3/include/wasm3_ext.h | 51 + Sources/CWasm3/m3_api_libc.c | 125 +- Sources/CWasm3/m3_api_meta_wasi.c | 349 ++++- Sources/CWasm3/m3_api_tracer.c | 1 - Sources/CWasm3/m3_api_uvwasi.c | 581 ++++++- Sources/CWasm3/m3_api_wasi.c | 139 +- Sources/CWasm3/m3_bind.c | 42 +- Sources/CWasm3/m3_code.c | 48 +- Sources/CWasm3/m3_compile.c | 1483 +++++++++++------- Sources/CWasm3/m3_core.c | 139 +- Sources/CWasm3/m3_emit.c | 10 +- Sources/CWasm3/m3_env.c | 531 ++++--- Sources/CWasm3/m3_extensions.c | 112 ++ Sources/CWasm3/m3_function.c | 233 +++ Sources/CWasm3/m3_info.c | 176 ++- Sources/CWasm3/m3_module.c | 82 +- Sources/CWasm3/m3_parse.c | 138 +- Tests/CWasm3Tests/CWasm3Tests.swift | 13 +- VERSION | 2 +- update-wasm3 | 5 +- 35 files changed, 3736 insertions(+), 1838 deletions(-) delete mode 100644 Sources/CWasm3/include/m3_api_defs.h create mode 100644 Sources/CWasm3/include/m3_function.h create mode 100644 Sources/CWasm3/include/wasm3_defs.h create mode 100644 Sources/CWasm3/include/wasm3_ext.h create mode 100644 Sources/CWasm3/m3_extensions.c create mode 100644 Sources/CWasm3/m3_function.c diff --git a/README.md b/README.md index a6ca166..6cdbfa4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ To use `CWasm3` with the Swift Package Manager, add a dependency to your Package ```swift let package = Package( dependencies: [ - .package(name: "CWasm3", url: "https://github.com/shareup/cwasm3.git", .upToNextMinor(from: "0.4.7")) + .package(name: "CWasm3", url: "https://github.com/shareup/cwasm3.git", .upToNextMinor(from: "0.5.0")) ] ) ``` diff --git a/Sources/CWasm3/include/m3_api_defs.h b/Sources/CWasm3/include/m3_api_defs.h deleted file mode 100644 index 1b9c159..0000000 --- a/Sources/CWasm3/include/m3_api_defs.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// m3_api_defs.h -// -// Created by Volodymyr Shymanskyy on 12/20/19. -// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. -// - -#ifndef m3_api_defs_h -#define m3_api_defs_h - -#include "m3_core.h" - -// TODO: perform bounds checks -#define m3ApiOffsetToPtr(offset) (void*)((u8*)_mem + (u32)(offset)) -#define m3ApiPtrToOffset(ptr) (u32)((u8*)ptr - (u8*)_mem) - -#define m3ApiReturnType(TYPE) TYPE* raw_return = ((TYPE*) (_sp)); -#define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); -#define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((u32 *) (_sp++))); - -#if d_m3SkipMemoryBoundsCheck -# define m3ApiCheckMem(off, len) -#else -# define m3ApiCheckMem(off, len) { if (UNLIKELY(off == _mem || ((u64)(off) + (len)) > ((u64)(_mem)+runtime->memory.mallocated->length))) m3ApiTrap(m3Err_trapOutOfBoundsMemoryAccess); } -#endif - -#define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) -#define m3ApiReturn(VALUE) { *raw_return = (VALUE); return m3Err_none; } -#define m3ApiTrap(VALUE) { return VALUE; } -#define m3ApiSuccess() { return m3Err_none; } - -# if defined(M3_BIG_ENDIAN) -# define m3ApiReadMem16(ptr) __builtin_bswap16((* (u16 *)(ptr))) -# define m3ApiReadMem32(ptr) __builtin_bswap32((* (u32 *)(ptr))) -# define m3ApiReadMem64(ptr) __builtin_bswap64((* (u64 *)(ptr))) -# define m3ApiWriteMem16(ptr, val) { * (u16 *)(ptr) = __builtin_bswap16((val)); } -# define m3ApiWriteMem32(ptr, val) { * (u32 *)(ptr) = __builtin_bswap32((val)); } -# define m3ApiWriteMem64(ptr, val) { * (u64 *)(ptr) = __builtin_bswap64((val)); } -# else -# define m3ApiReadMem16(ptr) (* (u16 *)(ptr)) -# define m3ApiReadMem32(ptr) (* (u32 *)(ptr)) -# define m3ApiReadMem64(ptr) (* (u64 *)(ptr)) -# define m3ApiWriteMem16(ptr, val) { * (u16 *)(ptr) = (val); } -# define m3ApiWriteMem32(ptr, val) { * (u32 *)(ptr) = (val); } -# define m3ApiWriteMem64(ptr, val) { * (u64 *)(ptr) = (val); } -# endif - -#endif // m3_api_defs_h diff --git a/Sources/CWasm3/include/m3_compile.h b/Sources/CWasm3/include/m3_compile.h index b6fd4f5..89fa201 100644 --- a/Sources/CWasm3/include/m3_compile.h +++ b/Sources/CWasm3/include/m3_compile.h @@ -10,6 +10,7 @@ #include "m3_code.h" #include "m3_exec_defs.h" +#include "m3_function.h" d_m3BeginExternC @@ -27,47 +28,34 @@ enum c_waOp_getLocal = 0x20, c_waOp_setLocal = 0x21, c_waOp_teeLocal = 0x22, -}; -typedef struct M3FuncType -{ - struct M3FuncType * next; + c_waOp_getGlobal = 0x23, - u32 numRets; - u32 numArgs; - u8 types[]; // returns, then args -} -M3FuncType; + c_waOp_i32_const = 0x41, + c_waOp_i64_const = 0x42, + c_waOp_f32_const = 0x43, + c_waOp_f64_const = 0x44, + + c_waOp_memoryCopy = 0xfc0a, + c_waOp_memoryFill = 0xfc0b +}; -typedef M3FuncType * IM3FuncType; #define d_FuncRetType(ftype,i) ((ftype)->types[(i)]) #define d_FuncArgType(ftype,i) ((ftype)->types[(ftype)->numRets + (i)]) //----------------------------------------------------------------------------------------------------------------------------------- -// since the end location of a block is unknown when a branch is compiled, writing -// the actual address must deferred. A linked-list of patch locations is kept in -// M3CompilationScope. When the block compilation exits, it patches these addresses. -typedef struct M3BranchPatch -{ - struct M3BranchPatch * next; - pc_t * location; -} -M3BranchPatch; - -typedef M3BranchPatch * IM3BranchPatch; - - typedef struct M3CompilationScope { struct M3CompilationScope * outer; pc_t pc; // used by ContinueLoop's - IM3BranchPatch patches; + pc_t patches; i32 depth; -// i32 loopDepth; - i16 initStackIndex; + u16 exitStackIndex; + i16 blockStackIndex; +// u16 topSlot; IM3FuncType type; m3opcode_t opcode; bool isPolymorphic; @@ -76,9 +64,6 @@ M3CompilationScope; typedef M3CompilationScope * IM3CompilationScope; -// double the slot count when using 32-bit slots, since every wasm stack element could be a 64-bit type -//static const u16 c_m3MaxFunctionSlots = d_m3MaxFunctionStackHeight * (d_m3Use32BitSlots + 1); - typedef struct { IM3Runtime runtime; @@ -94,19 +79,22 @@ typedef struct IM3CodePage page; - IM3BranchPatch releasedPatches; - +#ifdef DEBUG u32 numEmits; u32 numOpcodes; +#endif - u16 firstDynamicStackIndex; - u16 stackIndex; // current stack index + u16 stackFirstDynamicIndex; // args and locals are pushed to the stack so that their slot locations can be tracked. the wasm model itself doesn't + // treat these values as being on the stack, so stackFirstDynamicIndex marks the start of the real Wasm stack + u16 stackIndex; // current stack top - u16 firstConstSlotIndex; - u16 maxConstSlotIndex; // as const's are encountered during compilation this tracks their location in the "real" stack + u16 slotFirstConstIndex; + u16 slotMaxConstIndex; // as const's are encountered during compilation this tracks their location in the "real" stack - u16 firstLocalSlotIndex; - u16 firstDynamicSlotIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler. + u16 slotFirstLocalIndex; + u16 slotFirstDynamicIndex; // numArgs + numLocals + numReservedConstants. the first mutable slot available to the compiler. + + u16 maxStackSlots; m3slot_t constants [d_m3MaxConstantTableSize]; @@ -117,7 +105,7 @@ typedef struct // 'm3Slots' contains allocation usage counts u8 m3Slots [d_m3MaxFunctionSlots]; - u16 maxAllocatedSlotPlusOne; + u16 slotMaxAllocatedIndexPlusOne; u16 regStackIndexPlusOne [2]; @@ -152,22 +140,12 @@ M3OpInfo; typedef const M3OpInfo * IM3OpInfo; -extern const M3OpInfo c_operations []; -extern const M3OpInfo c_operationsFC []; - -static inline -const M3OpInfo* GetOpInfo(m3opcode_t opcode) { - switch (opcode >> 8) { - case 0x00: return &c_operations[opcode]; - case 0xFC: return &c_operationsFC[opcode & 0xFF]; - default: return NULL; - } -} +IM3OpInfo GetOpInfo (m3opcode_t opcode); // TODO: This helper should be removed, when MultiValue is implemented static inline u8 GetSingleRetType(IM3FuncType ftype) { - return (ftype && ftype->numRets) ? ftype->types[0] : c_m3Type_none; + return (ftype && ftype->numRets) ? ftype->types[0] : (u8)c_m3Type_none; } #ifdef DEBUG @@ -190,19 +168,19 @@ u8 GetSingleRetType(IM3FuncType ftype) { //----------------------------------------------------------------------------------------------------------------------------------- u16 GetTypeNumSlots (u8 i_type); -void AlignSlotIndexToType (u16 * io_slotIndex, u8 i_type); +void AlignSlotToType (u16 * io_slotIndex, u8 i_type); bool IsRegisterAllocated (IM3Compilation o, u32 i_register); -bool IsRegisterLocation (i16 i_location); -bool IsFpRegisterLocation (i16 i_location); -bool IsIntRegisterLocation (i16 i_location); +bool IsRegisterSlotAlias (u16 i_slot); +bool IsFpRegisterSlotAlias (u16 i_slot); +bool IsIntRegisterSlotAlias (u16 i_slot); bool IsStackPolymorphic (IM3Compilation o); -M3Result CompileBlock (IM3Compilation io, IM3FuncType i_blockType, u8 i_blockOpcode); +M3Result CompileBlock (IM3Compilation io, IM3FuncType i_blockType, m3opcode_t i_blockOpcode); -M3Result Compile_BlockStatements (IM3Compilation io); -M3Result Compile_Function (IM3Function io_function); +M3Result CompileBlockStatements (IM3Compilation io); +M3Result CompileFunction (IM3Function io_function); u16 GetMaxUsedSlotPlusOne (IM3Compilation o); diff --git a/Sources/CWasm3/include/m3_config.h b/Sources/CWasm3/include/m3_config.h index f2a80c1..6a204a3 100644 --- a/Sources/CWasm3/include/m3_config.h +++ b/Sources/CWasm3/include/m3_config.h @@ -24,6 +24,10 @@ # define d_m3MaxFunctionStackHeight 2000 // TODO: comment on upper limit # endif +# ifndef d_m3MaxLinearMemoryPages +# define d_m3MaxLinearMemoryPages 32768 +# endif + # ifndef d_m3MaxFunctionSlots # define d_m3MaxFunctionSlots ((d_m3MaxFunctionStackHeight)*2) # endif @@ -36,8 +40,12 @@ # define d_m3MaxDuplicateFunctionImpl 3 # endif -# ifndef d_m3VerboseLogs -# define d_m3VerboseLogs 1 +# ifndef d_m3EnableExtendedOpcodes +# define d_m3EnableExtendedOpcodes 1 +# endif + +# ifndef d_m3VerboseErrorMessages +# define d_m3VerboseErrorMessages 1 # endif # ifndef d_m3FixedHeap @@ -61,6 +69,11 @@ # define d_m3RecordBacktraces 0 # endif +# ifndef d_m3EnableExceptionBreakpoint +# define d_m3EnableExceptionBreakpoint 0 // see m3_exception.h +# endif + + // profiling and tracing ------------------------------------------------------ # ifndef d_m3EnableOpProfiling diff --git a/Sources/CWasm3/include/m3_config_platforms.h b/Sources/CWasm3/include/m3_config_platforms.h index 05c8e2d..b3b8e8b 100644 --- a/Sources/CWasm3/include/m3_config_platforms.h +++ b/Sources/CWasm3/include/m3_config_platforms.h @@ -8,201 +8,22 @@ #ifndef m3_config_platforms_h #define m3_config_platforms_h +#include "wasm3_defs.h" + /* - * Helpers + * Internal helpers */ -#define M3_STR__(x) #x -#define M3_STR(x) M3_STR__(x) - -#define M3_CONCAT__(a,b) a##b -#define M3_CONCAT(a,b) M3_CONCAT__(a,b) - # if !defined(__cplusplus) || defined(_MSC_VER) # define not ! # define and && # define or || # endif -/* - * Detect platform - */ - -# if defined(__clang__) -# define M3_COMPILER_CLANG 1 -# elif defined(__INTEL_COMPILER) -# define M3_COMPILER_ICC 1 -# elif defined(__GNUC__) || defined(__GNUG__) -# define M3_COMPILER_GCC 1 -# elif defined(_MSC_VER) -# define M3_COMPILER_MSVC 1 -# else -# warning "Compiler not detected" -# endif - -# if defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_GCC) -# if defined(__wasm__) -# define M3_ARCH "wasm" - -# elif defined(__x86_64__) -# define M3_ARCH "x86_64" - -# elif defined(__i386__) -# define M3_ARCH "i386" - -# elif defined(__aarch64__) -# define M3_ARCH "arm64-v8a" - -# elif defined(__arm__) -# if defined(__ARM_ARCH_7A__) -# if defined(__ARM_NEON__) -# if defined(__ARM_PCS_VFP) -# define M3_ARCH "arm-v7a/NEON hard-float" -# else -# define M3_ARCH "arm-v7a/NEON" -# endif -# else -# if defined(__ARM_PCS_VFP) -# define M3_ARCH "arm-v7a hard-float" -# else -# define M3_ARCH "arm-v7a" -# endif -# endif -# else -# define M3_ARCH "arm" -# endif - -# elif defined(__riscv) -# if defined(__riscv_32e) -# define _M3_ARCH_RV "rv32e" -# elif __riscv_xlen == 128 -# define _M3_ARCH_RV "rv128i" -# elif __riscv_xlen == 64 -# define _M3_ARCH_RV "rv64i" -# elif __riscv_xlen == 32 -# define _M3_ARCH_RV "rv32i" -# endif -# if defined(__riscv_muldiv) -# define _M3_ARCH_RV_M _M3_ARCH_RV "m" -# else -# define _M3_ARCH_RV_M _M3_ARCH_RV -# endif -# if defined(__riscv_atomic) -# define _M3_ARCH_RV_A _M3_ARCH_RV_M "a" -# else -# define _M3_ARCH_RV_A _M3_ARCH_RV_M -# endif -# if defined(__riscv_flen) -# define _M3_ARCH_RV_F _M3_ARCH_RV_A "f" -# else -# define _M3_ARCH_RV_F _M3_ARCH_RV_A -# endif -# if defined(__riscv_flen) && __riscv_flen >= 64 -# define _M3_ARCH_RV_D _M3_ARCH_RV_F "d" -# else -# define _M3_ARCH_RV_D _M3_ARCH_RV_F -# endif -# if defined(__riscv_compressed) -# define _M3_ARCH_RV_C _M3_ARCH_RV_D "c" -# else -# define _M3_ARCH_RV_C _M3_ARCH_RV_D -# endif -# define M3_ARCH _M3_ARCH_RV_C - -# elif defined(__mips__) -# if defined(__MIPSEB__) && defined(__mips64) -# define M3_ARCH "mips64 " _MIPS_ARCH -# elif defined(__MIPSEL__) && defined(__mips64) -# define M3_ARCH "mips64el " _MIPS_ARCH -# elif defined(__MIPSEB__) -# define M3_ARCH "mips " _MIPS_ARCH -# elif defined(__MIPSEL__) -# define M3_ARCH "mipsel " _MIPS_ARCH -# endif - -# elif defined(__PPC__) -# if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) -# define M3_ARCH "ppc64le" -# elif defined(__PPC64__) -# define M3_ARCH "ppc64" -# else -# define M3_ARCH "ppc" -# endif - -# elif defined(__sparc__) -# if defined(__arch64__) -# define M3_ARCH "sparc64" -# else -# define M3_ARCH "sparc" -# endif - -# elif defined(__s390x__) -# define M3_ARCH "s390x" - -# elif defined(__alpha__) -# define M3_ARCH "alpha" - -# elif defined(__m68k__) -# define M3_ARCH "m68k" - -# elif defined(__xtensa__) -# define M3_ARCH "xtensa" - -# elif defined(__arc__) -# define M3_ARCH "arc32" - -# elif defined(__AVR__) -# define M3_ARCH "avr" -# endif -# endif - -# if defined(M3_COMPILER_MSVC) -# if defined(_M_X64) -# define M3_ARCH "x86_64" -# elif defined(_M_IX86) -# define M3_ARCH "i386" -# elif defined(_M_ARM64) -# define M3_ARCH "arm64" -# elif defined(_M_ARM) -# define M3_ARCH "arm" -# endif -# endif - -# if !defined(M3_ARCH) -# warning "Architecture not detected" -# define M3_ARCH "unknown" -# endif - -# if defined(M3_COMPILER_CLANG) -# if defined(WIN32) -# define M3_COMPILER_VER __VERSION__ " for Windows" -# else -# define M3_COMPILER_VER __VERSION__ -# endif -# elif defined(M3_COMPILER_GCC) -# define M3_COMPILER_VER "GCC " __VERSION__ -# elif defined(M3_COMPILER_MSVC) -# define M3_COMPILER_VER "MSVC " M3_STR(_MSC_VER) -# else -# define M3_COMPILER_VER "unknown" -# endif - /* * Detect/define features */ -# ifdef __has_feature -# define M3_COMPILER_HAS_FEATURE(x) __has_feature(x) -# else -# define M3_COMPILER_HAS_FEATURE(x) 0 -# endif - -# ifdef __has_builtin -# define M3_COMPILER_HAS_BUILTIN(x) __has_builtin(x) -# else -# define M3_COMPILER_HAS_BUILTIN(x) 0 -# endif - # if defined(M3_COMPILER_MSVC) # include # if UINTPTR_MAX == 0xFFFFFFFF @@ -218,64 +39,6 @@ # error "Pointer size not detected" # endif -# if defined(M3_COMPILER_MSVC) -# define M3_LITTLE_ENDIAN //_byteswap_ushort, _byteswap_ulong, _byteswap_uint64 -# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define M3_LITTLE_ENDIAN -# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -# define M3_BIG_ENDIAN -# else -# error "Byte order not detected" -# endif - -// Byte swapping (for Big-Endian systems only) - -# if defined(M3_COMPILER_MSVC) -# define m3_bswap16(x) _byteswap_ushort((x)) -# define m3_bswap32(x) _byteswap_ulong((x)) -# define m3_bswap64(x) _byteswap_uint64((x)) -# elif defined(M3_COMPILER_GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) -// __builtin_bswap32/64 added in gcc 4.3, __builtin_bswap16 added in gcc 4.8 -# define m3_bswap16(x) __builtin_bswap16((x)) -# define m3_bswap32(x) __builtin_bswap32((x)) -# define m3_bswap64(x) __builtin_bswap64((x)) -# elif defined(M3_COMPILER_CLANG) && M3_COMPILER_HAS_BUILTIN(__builtin_bswap16) -# define m3_bswap16(x) __builtin_bswap16((x)) -# define m3_bswap32(x) __builtin_bswap32((x)) -# define m3_bswap64(x) __builtin_bswap64((x)) -# else -# include -# if defined(__bswap_16) -# define m3_bswap16(x) __bswap_16((x)) -# define m3_bswap32(x) __bswap_32((x)) -# define m3_bswap64(x) __bswap_64((x)) -# else -# warning "Using naive (probably slow) bswap operations" - static inline - uint16_t m3_bswap16(uint16_t x) { - return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 )); - } - static inline - uint32_t m3_bswap32(uint32_t x) { - return ((( x & 0xff000000u ) >> 24 ) | - (( x & 0x00ff0000u ) >> 8 ) | - (( x & 0x0000ff00u ) << 8 ) | - (( x & 0x000000ffu ) << 24 )); - } - static inline - uint64_t m3_bswap64(uint64_t x) { - return ((( x & 0xff00000000000000ull ) >> 56 ) | - (( x & 0x00ff000000000000ull ) >> 40 ) | - (( x & 0x0000ff0000000000ull ) >> 24 ) | - (( x & 0x000000ff00000000ull ) >> 8 ) | - (( x & 0x00000000ff000000ull ) << 8 ) | - (( x & 0x0000000000ff0000ull ) << 24 ) | - (( x & 0x000000000000ff00ull ) << 40 ) | - (( x & 0x00000000000000ffull ) << 56 )); - } -# endif -# endif - # if defined(M3_BIG_ENDIAN) # define M3_BSWAP_u8(X) {} # define M3_BSWAP_u16(X) { (X)=m3_bswap16((X)); } @@ -300,21 +63,15 @@ # define M3_BSWAP_f64(X) {} # endif -# if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) -# 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 defined(M3_COMPILER_MSVC) # define M3_WEAK //__declspec(selectany) +# define M3_NO_UBSAN # elif defined(__MINGW32__) # define M3_WEAK //__attribute__((selectany)) +# define M3_NO_UBSAN # else -# define M3_WEAK __attribute__((weak)) +# define M3_WEAK __attribute__((weak)) +# define M3_NO_UBSAN //__attribute__((no_sanitize("undefined"))) # endif # ifndef M3_MIN @@ -406,9 +163,12 @@ typedef int8_t i8; # if defined(ARDUINO) || defined(PARTICLE) || defined(PLATFORMIO) || defined(__MBED__) || \ defined(ESP8266) || defined(ESP32) || defined(BLUE_PILL) || defined(WM_W600) || defined(FOMU) -# ifndef d_m3VerboseLogs -# define d_m3VerboseLogs 0 +# ifndef d_m3VerboseErrorMessages +# define d_m3VerboseErrorMessages 0 # endif +# ifndef d_m3MaxConstantTableSize +# define d_m3MaxConstantTableSize 64 +# endif # ifndef d_m3MaxFunctionStackHeight # define d_m3MaxFunctionStackHeight 64 # endif @@ -417,4 +177,13 @@ typedef int8_t i8; # endif # endif +/* + * Arch-specific defaults + */ +#if defined(__riscv) && __riscv_xlen == 64 +# ifndef d_m3Use32BitSlots +# define d_m3Use32BitSlots 0 +# endif +#endif + #endif // m3_config_platforms_h diff --git a/Sources/CWasm3/include/m3_core.h b/Sources/CWasm3/include/m3_core.h index 10b4d01..86c5853 100644 --- a/Sources/CWasm3/include/m3_core.h +++ b/Sources/CWasm3/include/m3_core.h @@ -27,11 +27,9 @@ d_m3BeginExternC +#define d_m3ImplementFloat (d_m3HasFloat || d_m3NoFloatDynamic) + #if !defined(d_m3ShortTypesDefined) -#if d_m3HasFloat || d_m3NoFloatDynamic -typedef double f64; -typedef float f32; -#endif typedef uint64_t u64; typedef int64_t i64; @@ -41,6 +39,12 @@ typedef uint16_t u16; typedef int16_t i16; typedef uint8_t u8; typedef int8_t i8; + +#if d_m3ImplementFloat +typedef double f64; +typedef float f32; +#endif + #endif // d_m3ShortTypesDefined #define PRIf32 "f" @@ -84,12 +88,6 @@ const void * const cvptr_t; # define m3log_compile(...) {} # endif -# if d_m3LogWasmStack -# define m3log_stack(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) -# else -# define m3log_stack(...) {} -# endif - # if d_m3LogEmit # define m3log_emit(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) # else @@ -121,8 +119,8 @@ const void * const cvptr_t; # endif -# if (defined(DEBUG) || defined(ASSERTS)) && !defined(NASSERTS) -# define d_m3Assert(ASS) assert (ASS) +# if defined(ASSERTS) || (defined(DEBUG) && !defined(NASSERTS)) +# define d_m3Assert(ASS) if (!(ASS)) { printf("Assertion failed at %s:%d : %s\n", __FILE__, __LINE__, #ASS); abort(); } # else # define d_m3Assert(ASS) # endif @@ -161,11 +159,19 @@ M3CodePageHeader; #define d_m3MemPageSize 65536 -#define d_m3Reg0SlotAlias 30000 -#define d_m3Fp0SlotAlias 30001 +#define d_m3Reg0SlotAlias 60000 +#define d_m3Fp0SlotAlias (d_m3Reg0SlotAlias + 2) -#define d_m3MaxSaneUtf8Length 2000 -#define d_m3MaxSaneFunctionArgCount 1000 // still insane, but whatever +#define d_m3MaxSaneTypesCount 100000 +#define d_m3MaxSaneFunctionsCount 100000 +#define d_m3MaxSaneImportsCount 10000 +#define d_m3MaxSaneExportsCount 10000 +#define d_m3MaxSaneGlobalsCount 100000 +#define d_m3MaxSaneElementSegments 100000 +#define d_m3MaxSaneDataSegments 100000 +#define d_m3MaxSaneTableSize 100000 +#define d_m3MaxSaneUtf8Length 10000 +#define d_m3MaxSaneFunctionArgRetCount 1000 // still insane, but whatever #define d_externalKind_function 0 #define d_externalKind_table 1 @@ -176,7 +182,7 @@ static const char * const c_waTypes [] = { "nil", "i32", "i64", "f32", static const char * const c_waCompactTypes [] = { "_", "i", "I", "f", "F", "?" }; -# if d_m3VerboseLogs +# if d_m3VerboseErrorMessages M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...); @@ -202,17 +208,16 @@ int m3StackGetMax (); #define m3StackGetMax() 0 #endif -void m3_Abort (const char* message); -M3Result m3_Malloc (void ** o_ptr, size_t i_size); -M3Result m3_Realloc (void ** io_ptr, size_t i_newSize, size_t i_oldSize); -void m3_Free (void ** io_ptr); -M3Result m3_CopyMem (void ** o_to, const void * i_from, size_t i_size); +void m3_Abort (const char* message); +void * m3_Malloc (size_t i_size); +void * m3_Realloc (void *i_ptr, size_t i_newSize, size_t i_oldSize); +void m3_FreeImpl (void * i_ptr); +void * m3_CopyMem (const void * i_from, size_t i_size); -#define m3Alloc(OPTR, STRUCT, NUM) m3_Malloc ((void **) OPTR, sizeof (STRUCT) * (NUM)) -#define m3ReallocArray(PTR, STRUCT, NEW, OLD) m3_Realloc ((void **) (PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD)) -#define m3Reallocate(_ptr, _newSize, _oldSize) m3_Realloc ((void **) _ptr, _newSize, _oldSize) -#define m3Free(P) m3_Free ((void **)(& P)); -#define m3CopyMem(_to, _from, _size) m3_CopyMem ((void **) _to, (void *) _from, _size) +#define m3_AllocStruct(STRUCT) (STRUCT *)m3_Malloc (sizeof (STRUCT)) +#define m3_AllocArray(STRUCT, NUM) (STRUCT *)m3_Malloc (sizeof (STRUCT) * (NUM)) +#define m3_ReallocArray(STRUCT, PTR, NEW, OLD) (STRUCT *)m3_Realloc ((void *)(PTR), sizeof (STRUCT) * (NEW), sizeof (STRUCT) * (OLD)) +#define m3_Free(P) do { m3_FreeImpl ((void*)(P)); (P) = NULL; } while(0) M3Result NormalizeType (u8 * o_type, i8 i_convolutedWasmType); @@ -223,11 +228,12 @@ u32 SizeOfType (u8 i_m3Type); M3Result Read_u64 (u64 * o_value, bytes_t * io_bytes, cbytes_t i_end); M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end); -#if d_m3HasFloat || d_m3NoFloatDynamic +#if d_m3ImplementFloat M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end); M3Result Read_f32 (f32 * o_value, bytes_t * io_bytes, cbytes_t i_end); #endif M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end); +M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end); M3Result ReadLebUnsigned (u64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); M3Result ReadLebSigned (i64 * o_value, u32 i_maxNumBits, bytes_t * io_bytes, cbytes_t i_end); @@ -238,7 +244,8 @@ M3Result ReadLEB_i32 (i32 * o_value, bytes_t * io_bytes, cbytes_t M3Result ReadLEB_i64 (i64 * o_value, bytes_t * io_bytes, cbytes_t i_end); M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end); -size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type); +cstr_t SPrintValue (void * i_value, u8 i_type); +size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type); void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); diff --git a/Sources/CWasm3/include/m3_emit.h b/Sources/CWasm3/include/m3_emit.h index 5e36583..8b3711f 100644 --- a/Sources/CWasm3/include/m3_emit.h +++ b/Sources/CWasm3/include/m3_emit.h @@ -18,7 +18,7 @@ M3Result EnsureCodePageNumLines (IM3Compilation o, u32 i_numLines); M3Result EmitOp (IM3Compilation o, IM3Operation i_operation); void EmitConstant32 (IM3Compilation o, const u32 i_immediate); void EmitSlotOffset (IM3Compilation o, const i32 i_offset); -void EmitPointer (IM3Compilation o, const void * const i_pointer); +pc_t EmitPointer (IM3Compilation o, const void * const i_pointer); void * ReservePointer (IM3Compilation o); pc_t GetPC (IM3Compilation o); diff --git a/Sources/CWasm3/include/m3_env.h b/Sources/CWasm3/include/m3_env.h index 1155e05..15e8bde 100644 --- a/Sources/CWasm3/include/m3_env.h +++ b/Sources/CWasm3/include/m3_env.h @@ -14,65 +14,6 @@ d_m3BeginExternC -M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes); -bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); - - -//--------------------------------------------------------------------------------------------------------------------------------- -typedef struct M3Function -{ - struct M3Module * module; - - M3ImportInfo import; - - bytes_t wasm; - bytes_t wasmEnd; - - u16 numNames; // maximum of d_m3MaxDuplicateFunctionImpl - cstr_t names[d_m3MaxDuplicateFunctionImpl]; - - IM3FuncType funcType; - - pc_t compiled; - -#if (d_m3EnableCodePageRefCounting) - IM3CodePage * codePageRefs; // array of all pages used - u32 numCodePageRefs; -#endif - -#if defined(DEBUG) - u32 hits; -#endif - -#if d_m3EnableStrace >= 2 - u16 index; -#endif - u16 maxStackSlots; - - u16 numArgSlots; - - u16 numLocals; // not including args - u16 numLocalBytes; - - void * constants; - u16 numConstantBytes; - - //bool ownsWasmCode; -} -M3Function; - -void Function_Release (IM3Function i_function); -void Function_FreeCompiledCode (IM3Function i_function); - -cstr_t GetFunctionImportModuleName (IM3Function i_function); -cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); -u32 GetFunctionNumArgs (IM3Function i_function); -u32 GetFunctionNumReturns (IM3Function i_function); - -u32 GetFunctionNumArgsAndLocals (IM3Function i_function); - -cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); - //--------------------------------------------------------------------------------------------------------------------------------- @@ -100,7 +41,7 @@ typedef M3Memory * IM3Memory; typedef struct M3DataSegment { - const u8 * initExpr; // wasm code + const u8 * initExpr; // wasm code const u8 * data; u32 initExprSize; @@ -109,9 +50,6 @@ typedef struct M3DataSegment } M3DataSegment; - -void FreeImportInfo (M3ImportInfo * i_info); - //--------------------------------------------------------------------------------------------------------------------------------- typedef struct M3Global @@ -127,6 +65,7 @@ typedef struct M3Global #endif }; + cstr_t name; bytes_t initExpr; // wasm code u32 initExprSize; u8 type; @@ -134,7 +73,6 @@ typedef struct M3Global bool isMutable; } M3Global; -typedef M3Global * IM3Global; //--------------------------------------------------------------------------------------------------------------------------------- @@ -149,11 +87,9 @@ typedef struct M3Module cstr_t name; u32 numFuncTypes; - IM3FuncType * funcTypes; // array of pointers to list of FuncTypes - - u32 numImports; - //IM3Function * imports; b // notice: "I" prefix. imports are pointers to functions in another module. + IM3FuncType * funcTypes; // array of pointers to list of FuncTypes + u32 numFuncImports; u32 numFunctions; M3Function * functions; @@ -187,16 +123,20 @@ M3Result Module_AddGlobal (IM3Module io_module, IM M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo /* can be null */); IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex); +void Module_GenerateNames (IM3Module i_module); + +void FreeImportInfo (M3ImportInfo * i_info); + //--------------------------------------------------------------------------------------------------------------------------------- typedef struct M3Environment { // struct M3Runtime * runtimes; - IM3FuncType funcTypes; // linked list - - IM3FuncType retFuncTypes[5]; // the number of elements must match the basic types as per M3ValueType + IM3FuncType funcTypes; // linked list of unique M3FuncType structs that can be compared using pointer-equivalence + IM3FuncType retFuncTypes [c_m3Type_unknown]; // these 'point' to elements in the linked list above. + // the number of elements must match the basic types as per M3ValueType M3CodePage * pagesReleased; } M3Environment; @@ -237,7 +177,7 @@ typedef struct M3Runtime #endif M3ErrorInfo error; -#if d_m3VerboseLogs +#if d_m3VerboseErrorMessages char error_message[256]; // the actual buffer. M3ErrorInfo can point to this #endif diff --git a/Sources/CWasm3/include/m3_exception.h b/Sources/CWasm3/include/m3_exception.h index 3152db4..fa49e4e 100644 --- a/Sources/CWasm3/include/m3_exception.h +++ b/Sources/CWasm3/include/m3_exception.h @@ -4,19 +4,31 @@ // Created by Steven Massey on 7/5/19. // Copyright © 2019 Steven Massey. All rights reserved. // +// some macros to emulate try/catch #ifndef m3_exception_h #define m3_exception_h #include "m3_config.h" -// some macros to emulate try/catch -#define EXCEPTION_PRINT //puts("Exc: " __FILE__ ":" M3_STR(__LINE__) "\n"); +# if d_m3EnableExceptionBreakpoint + +// declared in m3_info.c +void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message); + +# define EXCEPTION_PRINT(ERROR) ExceptionBreakpoint (ERROR, (__FILE__ ":" M3_STR(__LINE__))) + +# else +# define EXCEPTION_PRINT(...) +# endif + #define _try -#define _(TRY) { result = TRY; if (result) { EXCEPTION_PRINT; goto _catch; } } -#define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT; goto _catch; } +#define _(TRY) { result = TRY; if (result) { EXCEPTION_PRINT (result); goto _catch; } } +#define _throw(ERROR) { result = ERROR; EXCEPTION_PRINT (result); goto _catch; } #define _throwif(ERROR, COND) if (UNLIKELY(COND)) \ - { result = ERROR; EXCEPTION_PRINT; goto _catch; } + { result = ERROR; EXCEPTION_PRINT (result); goto _catch; } + +#define _throwifnull(PTR) _throwif (m3Err_mallocFailed, !(PTR)) #endif // m3_exception_h diff --git a/Sources/CWasm3/include/m3_exec.h b/Sources/CWasm3/include/m3_exec.h index 8081e7f..1ebeead 100644 --- a/Sources/CWasm3/include/m3_exec.h +++ b/Sources/CWasm3/include/m3_exec.h @@ -8,7 +8,7 @@ #ifndef m3_exec_h #define m3_exec_h -// TODO: all these functions could move over to the .c at some point. normally, i'd say screw it, +// TODO: all these functions could move over to the .c at some point. normally, I'd say screw it, // but it might prove useful to be able to compile m3_exec alone w/ optimizations while the remaining // code is at debug O0 @@ -90,6 +90,22 @@ d_m3BeginExternC #define d_m3TraceStore(TYPE,offset,val) #endif +#ifdef DEBUG + #define d_outOfBounds newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ + _mem->runtime, "memory size: %zu; access offset: %zu", \ + _mem->length, operand)) + +# define d_outOfBoundsMemOp(OFFSET, SIZE) newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ + _mem->runtime, "memory size: %zu; access offset: %zu; size: %u", \ + _mem->length, OFFSET, SIZE)) +#else + #define d_outOfBounds newTrap (m3Err_trapOutOfBoundsMemoryAccess) + +# define d_outOfBoundsMemOp(OFFSET, SIZE) newTrap (m3Err_trapOutOfBoundsMemoryAccess) + +#endif + + d_m3RetSig Call (d_m3OpSig) { m3ret_t possible_trap = m3_Yield (); @@ -552,7 +568,7 @@ d_m3Op (CallIndirect) if (LIKELY(type == function->funcType)) { if (UNLIKELY(not function->compiled)) - r = Compile_Function (function); + r = CompileFunction (function); if (LIKELY(not r)) { @@ -606,13 +622,14 @@ d_m3Op (CallRawFunction) const int nArgs = ftype->numArgs; const int nRets = ftype->numRets; + u64 * args = sp + nRets; for (int i=0; itypes[nRets + i]; switch (type) { - case c_m3Type_i32: outp += snprintf(outp, oute-outp, "%" PRIi32, *(i32*)(sp+i)); break; - case c_m3Type_i64: outp += snprintf(outp, oute-outp, "%" PRIi64, *(i64*)(sp+i)); break; - case c_m3Type_f32: outp += snprintf(outp, oute-outp, "%" PRIf32, *(f32*)(sp+i)); break; - case c_m3Type_f64: outp += snprintf(outp, oute-outp, "%" PRIf64, *(f64*)(sp+i)); break; + case c_m3Type_i32: outp += snprintf(outp, oute-outp, "%" PRIi32, *(i32*)(args+i)); break; + case c_m3Type_i64: outp += snprintf(outp, oute-outp, "%" PRIi64, *(i64*)(args+i)); break; + case c_m3Type_f32: outp += snprintf(outp, oute-outp, "%" PRIf32, *(f32*)(args+i)); break; + case c_m3Type_f64: outp += snprintf(outp, oute-outp, "%" PRIf64, *(f64*)(args+i)); break; default: outp += snprintf(outp, oute-outp, "", type); break; } outp += snprintf(outp, oute-outp, (i < nArgs-1) ? ", " : ")"); @@ -632,7 +649,7 @@ d_m3Op (CallRawFunction) #if d_m3EnableStrace if (UNLIKELY(possible_trap)) { - d_m3TracePrint("%s -> %s", outbuff, possible_trap); + d_m3TracePrint("%s -> %s", outbuff, (char*)possible_trap); } else { switch (GetSingleRetType(ftype)) { case c_m3Type_none: d_m3TracePrint("%s", outbuff); break; @@ -652,7 +669,7 @@ d_m3Op (CallRawFunction) } -d_m3Op (MemCurrent) +d_m3Op (MemSize) { IM3Memory memory = m3MemInfo (_mem); @@ -685,9 +702,47 @@ d_m3Op (MemGrow) } +d_m3Op (MemCopy) +{ + u32 size = (u32) _r0; + u64 source = slot (u32); + u64 destination = slot (u32); + + if (destination + size <= _mem->length) + { + if (source + size <= _mem->length) + { + u8 * dst = m3MemData (_mem) + destination; + u8 * src = m3MemData (_mem) + source; + memmove (dst, src, size); + + nextOp (); + } + else d_outOfBoundsMemOp (source, size); + } + else d_outOfBoundsMemOp (destination, size); +} + + +d_m3Op (MemFill) +{ + u32 size = (u32) _r0; + u32 byte = slot (u32); + u64 destination = slot (u32); + + if (destination + size <= _mem->length) + { + u8 * mem8 = m3MemData (_mem) + destination; + memset (mem8, (u8) byte, size); + nextOp (); + } + else d_outOfBoundsMemOp (destination, size); +} + + // it's a debate: should the compilation be trigger be the caller or callee page. // it's a much easier to put it in the caller pager. if it's in the callee, either the entire page -// has be left dangling or it's just a stub that jumps to a newly acquire page. In Gestalt, I opted +// has be left dangling or it's just a stub that jumps to a newly acquired page. In Gestalt, I opted // for the stub approach. Stubbing makes it easier to dynamically free the compilation. You can also // do both. d_m3Op (Compile) @@ -699,11 +754,11 @@ d_m3Op (Compile) m3ret_t result = m3Err_none; if (UNLIKELY(not function->compiled)) // check to see if function was compiled since this operation was emitted. - result = Compile_Function (function); + result = CompileFunction (function); if (not result) { - // patch up compiled pc and call rewriten op_Call + // patch up compiled pc and call rewritten op_Call * ((void**) --_pc) = (void*) (function->compiled); --_pc; nextOpDirect (); @@ -726,13 +781,13 @@ d_m3Op (Entry) #if d_m3SkipStackCheck if (true) #else - if (LIKELY((void *)((m3slot_t *) _sp + function->maxStackSlots) < _mem->maxStack)) + if (LIKELY ((void *) (_sp + function->maxStackSlots) < _mem->maxStack)) #endif { #if defined(DEBUG) function->hits++; #endif - u8 * stack = (u8 *) ((m3slot_t *) _sp + function->numArgSlots); + u8 * stack = (u8 *) ((m3slot_t *) _sp + function->numRetAndArgSlots); memset (stack, 0x0, function->numLocalBytes); stack += function->numLocalBytes; @@ -743,13 +798,7 @@ d_m3Op (Entry) } #if d_m3EnableStrace >= 2 - u16 numNames = 0; - cstr_t *names = GetFunctionNames(function, &numNames); - if (numNames) { - d_m3TracePrint("%s %s {", names[0], SPrintFunctionArgList (function, _sp)); - } else { - d_m3TracePrint("$%d %s {", function->index, SPrintFunctionArgList (function, _sp)); - } + d_m3TracePrint("%s %s {", m3_GetFunctionName(function), SPrintFunctionArgList (function, _sp + function->numRetSlots)); trace_rt->callDepth++; #endif @@ -800,7 +849,7 @@ d_m3Op (Loop) d_m3TracePrint("iter {"); trace_rt->callDepth++; #endif - r = nextOpImpl (); // printf ("loop: %p\n", r); + r = nextOpImpl (); #if d_m3EnableStrace >= 3 trace_rt->callDepth--; @@ -1105,32 +1154,38 @@ d_m3Op (BranchIf_s) } -// branching to blocks that produce a (int) value -#define d_m3BranchIf(TYPE, LABEL, COND) \ -d_m3Op (TYPE##_BranchIf_##LABEL##s) \ -{ \ - i32 condition = (i32) COND; \ - TYPE value = slot (TYPE); \ - pc_t branch = immediate (pc_t); \ - \ - if (condition) \ - { \ - _r0 = value; \ - jumpOp (branch); \ - } \ - else nextOp (); \ +d_m3Op (BranchIfPrologue_r) +{ + i32 condition = (i32) _r0; + pc_t branch = immediate (pc_t); + + if (condition) + { + // this is the "prologue" that ends with + // a plain branch to the actual target + nextOp (); + } + else jumpOp (branch); // jump over the prologue } -d_m3BranchIf (i32, r, _r0) -d_m3BranchIf (i64, r, _r0) -d_m3BranchIf (i32, s, slot (i32)) -d_m3BranchIf (i64, s, slot (i32)) +d_m3Op (BranchIfPrologue_s) +{ + i32 condition = slot (i32); + pc_t branch = immediate (pc_t); + if (condition) + { + nextOp (); + } + else jumpOp (branch); +} d_m3Op (ContinueLoop) { + m3StackCheck(); + // TODO: this is where execution can "escape" the M3 code and callback to the client / fiber switch // OR it can go in the Loop operation. I think it's best to do here. adding code to the loop operation // has the potential to increase its native-stack usage. (don't forget ContinueLoopIf too.) @@ -1231,14 +1286,6 @@ d_m3Op (SetGlobal_f64) # define m3MemCheck(x) LIKELY(x) #endif -#ifdef DEBUG - #define d_outOfBounds newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ - _mem->runtime, "memory size: %zu; access offset: %zu", \ - _mem->length, operand)) -#else - #define d_outOfBounds newTrap (m3Err_trapOutOfBoundsMemoryAccess) -#endif - // memcpy here is to support non-aligned access on some platforms. #define d_m3Load(REG,DEST_TYPE,SRC_TYPE) \ @@ -1405,29 +1452,6 @@ d_m3Store_i (i64, i64) #undef m3MemCheck -//--------------------------------------------------------------------------------------------------------------------- -# if 0 //d_m3EnableOptimizations -//--------------------------------------------------------------------------------------------------------------------- - - #define d_m3BinaryOpWith1_i(TYPE, NAME, OPERATION) \ - d_m3Op(TYPE##_##NAME) \ - { \ - _r0 = _r0 OPERATION 1; \ - nextOp (); \ - } - - d_m3BinaryOpWith1_i (u64, Increment, +) - d_m3BinaryOpWith1_i (u32, Decrement, -) - - d_m3BinaryOpWith1_i (u32, ShiftLeft1, <<) - d_m3BinaryOpWith1_i (u64, ShiftLeft1, <<) - - d_m3BinaryOpWith1_i (u32, ShiftRight1, >>) - d_m3BinaryOpWith1_i (u64, ShiftRight1, >>) - -//--------------------------------------------------------------------------------------------------------------------- -# endif - //--------------------------------------------------------------------------------------------------------------------- // debug/profiling diff --git a/Sources/CWasm3/include/m3_exec_defs.h b/Sources/CWasm3/include/m3_exec_defs.h index 7bb4e81..991ce59 100644 --- a/Sources/CWasm3/include/m3_exec_defs.h +++ b/Sources/CWasm3/include/m3_exec_defs.h @@ -12,32 +12,40 @@ d_m3BeginExternC -#define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) -#define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) -#define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) - -#if d_m3HasFloat - -# define d_m3OpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0, f64 _fp0 -# define d_m3OpArgs _sp, _mem, _r0, _fp0 -# define d_m3OpAllArgs _pc, _sp, _mem, _r0, _fp0 -# define d_m3OpDefaultArgs 0, 0.0 -# define d_m3ClearRegisters _r0 = 0; _fp0 = 0.0; - -#else - -# define d_m3OpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 -# define d_m3OpArgs _sp, _mem, _r0 -# define d_m3OpAllArgs _pc, _sp, _mem, _r0 -# define d_m3OpDefaultArgs 0 -# define d_m3ClearRegisters _r0 = 0; - -#endif +# define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) +# define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) +# define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) + +# define d_m3BaseOpSig pc_t _pc, m3stack_t _sp, M3MemoryHeader * _mem, m3reg_t _r0 +# define d_m3BaseOpArgs _sp, _mem, _r0 +# define d_m3BaseOpAllArgs _pc, _sp, _mem, _r0 +# define d_m3BaseOpDefaultArgs 0 +# define d_m3BaseClearRegisters _r0 = 0; + +# define d_m3ExpOpSig(...) d_m3BaseOpSig, __VA_ARGS__ +# define d_m3ExpOpArgs(...) d_m3BaseOpArgs, __VA_ARGS__ +# define d_m3ExpOpAllArgs(...) d_m3BaseOpAllArgs, __VA_ARGS__ +# define d_m3ExpOpDefaultArgs(...) d_m3BaseOpDefaultArgs, __VA_ARGS__ +# define d_m3ExpClearRegisters(...) d_m3BaseClearRegisters; __VA_ARGS__ + +# if d_m3HasFloat +# define d_m3OpSig d_m3ExpOpSig (f64 _fp0) +# define d_m3OpArgs d_m3ExpOpArgs (_fp0) +# define d_m3OpAllArgs d_m3ExpOpAllArgs (_fp0) +# define d_m3OpDefaultArgs d_m3ExpOpDefaultArgs (0.) +# define d_m3ClearRegisters d_m3ExpClearRegisters (_fp0 = 0.;) +# else +# define d_m3OpSig d_m3BaseOpSig +# define d_m3OpArgs d_m3BaseOpArgs +# define d_m3OpAllArgs d_m3BaseOpAllArgs +# define d_m3OpDefaultArgs d_m3BaseOpDefaultArgs +# define d_m3ClearRegisters d_m3BaseClearRegisters +# endif typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); #define d_m3RetSig static inline m3ret_t vectorcall -#define d_m3Op(NAME) op_section d_m3RetSig op_##NAME (d_m3OpSig) +#define d_m3Op(NAME) M3_NO_UBSAN op_section d_m3RetSig op_##NAME (d_m3OpSig) #define nextOpImpl() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) #define jumpOpImpl(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) diff --git a/Sources/CWasm3/include/m3_function.h b/Sources/CWasm3/include/m3_function.h new file mode 100644 index 0000000..e25b3f8 --- /dev/null +++ b/Sources/CWasm3/include/m3_function.h @@ -0,0 +1,102 @@ +// +// m3_function.h +// +// Created by Steven Massey on 4/7/21. +// Copyright © 2021 Steven Massey. All rights reserved. +// + +#ifndef m3_function_h +#define m3_function_h + +#include "m3_core.h" + +d_m3BeginExternC + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3FuncType +{ + struct M3FuncType * next; + + u16 numRets; + u16 numArgs; + u8 types []; // returns, then args +} +M3FuncType; + +typedef M3FuncType * IM3FuncType; + + +M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes); +bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB); + +u16 GetFuncTypeNumParams (const IM3FuncType i_funcType); +u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index); + +u16 GetFuncTypeNumResults (const IM3FuncType i_funcType); +u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index); + +//--------------------------------------------------------------------------------------------------------------------------------- + +typedef struct M3Function +{ + struct M3Module * module; + + M3ImportInfo import; + + bytes_t wasm; + bytes_t wasmEnd; + + cstr_t names[d_m3MaxDuplicateFunctionImpl]; + u16 numNames; // maximum of d_m3MaxDuplicateFunctionImpl + + IM3FuncType funcType; + + pc_t compiled; + +# if (d_m3EnableCodePageRefCounting) + IM3CodePage * codePageRefs; // array of all pages used + u32 numCodePageRefs; +# endif + +# if defined (DEBUG) + u32 hits; + u32 index; +# endif + + u16 maxStackSlots; + + u16 numRetSlots; + u16 numRetAndArgSlots; + + u16 numLocals; // not including args + u16 numLocalBytes; + + bool ownsWasmCode; + + u16 numConstantBytes; + void * constants; +} +M3Function; + +void Function_Release (IM3Function i_function); +void Function_FreeCompiledCode (IM3Function i_function); + +cstr_t GetFunctionImportModuleName (IM3Function i_function); +cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); +u16 GetFunctionNumArgs (IM3Function i_function); +u8 GetFunctionArgType (IM3Function i_function, u32 i_index); + +u16 GetFunctionNumReturns (IM3Function i_function); +u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index); + +u32 GetFunctionNumArgsAndLocals (IM3Function i_function); + +cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp); + +//--------------------------------------------------------------------------------------------------------------------------------- + + +d_m3EndExternC + +#endif /* m3_function_h */ diff --git a/Sources/CWasm3/include/m3_info.h b/Sources/CWasm3/include/m3_info.h index 573f156..19f36cc 100644 --- a/Sources/CWasm3/include/m3_info.h +++ b/Sources/CWasm3/include/m3_info.h @@ -15,7 +15,7 @@ d_m3BeginExternC #ifdef DEBUG void dump_type_stack (IM3Compilation o); -void log_opcode (IM3Compilation o, u8 i_opcode); +void log_opcode (IM3Compilation o, m3opcode_t i_opcode); const char * get_indention_string (IM3Compilation o); void emit_stack_dump (IM3Compilation o); void log_emit (IM3Compilation o, IM3Operation i_operation); diff --git a/Sources/CWasm3/include/wasm3.h b/Sources/CWasm3/include/wasm3.h index c6face8..30f00df 100644 --- a/Sources/CWasm3/include/wasm3.h +++ b/Sources/CWasm3/include/wasm3.h @@ -9,15 +9,17 @@ #define wasm3_h #define M3_VERSION_MAJOR 0 -#define M3_VERSION_MINOR 4 -#define M3_VERSION_REV 9 -#define M3_VERSION "0.4.9" +#define M3_VERSION_MINOR 5 +#define M3_VERSION_REV 0 +#define M3_VERSION "0.5.0" #include #include #include #include +#include "wasm3_defs.h" + #if defined(__cplusplus) extern "C" { #endif @@ -28,6 +30,7 @@ struct M3Environment; typedef struct M3Environment * IM3Environment; struct M3Runtime; typedef struct M3Runtime * IM3Runtime; struct M3Module; typedef struct M3Module * IM3Module; struct M3Function; typedef struct M3Function * IM3Function; +struct M3Global; typedef struct M3Global * IM3Global; typedef struct M3ErrorInfo { @@ -73,6 +76,18 @@ typedef enum M3ValueType c_m3Type_unknown } M3ValueType; +typedef struct M3TaggedValue +{ + M3ValueType type; + union M3ValueUnion + { + uint32_t i32; + uint64_t i64; + float f32; + double f64; + } value; +} +M3TaggedValue, * IM3TaggedValue; typedef struct M3ImportInfo { @@ -104,7 +119,6 @@ M3ImportContext, * IM3ImportContext; d_m3ErrorConst (none, NULL) // general errors -d_m3ErrorConst (typeListOverflow, "type list count exceeds 32 types") d_m3ErrorConst (mallocFailed, "memory allocation failed") // parse errors @@ -119,7 +133,8 @@ d_m3ErrorConst (missingUTF8, "invalid length UTF-8 string") d_m3ErrorConst (wasmSectionUnderrun, "section underrun while parsing Wasm binary") d_m3ErrorConst (wasmSectionOverrun, "section overrun while parsing Wasm binary") d_m3ErrorConst (invalidTypeId, "unknown value_type") -d_m3ErrorConst (tooManyMemorySections, "Wasm MVP can only define one memory per module") +d_m3ErrorConst (tooManyMemorySections, "only one memory per module is supported") +d_m3ErrorConst (tooManyArgsRets, "too many arguments or return values") // link errors d_m3ErrorConst (moduleAlreadyLinked, "attempting to bind module to multiple runtimes") @@ -131,10 +146,13 @@ d_m3ErrorConst (malformedFunctionSignature, "malformed function signature") // compilation errors d_m3ErrorConst (noCompiler, "no compiler found for opcode") d_m3ErrorConst (unknownOpcode, "unknown opcode") +d_m3ErrorConst (restictedOpcode, "restricted opcode") d_m3ErrorConst (functionStackOverflow, "compiling function overran its stack height limit") d_m3ErrorConst (functionStackUnderrun, "compiling function underran the stack") d_m3ErrorConst (mallocFailedCodePage, "memory allocation failed when acquiring a new M3 code page") d_m3ErrorConst (settingImmutableGlobal, "attempting to set an immutable global") +d_m3ErrorConst (typeMismatch, "incorrect type on stack") +d_m3ErrorConst (typeCountMismatch, "incorrect value count on stack") // runtime errors d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code") @@ -142,6 +160,10 @@ d_m3ErrorConst (wasmMemoryOverflow, "runtime ran out of memory") d_m3ErrorConst (globalMemoryNotAllocated, "global memory is missing from a module") d_m3ErrorConst (globaIndexOutOfBounds, "global index is too large") d_m3ErrorConst (argumentCountMismatch, "argument count mismatch") +d_m3ErrorConst (argumentTypeMismatch, "argument type mismatch") +d_m3ErrorConst (globalLookupFailed, "global lookup failed") +d_m3ErrorConst (globalTypeMismatch, "global type mismatch") +d_m3ErrorConst (globalNotMutable, "global is not mutable") // traps d_m3ErrorConst (trapOutOfBoundsMemoryAccess, "[trap] out of bounds memory access") @@ -178,33 +200,44 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") void m3_FreeRuntime (IM3Runtime i_runtime); + // Wasm currently only supports one memory region. i_memoryIndex should be zero. uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, uint32_t i_memoryIndex); + // This is used internally by Raw Function helpers + uint32_t m3_GetMemorySize (IM3Runtime i_runtime); + void * m3_GetUserData (IM3Runtime i_runtime); - // Wasm currently only supports one memory region. i_memoryIndex should be zero. //------------------------------------------------------------------------------------------------------------------------------- // modules //------------------------------------------------------------------------------------------------------------------------------- + // i_wasmBytes data must be persistent during the lifetime of the module M3Result m3_ParseModule (IM3Environment i_environment, IM3Module * o_module, const uint8_t * const i_wasmBytes, uint32_t i_numWasmBytes); - // i_wasmBytes data must be persistent during the lifetime of the module + // Only modules not loaded into a M3Runtime need to be freed. A module is considered unloaded if + // a. m3_LoadModule has not yet been called on that module. Or, + // b. m3_LoadModule returned a result. void m3_FreeModule (IM3Module i_module); - // Only unloaded modules need to be freed + // LoadModule transfers ownership of a module to the runtime. Do not free modules once successfully loaded into the runtime M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module); - // LoadModule transfers ownership of a module to the runtime. Do not free modules once successfully imported into the runtime + + // Optional, compiles all functions in the module + M3Result m3_CompileModule (IM3Module io_module); // Calling m3_RunStart is optional M3Result m3_RunStart (IM3Module i_module); + // Arguments and return values are passed in and out through the stack pointer _sp. + // Placeholder return value slots are first and arguments after. So, the first argument is at _sp [numReturns] + // Return values should be written into _sp [0] to _sp [num_returns - 1] typedef const void * (* M3RawCall) (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem); M3Result m3_LinkRawFunction (IM3Module io_module, @@ -221,13 +254,29 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") const void * i_userdata); const char* m3_GetModuleName (IM3Module i_module); + void m3_SetModuleName (IM3Module i_module, const char* name); IM3Runtime m3_GetModuleRuntime (IM3Module i_module); +//------------------------------------------------------------------------------------------------------------------------------- +// globals +//------------------------------------------------------------------------------------------------------------------------------- + IM3Global m3_FindGlobal (IM3Module io_module, + const char * const i_globalName); + + M3Result m3_GetGlobal (IM3Global i_global, + IM3TaggedValue o_value); + + M3Result m3_SetGlobal (IM3Global i_global, + const IM3TaggedValue i_value); + + M3ValueType m3_GetGlobalType (IM3Global i_global); + //------------------------------------------------------------------------------------------------------------------------------- // functions //------------------------------------------------------------------------------------------------------------------------------- M3Result m3_Yield (void); + // o_function is valid during the lifetime of the originating runtime M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, const char * const i_functionName); @@ -246,7 +295,6 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets); M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]); - // IM3Functions are valid during the lifetime of the originating runtime void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info); void m3_ResetErrorInfo (IM3Runtime i_runtime); @@ -265,6 +313,45 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") // The runtime owns the backtrace, do not free the backtrace you obtain. Returns NULL if there's no backtrace. IM3BacktraceInfo m3_GetBacktrace (IM3Runtime i_runtime); +//------------------------------------------------------------------------------------------------------------------------------- +// raw function definition helpers +//------------------------------------------------------------------------------------------------------------------------------- + +# define m3ApiOffsetToPtr(offset) (void*)((uint8_t*)_mem + (uint32_t)(offset)) +# define m3ApiPtrToOffset(ptr) (uint32_t)((uint8_t*)ptr - (uint8_t*)_mem) + +# define m3ApiReturnType(TYPE) TYPE* raw_return = ((TYPE*) (_sp++)); +# define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); +# define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((uint32_t *) (_sp++))); + +# define m3ApiIsNullPtr(addr) ((void*)(addr) <= _mem) +# define m3ApiCheckMem(addr, len) { if (M3_UNLIKELY(m3ApiIsNullPtr(addr) || ((uint64_t)(uintptr_t)(addr) + (len)) > ((uint64_t)(uintptr_t)(_mem)+m3_GetMemorySize(runtime)))) m3ApiTrap(m3Err_trapOutOfBoundsMemoryAccess); } + +# define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) +# define m3ApiReturn(VALUE) { *raw_return = (VALUE); return m3Err_none; } +# define m3ApiTrap(VALUE) { return VALUE; } +# define m3ApiSuccess() { return m3Err_none; } + +# if defined(M3_BIG_ENDIAN) +# define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) +# define m3ApiReadMem16(ptr) m3_bswap16((* (uint16_t *)(ptr))) +# define m3ApiReadMem32(ptr) m3_bswap32((* (uint32_t *)(ptr))) +# define m3ApiReadMem64(ptr) m3_bswap64((* (uint64_t *)(ptr))) +# define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } +# define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = m3_bswap16((val)); } +# define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = m3_bswap32((val)); } +# define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = m3_bswap64((val)); } +# else +# define m3ApiReadMem8(ptr) (* (uint8_t *)(ptr)) +# define m3ApiReadMem16(ptr) (* (uint16_t *)(ptr)) +# define m3ApiReadMem32(ptr) (* (uint32_t *)(ptr)) +# define m3ApiReadMem64(ptr) (* (uint64_t *)(ptr)) +# define m3ApiWriteMem8(ptr, val) { * (uint8_t *)(ptr) = (val); } +# define m3ApiWriteMem16(ptr, val) { * (uint16_t *)(ptr) = (val); } +# define m3ApiWriteMem32(ptr, val) { * (uint32_t *)(ptr) = (val); } +# define m3ApiWriteMem64(ptr, val) { * (uint64_t *)(ptr) = (val); } +# endif + #if defined(__cplusplus) } #endif diff --git a/Sources/CWasm3/include/wasm3_defs.h b/Sources/CWasm3/include/wasm3_defs.h new file mode 100644 index 0000000..77bccb1 --- /dev/null +++ b/Sources/CWasm3/include/wasm3_defs.h @@ -0,0 +1,282 @@ +// +// wasm3_defs.h +// +// Created by Volodymyr Shymanskyy on 11/20/19. +// Copyright © 2019 Volodymyr Shymanskyy. All rights reserved. +// + +#ifndef wasm3_defs_h +#define wasm3_defs_h + +#define M3_STR__(x) #x +#define M3_STR(x) M3_STR__(x) + +#define M3_CONCAT__(a,b) a##b +#define M3_CONCAT(a,b) M3_CONCAT__(a,b) + +/* + * Detect compiler + */ + +# if defined(__clang__) +# define M3_COMPILER_CLANG 1 +# elif defined(__INTEL_COMPILER) +# define M3_COMPILER_ICC 1 +# elif defined(__GNUC__) || defined(__GNUG__) +# define M3_COMPILER_GCC 1 +# elif defined(_MSC_VER) +# define M3_COMPILER_MSVC 1 +# else +# warning "Compiler not detected" +# endif + +# if defined(M3_COMPILER_CLANG) +# if defined(WIN32) +# define M3_COMPILER_VER __VERSION__ " for Windows" +# else +# define M3_COMPILER_VER __VERSION__ +# endif +# elif defined(M3_COMPILER_GCC) +# define M3_COMPILER_VER "GCC " __VERSION__ +# elif defined(M3_COMPILER_MSVC) +# define M3_COMPILER_VER "MSVC " M3_STR(_MSC_VER) +# else +# define M3_COMPILER_VER "unknown" +# endif + +# ifdef __has_feature +# define M3_COMPILER_HAS_FEATURE(x) __has_feature(x) +# else +# define M3_COMPILER_HAS_FEATURE(x) 0 +# endif + +# ifdef __has_builtin +# define M3_COMPILER_HAS_BUILTIN(x) __has_builtin(x) +# else +# define M3_COMPILER_HAS_BUILTIN(x) 0 +# endif + +/* + * Detect endianness + */ + +# if defined(M3_COMPILER_MSVC) +# define M3_LITTLE_ENDIAN //_byteswap_ushort, _byteswap_ulong, _byteswap_uint64 +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define M3_LITTLE_ENDIAN +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define M3_BIG_ENDIAN +# else +# error "Byte order not detected" +# endif + +/* + * Detect platform + */ + +# if defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_GCC) +# if defined(__wasm__) +# define M3_ARCH "wasm" + +# elif defined(__x86_64__) +# define M3_ARCH "x86_64" + +# elif defined(__i386__) +# define M3_ARCH "i386" + +# elif defined(__aarch64__) +# define M3_ARCH "arm64-v8a" + +# elif defined(__arm__) +# if defined(__ARM_ARCH_7A__) +# if defined(__ARM_NEON__) +# if defined(__ARM_PCS_VFP) +# define M3_ARCH "arm-v7a/NEON hard-float" +# else +# define M3_ARCH "arm-v7a/NEON" +# endif +# else +# if defined(__ARM_PCS_VFP) +# define M3_ARCH "arm-v7a hard-float" +# else +# define M3_ARCH "arm-v7a" +# endif +# endif +# else +# define M3_ARCH "arm" +# endif + +# elif defined(__riscv) +# if defined(__riscv_32e) +# define _M3_ARCH_RV "rv32e" +# elif __riscv_xlen == 128 +# define _M3_ARCH_RV "rv128i" +# elif __riscv_xlen == 64 +# define _M3_ARCH_RV "rv64i" +# elif __riscv_xlen == 32 +# define _M3_ARCH_RV "rv32i" +# endif +# if defined(__riscv_muldiv) +# define _M3_ARCH_RV_M _M3_ARCH_RV "m" +# else +# define _M3_ARCH_RV_M _M3_ARCH_RV +# endif +# if defined(__riscv_atomic) +# define _M3_ARCH_RV_A _M3_ARCH_RV_M "a" +# else +# define _M3_ARCH_RV_A _M3_ARCH_RV_M +# endif +# if defined(__riscv_flen) +# define _M3_ARCH_RV_F _M3_ARCH_RV_A "f" +# else +# define _M3_ARCH_RV_F _M3_ARCH_RV_A +# endif +# if defined(__riscv_flen) && __riscv_flen >= 64 +# define _M3_ARCH_RV_D _M3_ARCH_RV_F "d" +# else +# define _M3_ARCH_RV_D _M3_ARCH_RV_F +# endif +# if defined(__riscv_compressed) +# define _M3_ARCH_RV_C _M3_ARCH_RV_D "c" +# else +# define _M3_ARCH_RV_C _M3_ARCH_RV_D +# endif +# define M3_ARCH _M3_ARCH_RV_C + +# elif defined(__mips__) +# if defined(__MIPSEB__) && defined(__mips64) +# define M3_ARCH "mips64 " _MIPS_ARCH +# elif defined(__MIPSEL__) && defined(__mips64) +# define M3_ARCH "mips64el " _MIPS_ARCH +# elif defined(__MIPSEB__) +# define M3_ARCH "mips " _MIPS_ARCH +# elif defined(__MIPSEL__) +# define M3_ARCH "mipsel " _MIPS_ARCH +# endif + +# elif defined(__PPC__) +# if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) +# define M3_ARCH "ppc64le" +# elif defined(__PPC64__) +# define M3_ARCH "ppc64" +# else +# define M3_ARCH "ppc" +# endif + +# elif defined(__sparc__) +# if defined(__arch64__) +# define M3_ARCH "sparc64" +# else +# define M3_ARCH "sparc" +# endif + +# elif defined(__s390x__) +# define M3_ARCH "s390x" + +# elif defined(__alpha__) +# define M3_ARCH "alpha" + +# elif defined(__m68k__) +# define M3_ARCH "m68k" + +# elif defined(__xtensa__) +# define M3_ARCH "xtensa" + +# elif defined(__arc__) +# define M3_ARCH "arc32" + +# elif defined(__AVR__) +# define M3_ARCH "avr" +# endif +# endif + +# if defined(M3_COMPILER_MSVC) +# if defined(_M_X64) +# define M3_ARCH "x86_64" +# elif defined(_M_IX86) +# define M3_ARCH "i386" +# elif defined(_M_ARM64) +# define M3_ARCH "arm64" +# elif defined(_M_ARM) +# define M3_ARCH "arm" +# endif +# endif + +# if !defined(M3_ARCH) +# warning "Architecture not detected" +# define M3_ARCH "unknown" +# endif + +/* + * Byte swapping (for Big-Endian systems only) + */ + +# if defined(M3_COMPILER_MSVC) +# define m3_bswap16(x) _byteswap_ushort((x)) +# define m3_bswap32(x) _byteswap_ulong((x)) +# define m3_bswap64(x) _byteswap_uint64((x)) +# elif defined(M3_COMPILER_GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +// __builtin_bswap32/64 added in gcc 4.3, __builtin_bswap16 added in gcc 4.8 +# define m3_bswap16(x) __builtin_bswap16((x)) +# define m3_bswap32(x) __builtin_bswap32((x)) +# define m3_bswap64(x) __builtin_bswap64((x)) +# elif defined(M3_COMPILER_CLANG) && M3_COMPILER_HAS_BUILTIN(__builtin_bswap16) +# define m3_bswap16(x) __builtin_bswap16((x)) +# define m3_bswap32(x) __builtin_bswap32((x)) +# define m3_bswap64(x) __builtin_bswap64((x)) +# else +# include +# if defined(__bswap_16) +# define m3_bswap16(x) __bswap_16((x)) +# define m3_bswap32(x) __bswap_32((x)) +# define m3_bswap64(x) __bswap_64((x)) +# else +# warning "Using naive (probably slow) bswap operations" + static inline + uint16_t m3_bswap16(uint16_t x) { + return ((( x >> 8 ) & 0xffu ) | (( x & 0xffu ) << 8 )); + } + static inline + uint32_t m3_bswap32(uint32_t x) { + return ((( x & 0xff000000u ) >> 24 ) | + (( x & 0x00ff0000u ) >> 8 ) | + (( x & 0x0000ff00u ) << 8 ) | + (( x & 0x000000ffu ) << 24 )); + } + static inline + uint64_t m3_bswap64(uint64_t x) { + return ((( x & 0xff00000000000000ull ) >> 56 ) | + (( x & 0x00ff000000000000ull ) >> 40 ) | + (( x & 0x0000ff0000000000ull ) >> 24 ) | + (( x & 0x000000ff00000000ull ) >> 8 ) | + (( x & 0x00000000ff000000ull ) << 8 ) | + (( x & 0x0000000000ff0000ull ) << 24 ) | + (( x & 0x000000000000ff00ull ) << 40 ) | + (( x & 0x00000000000000ffull ) << 56 )); + } +# endif +# endif + +/* + * Other + */ + +# if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) +# define M3_UNLIKELY(x) __builtin_expect(!!(x), 0) +# define M3_LIKELY(x) __builtin_expect(!!(x), 1) +# else +# define M3_UNLIKELY(x) (x) +# define M3_LIKELY(x) (x) +# endif + +// TODO: remove +# if defined(M3_COMPILER_GCC) || defined(M3_COMPILER_CLANG) || defined(M3_COMPILER_ICC) +# define UNLIKELY(x) __builtin_expect(!!(x), 0) +# define LIKELY(x) __builtin_expect(!!(x), 1) +# else +# define UNLIKELY(x) (x) +# define LIKELY(x) (x) +# endif + + +#endif // wasm3_defs_h diff --git a/Sources/CWasm3/include/wasm3_ext.h b/Sources/CWasm3/include/wasm3_ext.h new file mode 100644 index 0000000..2de1fc3 --- /dev/null +++ b/Sources/CWasm3/include/wasm3_ext.h @@ -0,0 +1,51 @@ +// +// Wasm3, high performance WebAssembly interpreter +// +// Extensions +// +// Copyright © 2019-2021 Steven Massey, Volodymyr Shymanskyy. +// All rights reserved. +// + +#ifndef wasm3_ext_h +#define wasm3_ext_h + +#include "wasm3.h" +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +//------------------------------------------------------------------------------------------------------------------------------- +// API extensions +//------------------------------------------------------------------------------------------------------------------------------- +/* + These extensions allow for unconventional uses of Wasm3 -- mainly dynamic modification of modules to inject new Wasm + functions during runtime. +*/ +//------------------------------------------------------------------------------------------------------------------------------- + + // Creates an empty module. + IM3Module m3_NewModule (IM3Environment i_environment); + + + // To append a new function, set io_functionIndex to negative. On return, the new function index will be set. + // To overwrite an existing function, set io_functionIndex to the desired element. i_signature must match the existing + // function signature. + // ** InjectFunction invalidates any existing IM3Function pointers + M3Result m3_InjectFunction (IM3Module i_module, + int32_t * io_functionIndex, + const char * const i_signature, + const uint8_t * const i_wasmBytes, // i_wasmBytes is copied + bool i_doCompilation); + + + IM3Function m3_GetFunctionByIndex (IM3Module i_module, + uint32_t i_index); + +#if defined(__cplusplus) +} +#endif + +#endif // wasm3_h diff --git a/Sources/CWasm3/m3_api_libc.c b/Sources/CWasm3/m3_api_libc.c index c0ae406..e0c1b18 100644 --- a/Sources/CWasm3/m3_api_libc.c +++ b/Sources/CWasm3/m3_api_libc.c @@ -9,7 +9,6 @@ #include "m3_api_libc.h" -#include "m3_api_defs.h" #include "m3_env.h" #include "m3_exception.h" @@ -17,6 +16,8 @@ #include #include +typedef uint32_t wasm_ptr_t; +typedef uint32_t wasm_size_t; m3ApiRawFunction(m3_libc_abort) { @@ -35,9 +36,9 @@ m3ApiRawFunction(m3_libc_memset) { m3ApiReturnType (int32_t) - m3ApiGetArgMem (void*, i_ptr) - m3ApiGetArg (int32_t, i_value) - m3ApiGetArg (int32_t, i_size) + m3ApiGetArgMem (void*, i_ptr) + m3ApiGetArg (int32_t, i_value) + m3ApiGetArg (wasm_size_t, i_size) m3ApiCheckMem(i_ptr, i_size); @@ -49,9 +50,9 @@ m3ApiRawFunction(m3_libc_memmove) { m3ApiReturnType (int32_t) - m3ApiGetArgMem (void*, o_dst) - m3ApiGetArgMem (void*, i_src) - m3ApiGetArg (int32_t, i_size) + m3ApiGetArgMem (void*, o_dst) + m3ApiGetArgMem (void*, i_src) + m3ApiGetArg (wasm_size_t, i_size) m3ApiCheckMem(o_dst, i_size); m3ApiCheckMem(i_src, i_size); @@ -64,8 +65,8 @@ m3ApiRawFunction(m3_libc_print) { m3ApiReturnType (uint32_t) - m3ApiGetArgMem (void*, i_ptr) - m3ApiGetArg (uint32_t, i_size) + m3ApiGetArgMem (void*, i_ptr) + m3ApiGetArg (wasm_size_t, i_size) m3ApiCheckMem(i_ptr, i_size); @@ -75,11 +76,114 @@ m3ApiRawFunction(m3_libc_print) m3ApiReturn(i_size); } +static +void internal_itoa(int n, char s[], int radix) +{ + static char const HEXDIGITS[0x10] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + int i, j, sign; + char c; + + if ((sign = n) < 0) { n = -n; } + i = 0; + do { + s[i++] = HEXDIGITS[n % radix]; + } while ((n /= radix) > 0); + + if (sign < 0) { s[i++] = '-'; } + s[i] = '\0'; + + // reverse + for (i = 0, j = strlen(s)-1; i + #elif __has_include("wasi/core.h") # warning "Using legacy WASI headers" # include # define __WASI_ERRNO_SUCCESS __WASI_ESUCCESS # define __WASI_ERRNO_INVAL __WASI_EINVAL + #else # error "Missing WASI headers" #endif @@ -48,7 +49,7 @@ void copy_iov_to_host(void* _mem, __wasi_iovec_t* host_iov, __wasi_iovec_t* wasi * WASI API implementation */ -m3ApiRawFunction(m3_wasi_unstable_args_get) +m3ApiRawFunction(m3_wasi_generic_args_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint32_t * , argv) @@ -75,7 +76,7 @@ m3ApiRawFunction(m3_wasi_unstable_args_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_args_sizes_get) +m3ApiRawFunction(m3_wasi_generic_args_sizes_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (__wasi_size_t * , argc) @@ -100,7 +101,7 @@ m3ApiRawFunction(m3_wasi_unstable_args_sizes_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_environ_get) +m3ApiRawFunction(m3_wasi_generic_environ_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint32_t * , env) @@ -125,7 +126,7 @@ m3ApiRawFunction(m3_wasi_unstable_environ_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_environ_sizes_get) +m3ApiRawFunction(m3_wasi_generic_environ_sizes_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (__wasi_size_t * , env_count) @@ -139,7 +140,7 @@ m3ApiRawFunction(m3_wasi_unstable_environ_sizes_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_prestat_dir_name) +m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -153,7 +154,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_prestat_dir_name) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_prestat_get) +m3ApiRawFunction(m3_wasi_generic_fd_prestat_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -166,7 +167,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_prestat_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_get) +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -179,7 +180,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_set_flags) +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -190,23 +191,193 @@ m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_set_flags) m3ApiReturn(ret); } +m3ApiRawFunction(m3_wasi_unstable_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_fd_filestat_get(fd, &stat); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem32(buf+20, stat.st_nlink); + m3ApiWriteMem64(buf+24, stat.st_size); + m3ApiWriteMem64(buf+32, stat.st_atim); + m3ApiWriteMem64(buf+40, stat.st_mtim); + m3ApiWriteMem64(buf+48, stat.st_ctim); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_fd_filestat_get(fd, &stat); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem64(buf+24, stat.st_nlink); + m3ApiWriteMem64(buf+32, stat.st_size); + m3ApiWriteMem64(buf+40, stat.st_atim); + m3ApiWriteMem64(buf+48, stat.st_mtim); + m3ApiWriteMem64(buf+56, stat.st_ctim); + + m3ApiReturn(ret); +} + m3ApiRawFunction(m3_wasi_unstable_fd_seek) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) m3ApiGetArg (__wasi_filedelta_t , offset) - m3ApiGetArg (__wasi_whence_t , whence) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + __wasi_whence_t whence; + + switch (wasi_whence) { + case 0: whence = __WASI_WHENCE_CUR; break; + case 1: whence = __WASI_WHENCE_END; break; + case 2: whence = __WASI_WHENCE_SET; break; + default: m3ApiReturn(__WASI_ERRNO_INVAL); + } + + __wasi_errno_t ret = __wasi_fd_seek(fd, offset, whence, result); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) m3ApiGetArgMem (__wasi_filesize_t * , result) m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + __wasi_whence_t whence; + + switch (wasi_whence) { + case 0: whence = __WASI_WHENCE_SET; break; + case 1: whence = __WASI_WHENCE_CUR; break; + case 2: whence = __WASI_WHENCE_END; break; + default: m3ApiReturn(__WASI_ERRNO_INVAL); + } + __wasi_errno_t ret = __wasi_fd_seek(fd, offset, whence, result); m3ApiReturn(ret); } +m3ApiRawFunction(m3_wasi_generic_path_create_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_path_create_directory(fd, path, path_len); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_readlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + m3ApiGetArgMem (char * , buf) + m3ApiGetArg (__wasi_size_t , buf_len) + m3ApiGetArgMem (__wasi_size_t * , bufused) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, buf_len); + m3ApiCheckMem(bufused, sizeof(__wasi_size_t)); + + __wasi_errno_t ret = __wasi_path_readlink(fd, path, path_len, buf, buf_len, bufused); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_remove_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_path_remove_directory(fd, path, path_len); -m3ApiRawFunction(m3_wasi_unstable_path_open) + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_rename) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , old_fd) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (__wasi_size_t , old_path_len) + m3ApiGetArg (__wasi_fd_t , new_fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (__wasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + __wasi_errno_t ret = __wasi_path_rename(old_fd, old_path, old_path_len, + new_fd, new_path, new_path_len); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_unlink_file) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (__wasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + __wasi_errno_t ret = __wasi_path_unlink_file(fd, path, path_len); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_open) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , dirfd) @@ -242,17 +413,86 @@ m3ApiRawFunction(m3_wasi_unstable_path_filestat_get) m3ApiGetArg (__wasi_lookupflags_t , flags) m3ApiGetArgMem (const char * , path) m3ApiGetArg (uint32_t , path_len) - m3ApiGetArgMem (__wasi_filestat_t * , buf) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_path_filestat_get(fd, flags, path, path_len, &stat); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem32(buf+20, stat.st_nlink); + m3ApiWriteMem64(buf+24, stat.st_size); + m3ApiWriteMem64(buf+32, stat.st_atim); + m3ApiWriteMem64(buf+40, stat.st_mtim); + m3ApiWriteMem64(buf+48, stat.st_ctim); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_path_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_lookupflags_t , flags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uint32_t , path_len) + m3ApiGetArgMem (uint8_t * , buf) m3ApiCheckMem(path, path_len); - m3ApiCheckMem(buf, sizeof(__wasi_filestat_t)); + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + __wasi_filestat_t stat; + + __wasi_errno_t ret = __wasi_path_filestat_get(fd, flags, path, path_len, &stat); + + if (ret != __WASI_ERRNO_SUCCESS) { + m3ApiReturn(ret); + } - __wasi_errno_t ret = __wasi_path_filestat_get(fd, flags, path, path_len, buf); + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem64(buf+24, stat.st_nlink); + m3ApiWriteMem64(buf+32, stat.st_size); + m3ApiWriteMem64(buf+40, stat.st_atim); + m3ApiWriteMem64(buf+48, stat.st_mtim); + m3ApiWriteMem64(buf+56, stat.st_ctim); m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_read) +m3ApiRawFunction(m3_wasi_generic_fd_pread) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArgMem (__wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (__wasi_size_t , iovs_len) + m3ApiGetArg (__wasi_filesize_t , offset) + m3ApiGetArgMem (__wasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(__wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(__wasi_size_t)); + + __wasi_iovec_t iovs[iovs_len]; + copy_iov_to_host(_mem, iovs, wasi_iovs, iovs_len); + + __wasi_errno_t ret = __wasi_fd_pread(fd, iovs, iovs_len, offset, nread); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_fd_read) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -271,7 +511,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_read) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_write) +m3ApiRawFunction(m3_wasi_generic_fd_write) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -290,7 +530,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_write) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_readdir) +m3ApiRawFunction(m3_wasi_generic_fd_readdir) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -307,7 +547,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_readdir) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_close) +m3ApiRawFunction(m3_wasi_generic_fd_close) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t, fd) @@ -317,7 +557,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_close) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_datasync) +m3ApiRawFunction(m3_wasi_generic_fd_datasync) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t, fd) @@ -327,7 +567,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_datasync) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_random_get) +m3ApiRawFunction(m3_wasi_generic_random_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint8_t * , buf) @@ -340,7 +580,7 @@ m3ApiRawFunction(m3_wasi_unstable_random_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_clock_res_get) +m3ApiRawFunction(m3_wasi_generic_clock_res_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) @@ -353,7 +593,7 @@ m3ApiRawFunction(m3_wasi_unstable_clock_res_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_clock_time_get) +m3ApiRawFunction(m3_wasi_generic_clock_time_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) @@ -367,7 +607,7 @@ m3ApiRawFunction(m3_wasi_unstable_clock_time_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_poll_oneoff) +m3ApiRawFunction(m3_wasi_generic_poll_oneoff) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (const __wasi_subscription_t * , in) @@ -384,7 +624,7 @@ m3ApiRawFunction(m3_wasi_unstable_poll_oneoff) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_proc_exit) +m3ApiRawFunction(m3_wasi_generic_proc_exit) { m3ApiGetArg (uint32_t, code) @@ -426,54 +666,59 @@ M3Result m3_LinkWASI (IM3Module module) static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" }; + // fd_seek is incompatible +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_filestat_get", "i(i*)", &m3_wasi_unstable_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_filestat_get", "i(i*)", &m3_wasi_snapshot_preview1_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "path_filestat_get", "i(ii*i*)", &m3_wasi_snapshot_preview1_path_filestat_get))); + for (int i=0; i<2; i++) { const char* wasi = namespaces[i]; -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_unstable_args_get, wasi_context))); -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_unstable_args_sizes_get, wasi_context))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_unstable_clock_res_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_unstable_clock_time_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_unstable_environ_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_unstable_environ_sizes_get))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_unstable_fd_close))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_unstable_fd_datasync))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_unstable_fd_fdstat_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_unstable_fd_fdstat_set_flags))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_get", "i(i*)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_unstable_fd_prestat_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_unstable_fd_prestat_dir_name))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",&m3_wasi_generic_fd_pread))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_unstable_fd_read))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",&m3_wasi_unstable_fd_readdir))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",&m3_wasi_generic_fd_readdir))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_unstable_fd_write))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", &m3_wasi_generic_path_create_directory))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_unstable_path_open))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", &m3_wasi_generic_path_readlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", &m3_wasi_generic_path_remove_directory))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", &m3_wasi_generic_path_rename))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", ))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", &m3_wasi_generic_path_unlink_file))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_unstable_poll_oneoff))); -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_unstable_proc_exit, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_unstable_random_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", ))); diff --git a/Sources/CWasm3/m3_api_tracer.c b/Sources/CWasm3/m3_api_tracer.c index fb4a185..4abedbd 100644 --- a/Sources/CWasm3/m3_api_tracer.c +++ b/Sources/CWasm3/m3_api_tracer.c @@ -7,7 +7,6 @@ #include "m3_api_tracer.h" -#include "m3_api_defs.h" #include "m3_env.h" #include "m3_exception.h" diff --git a/Sources/CWasm3/m3_api_uvwasi.c b/Sources/CWasm3/m3_api_uvwasi.c index 8724e9a..a059ffa 100644 --- a/Sources/CWasm3/m3_api_uvwasi.c +++ b/Sources/CWasm3/m3_api_uvwasi.c @@ -9,7 +9,6 @@ #include "m3_api_wasi.h" -#include "m3_api_defs.h" #include "m3_env.h" #include "m3_exception.h" @@ -20,6 +19,10 @@ #include "uvwasi.h" +#ifndef d_m3EnableWasiTracing +# define d_m3EnableWasiTracing 0 +#endif + #ifdef __APPLE__ # include # define environ (*_NSGetEnviron()) @@ -36,11 +39,102 @@ typedef struct wasi_iovec_t uvwasi_size_t buf_len; } wasi_iovec_t; +#if d_m3EnableWasiTracing + +const char* wasi_errno2str(uvwasi_errno_t err) +{ + switch (err) { + case 0: return "ESUCCESS"; + case 1: return "E2BIG"; + case 2: return "EACCES"; + case 3: return "EADDRINUSE"; + case 4: return "EADDRNOTAVAIL"; + case 5: return "EAFNOSUPPORT"; + case 6: return "EAGAIN"; + case 7: return "EALREADY"; + case 8: return "EBADF"; + case 9: return "EBADMSG"; + case 10: return "EBUSY"; + case 11: return "ECANCELED"; + case 12: return "ECHILD"; + case 13: return "ECONNABORTED"; + case 14: return "ECONNREFUSED"; + case 15: return "ECONNRESET"; + case 16: return "EDEADLK"; + case 17: return "EDESTADDRREQ"; + case 18: return "EDOM"; + case 19: return "EDQUOT"; + case 20: return "EEXIST"; + case 21: return "EFAULT"; + case 22: return "EFBIG"; + case 23: return "EHOSTUNREACH"; + case 24: return "EIDRM"; + case 25: return "EILSEQ"; + case 26: return "EINPROGRESS"; + case 27: return "EINTR"; + case 28: return "EINVAL"; + case 29: return "EIO"; + case 30: return "EISCONN"; + case 31: return "EISDIR"; + case 32: return "ELOOP"; + case 33: return "EMFILE"; + case 34: return "EMLINK"; + case 35: return "EMSGSIZE"; + case 36: return "EMULTIHOP"; + case 37: return "ENAMETOOLONG"; + case 38: return "ENETDOWN"; + case 39: return "ENETRESET"; + case 40: return "ENETUNREACH"; + case 41: return "ENFILE"; + case 42: return "ENOBUFS"; + case 43: return "ENODEV"; + case 44: return "ENOENT"; + case 45: return "ENOEXEC"; + case 46: return "ENOLCK"; + case 47: return "ENOLINK"; + case 48: return "ENOMEM"; + case 49: return "ENOMSG"; + case 50: return "ENOPROTOOPT"; + case 51: return "ENOSPC"; + case 52: return "ENOSYS"; + case 53: return "ENOTCONN"; + case 54: return "ENOTDIR"; + case 55: return "ENOTEMPTY"; + case 56: return "ENOTRECOVERABLE"; + case 57: return "ENOTSOCK"; + case 58: return "ENOTSUP"; + case 59: return "ENOTTY"; + case 60: return "ENXIO"; + case 61: return "EOVERFLOW"; + case 62: return "EOWNERDEAD"; + case 63: return "EPERM"; + case 64: return "EPIPE"; + case 65: return "EPROTO"; + case 66: return "EPROTONOSUPPORT"; + case 67: return "EPROTOTYPE"; + case 68: return "ERANGE"; + case 69: return "EROFS"; + case 70: return "ESPIPE"; + case 71: return "ESRCH"; + case 72: return "ESTALE"; + case 73: return "ETIMEDOUT"; + case 74: return "ETXTBSY"; + case 75: return "EXDEV"; + case 76: return "ENOTCAPABLE"; + default: return ""; + } +} + +# define WASI_TRACE(fmt, ...) { fprintf(stderr, "%s " fmt, __FUNCTION__+16, ##__VA_ARGS__); fprintf(stderr, " => %s\n", wasi_errno2str(ret)); } +#else +# define WASI_TRACE(fmt, ...) +#endif + /* * WASI API implementation */ -m3ApiRawFunction(m3_wasi_unstable_args_get) +m3ApiRawFunction(m3_wasi_generic_args_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint32_t * , argv) @@ -67,7 +161,7 @@ m3ApiRawFunction(m3_wasi_unstable_args_get) m3ApiReturn(UVWASI_ESUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_args_sizes_get) +m3ApiRawFunction(m3_wasi_generic_args_sizes_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uvwasi_size_t * , argc) @@ -92,7 +186,7 @@ m3ApiRawFunction(m3_wasi_unstable_args_sizes_get) m3ApiReturn(UVWASI_ESUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_environ_get) +m3ApiRawFunction(m3_wasi_generic_environ_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint32_t * , env) @@ -134,7 +228,7 @@ m3ApiRawFunction(m3_wasi_unstable_environ_get) m3ApiReturn(UVWASI_ESUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_environ_sizes_get) +m3ApiRawFunction(m3_wasi_generic_environ_sizes_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uvwasi_size_t * , env_count) @@ -154,7 +248,7 @@ m3ApiRawFunction(m3_wasi_unstable_environ_sizes_get) m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_prestat_dir_name) +m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) @@ -165,53 +259,60 @@ m3ApiRawFunction(m3_wasi_unstable_fd_prestat_dir_name) uvwasi_errno_t ret = uvwasi_fd_prestat_dir_name(&uvwasi, fd, path, path_len); + WASI_TRACE("fd:%d, len:%d | path:%s", fd, path_len, path); + m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_prestat_get) +m3ApiRawFunction(m3_wasi_generic_fd_prestat_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) - m3ApiGetArgMem (uint32_t * , buf) + m3ApiGetArgMem (uint8_t * , buf) - m3ApiCheckMem(buf, 2*sizeof(uint32_t)); + m3ApiCheckMem(buf, 8); uvwasi_prestat_t prestat; - uvwasi_errno_t ret; - ret = uvwasi_fd_prestat_get(&uvwasi, fd, &prestat); + uvwasi_errno_t ret = uvwasi_fd_prestat_get(&uvwasi, fd, &prestat); + + WASI_TRACE("fd:%d | type:%d, name_len:%d", fd, prestat.pr_type, prestat.u.dir.pr_name_len); + if (ret != UVWASI_ESUCCESS) { m3ApiReturn(ret); } - m3ApiWriteMem32(buf, prestat.pr_type); - m3ApiWriteMem32(buf+1, prestat.u.dir.pr_name_len); - m3ApiReturn(UVWASI_ESUCCESS); + m3ApiWriteMem32(buf+0, prestat.pr_type); + m3ApiWriteMem32(buf+4, prestat.u.dir.pr_name_len); + m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_get) +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) - m3ApiGetArgMem (uint8_t * , fdstat) + m3ApiGetArgMem (uint8_t * , buf) - m3ApiCheckMem(fdstat, 24); + m3ApiCheckMem(buf, 24); uvwasi_fdstat_t stat; uvwasi_errno_t ret = uvwasi_fd_fdstat_get(&uvwasi, fd, &stat); + WASI_TRACE("fd:%d", fd); + if (ret != UVWASI_ESUCCESS) { m3ApiReturn(ret); } - m3ApiWriteMem16(fdstat, stat.fs_filetype); - m3ApiWriteMem16(fdstat+2, stat.fs_flags); - m3ApiWriteMem64(fdstat+8, stat.fs_rights_base); - m3ApiWriteMem64(fdstat+16, stat.fs_rights_inheriting); - m3ApiReturn(UVWASI_ESUCCESS); + memset(buf, 0, 24); + m3ApiWriteMem8 (buf+0, stat.fs_filetype); + m3ApiWriteMem16(buf+2, stat.fs_flags); + m3ApiWriteMem64(buf+8, stat.fs_rights_base); + m3ApiWriteMem64(buf+16, stat.fs_rights_inheriting); + m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_set_flags) +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) @@ -219,10 +320,73 @@ m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_set_flags) uvwasi_errno_t ret = uvwasi_fd_fdstat_set_flags(&uvwasi, fd, flags); + WASI_TRACE("fd:%d, flags:0x%x", fd, flags); + m3ApiReturn(ret); } -// TODO: Remove this at some point +m3ApiRawFunction(m3_wasi_unstable_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 56); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_fd_filestat_get(&uvwasi, fd, &stat); + + WASI_TRACE("fd:%d | fs.size:%d", fd, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem32(buf+20, stat.st_nlink); + m3ApiWriteMem64(buf+24, stat.st_size); + m3ApiWriteMem64(buf+32, stat.st_atim); + m3ApiWriteMem64(buf+40, stat.st_mtim); + m3ApiWriteMem64(buf+48, stat.st_ctim); + + m3ApiReturn(UVWASI_ESUCCESS); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_fd_filestat_get(&uvwasi, fd, &stat); + + WASI_TRACE("fd:%d | fs.size:%d", fd, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem64(buf+24, stat.st_nlink); + m3ApiWriteMem64(buf+32, stat.st_size); + m3ApiWriteMem64(buf+40, stat.st_atim); + m3ApiWriteMem64(buf+48, stat.st_mtim); + m3ApiWriteMem64(buf+56, stat.st_ctim); + + m3ApiReturn(UVWASI_ESUCCESS); +} + m3ApiRawFunction(m3_wasi_unstable_fd_seek) { m3ApiReturnType (uint32_t) @@ -233,17 +397,19 @@ m3ApiRawFunction(m3_wasi_unstable_fd_seek) m3ApiCheckMem(result, sizeof(uvwasi_filesize_t)); - uvwasi_whence_t whence = wasi_whence; + uvwasi_whence_t whence = -1; + const char* whstr = "???"; switch (wasi_whence) { - case 0: whence = UVWASI_WHENCE_CUR; break; - case 1: whence = UVWASI_WHENCE_END; break; - case 2: whence = UVWASI_WHENCE_SET; break; - default: m3ApiReturn(UVWASI_EINVAL); + case 0: whence = UVWASI_WHENCE_CUR; whstr = "CUR"; break; + case 1: whence = UVWASI_WHENCE_END; whstr = "END"; break; + case 2: whence = UVWASI_WHENCE_SET; whstr = "SET"; break; } uvwasi_errno_t ret = uvwasi_fd_seek(&uvwasi, fd, offset, whence, result); + WASI_TRACE("fd:%d, offset:%d, whence:%s | result:%d", fd, offset, whstr, *result); + //TODO: m3ApiWriteMem m3ApiReturn(ret); @@ -254,20 +420,144 @@ m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek) m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) m3ApiGetArg (uvwasi_filedelta_t , offset) - m3ApiGetArg (uint32_t , whence) + m3ApiGetArg (uint32_t , wasi_whence) m3ApiGetArgMem (uvwasi_filesize_t * , result) m3ApiCheckMem(result, sizeof(uvwasi_filesize_t)); + uvwasi_whence_t whence = -1; + const char* whstr = "???"; + + switch (wasi_whence) { + case 0: whence = UVWASI_WHENCE_SET; whstr = "SET"; break; + case 1: whence = UVWASI_WHENCE_CUR; whstr = "CUR"; break; + case 2: whence = UVWASI_WHENCE_END; whstr = "END"; break; + } + uvwasi_errno_t ret = uvwasi_fd_seek(&uvwasi, fd, offset, whence, result); + WASI_TRACE("fd:%d, offset:%d, whence:%s | result:%d", fd, offset, whstr, *result); + //TODO: m3ApiWriteMem m3ApiReturn(ret); } +m3ApiRawFunction(m3_wasi_generic_path_create_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_path_create_directory(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_readlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + m3ApiGetArgMem (char * , buf) + m3ApiGetArg (uvwasi_size_t , buf_len) + m3ApiGetArgMem (uvwasi_size_t * , bufused) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, buf_len); + m3ApiCheckMem(bufused, sizeof(uvwasi_size_t)); + + uvwasi_size_t uvbufused; + + uvwasi_errno_t ret = uvwasi_path_readlink(&uvwasi, fd, path, path_len, buf, buf_len, &uvbufused); + + WASI_TRACE("fd:%d, path:%s | buf:%s, bufused:%d", fd, path, buf, uvbufused); + + m3ApiWriteMem32(bufused, uvbufused); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_remove_directory) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_path_remove_directory(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_rename) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , old_fd) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (uvwasi_size_t , old_path_len) + m3ApiGetArg (uvwasi_fd_t , new_fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (uvwasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + uvwasi_errno_t ret = uvwasi_path_rename(&uvwasi, old_fd, old_path, old_path_len, + new_fd, new_path, new_path_len); + + WASI_TRACE("old_fd:%d, old_path:%s, new_fd:%d, new_path:%s", old_fd, old_path, new_fd, new_path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_symlink) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArgMem (const char * , old_path) + m3ApiGetArg (uvwasi_size_t , old_path_len) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , new_path) + m3ApiGetArg (uvwasi_size_t , new_path_len) + + m3ApiCheckMem(old_path, old_path_len); + m3ApiCheckMem(new_path, new_path_len); + + uvwasi_errno_t ret = uvwasi_path_symlink(&uvwasi, old_path, old_path_len, + fd, new_path, new_path_len); + + WASI_TRACE("old_fd:%d, old_path:%s, fd:%d, new_path:%s", old_fd, old_path, fd, new_path); + + m3ApiReturn(ret); +} + +m3ApiRawFunction(m3_wasi_generic_path_unlink_file) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uvwasi_size_t , path_len) + + m3ApiCheckMem(path, path_len); + + uvwasi_errno_t ret = uvwasi_path_unlink_file(&uvwasi, fd, path, path_len); + + WASI_TRACE("fd:%d, path:%s", fd, path); + + m3ApiReturn(ret); +} -m3ApiRawFunction(m3_wasi_unstable_path_open) +m3ApiRawFunction(m3_wasi_generic_path_open) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , dirfd) @@ -283,6 +573,8 @@ m3ApiRawFunction(m3_wasi_unstable_path_open) m3ApiCheckMem(path, path_len); m3ApiCheckMem(fd, sizeof(uvwasi_fd_t)); + uvwasi_fd_t uvfd; + uvwasi_errno_t ret = uvwasi_path_open(&uvwasi, dirfd, dirflags, @@ -292,9 +584,11 @@ m3ApiRawFunction(m3_wasi_unstable_path_open) fs_rights_base, fs_rights_inheriting, fs_flags, - fd); + &uvfd); - //TODO: m3ApiWriteMem + WASI_TRACE("dirfd:%d, dirflags:0x%x, path:%s, oflags:0x%x, fs_flags:0x%x | fd:%d", dirfd, dirflags, path, oflags, fs_flags, uvfd); + + m3ApiWriteMem32(fd, uvfd); m3ApiReturn(ret); } @@ -306,19 +600,107 @@ m3ApiRawFunction(m3_wasi_unstable_path_filestat_get) m3ApiGetArg (uvwasi_lookupflags_t , flags) m3ApiGetArgMem (const char * , path) m3ApiGetArg (uint32_t , path_len) - m3ApiGetArgMem (uvwasi_filestat_t * , buf) + m3ApiGetArgMem (uint8_t * , buf) m3ApiCheckMem(path, path_len); - m3ApiCheckMem(buf, sizeof(uvwasi_filestat_t)); + m3ApiCheckMem(buf, 56); // wasi_filestat_t - uvwasi_errno_t ret = uvwasi_path_filestat_get(&uvwasi, fd, flags, path, path_len, buf); + uvwasi_filestat_t stat; - //TODO: m3ApiWriteMem + uvwasi_errno_t ret = uvwasi_path_filestat_get(&uvwasi, fd, flags, path, path_len, &stat); + + WASI_TRACE("fd:%d, flags:0x%x, path:%s | fs.size:%d", fd, flags, path, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 56); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem32(buf+20, stat.st_nlink); + m3ApiWriteMem64(buf+24, stat.st_size); + m3ApiWriteMem64(buf+32, stat.st_atim); + m3ApiWriteMem64(buf+40, stat.st_mtim); + m3ApiWriteMem64(buf+48, stat.st_ctim); + + m3ApiReturn(UVWASI_ESUCCESS); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_path_filestat_get) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArg (uvwasi_lookupflags_t , flags) + m3ApiGetArgMem (const char * , path) + m3ApiGetArg (uint32_t , path_len) + m3ApiGetArgMem (uint8_t * , buf) + + m3ApiCheckMem(path, path_len); + m3ApiCheckMem(buf, 64); // wasi_filestat_t + + uvwasi_filestat_t stat; + + uvwasi_errno_t ret = uvwasi_path_filestat_get(&uvwasi, fd, flags, path, path_len, &stat); + + WASI_TRACE("fd:%d, flags:0x%x, path:%s | fs.size:%d", fd, flags, path, stat.st_size); + + if (ret != UVWASI_ESUCCESS) { + m3ApiReturn(ret); + } + + memset(buf, 0, 64); + m3ApiWriteMem64(buf+0, stat.st_dev); + m3ApiWriteMem64(buf+8, stat.st_ino); + m3ApiWriteMem8 (buf+16, stat.st_filetype); + m3ApiWriteMem64(buf+24, stat.st_nlink); + m3ApiWriteMem64(buf+32, stat.st_size); + m3ApiWriteMem64(buf+40, stat.st_atim); + m3ApiWriteMem64(buf+48, stat.st_mtim); + m3ApiWriteMem64(buf+56, stat.st_ctim); + + m3ApiReturn(UVWASI_ESUCCESS); +} + +m3ApiRawFunction(m3_wasi_generic_fd_pread) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (uvwasi_fd_t , fd) + m3ApiGetArgMem (wasi_iovec_t * , wasi_iovs) + m3ApiGetArg (uvwasi_size_t , iovs_len) + m3ApiGetArg (uvwasi_filesize_t , offset) + m3ApiGetArgMem (uvwasi_size_t * , nread) + + m3ApiCheckMem(wasi_iovs, iovs_len * sizeof(wasi_iovec_t)); + m3ApiCheckMem(nread, sizeof(uvwasi_size_t)); + +#if defined(M3_COMPILER_MSVC) + if (iovs_len > 32) m3ApiReturn(UVWASI_EINVAL); + uvwasi_ciovec_t iovs[32]; +#else + if (iovs_len > 128) m3ApiReturn(UVWASI_EINVAL); + uvwasi_ciovec_t iovs[iovs_len]; +#endif + + for (uvwasi_size_t i = 0; i < iovs_len; ++i) { + iovs[i].buf = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); + iovs[i].buf_len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + + //fprintf(stderr, "> fd_pread fd:%d iov%d.len:%d\n", fd, i, iovs[i].buf_len); + } + + uvwasi_size_t num_read; + + uvwasi_errno_t ret = uvwasi_fd_pread(&uvwasi, fd, (const uvwasi_iovec_t *) iovs, iovs_len, offset, &num_read); + + WASI_TRACE("fd:%d | nread:%d", fd, num_read); + m3ApiWriteMem32(nread, num_read); m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_read) +m3ApiRawFunction(m3_wasi_generic_fd_read) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) @@ -342,14 +724,19 @@ m3ApiRawFunction(m3_wasi_unstable_fd_read) for (uvwasi_size_t i = 0; i < iovs_len; ++i) { iovs[i].buf = m3ApiOffsetToPtr(m3ApiReadMem32(&wasi_iovs[i].buf)); iovs[i].buf_len = m3ApiReadMem32(&wasi_iovs[i].buf_len); + + //fprintf(stderr, "> fd_read fd:%d iov%d.len:%d\n", fd, i, iovs[i].buf_len); } ret = uvwasi_fd_read(&uvwasi, fd, (const uvwasi_iovec_t *) iovs, iovs_len, &num_read); + + WASI_TRACE("fd:%d | nread:%d", fd, num_read); + m3ApiWriteMem32(nread, num_read); m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_write) +m3ApiRawFunction(m3_wasi_generic_fd_write) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) @@ -376,11 +763,14 @@ m3ApiRawFunction(m3_wasi_unstable_fd_write) } ret = uvwasi_fd_write(&uvwasi, fd, iovs, iovs_len, &num_written); + + WASI_TRACE("fd:%d | nwritten:%d", fd, num_written); + m3ApiWriteMem32(nwritten, num_written); m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_readdir) +m3ApiRawFunction(m3_wasi_generic_fd_readdir) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t , fd) @@ -395,32 +785,38 @@ m3ApiRawFunction(m3_wasi_unstable_fd_readdir) uvwasi_size_t uvbufused; uvwasi_errno_t ret = uvwasi_fd_readdir(&uvwasi, fd, buf, buf_len, cookie, &uvbufused); + WASI_TRACE("fd:%d | bufused:%d", fd, uvbufused); + m3ApiWriteMem32(bufused, uvbufused); m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_close) +m3ApiRawFunction(m3_wasi_generic_fd_close) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t, fd) uvwasi_errno_t ret = uvwasi_fd_close(&uvwasi, fd); + WASI_TRACE("fd:%d", fd); + m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_datasync) +m3ApiRawFunction(m3_wasi_generic_fd_datasync) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_fd_t, fd) uvwasi_errno_t ret = uvwasi_fd_datasync(&uvwasi, fd); + WASI_TRACE("fd:%d", fd); + m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_random_get) +m3ApiRawFunction(m3_wasi_generic_random_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint8_t * , buf) @@ -430,10 +826,12 @@ m3ApiRawFunction(m3_wasi_unstable_random_get) uvwasi_errno_t ret = uvwasi_random_get(&uvwasi, buf, buf_len); + WASI_TRACE("len:%d", buf_len); + m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_clock_res_get) +m3ApiRawFunction(m3_wasi_generic_clock_res_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_clockid_t , wasi_clk_id) @@ -443,12 +841,14 @@ m3ApiRawFunction(m3_wasi_unstable_clock_res_get) uvwasi_errno_t ret = uvwasi_clock_res_get(&uvwasi, wasi_clk_id, resolution); + WASI_TRACE("clk_id:%d", wasi_clk_id); + //TODO: m3ApiWriteMem64 m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_clock_time_get) +m3ApiRawFunction(m3_wasi_generic_clock_time_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (uvwasi_clockid_t , wasi_clk_id) @@ -459,12 +859,14 @@ m3ApiRawFunction(m3_wasi_unstable_clock_time_get) uvwasi_errno_t ret = uvwasi_clock_time_get(&uvwasi, wasi_clk_id, precision, time); + WASI_TRACE("clk_id:%d", wasi_clk_id); + //TODO: m3ApiWriteMem64 m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_poll_oneoff) +m3ApiRawFunction(m3_wasi_generic_poll_oneoff) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (const uvwasi_subscription_t * , in) @@ -476,14 +878,18 @@ m3ApiRawFunction(m3_wasi_unstable_poll_oneoff) m3ApiCheckMem(out, nsubscriptions * sizeof(uvwasi_event_t)); m3ApiCheckMem(nevents, sizeof(uvwasi_size_t)); + // TODO: unstable/snapshot_preview1 compatibility + uvwasi_errno_t ret = uvwasi_poll_oneoff(&uvwasi, in, out, nsubscriptions, nevents); + WASI_TRACE("nsubscriptions:%d | nevents:%d", nsubscriptions, *nevents); + //TODO: m3ApiWriteMem m3ApiReturn(ret); } -m3ApiRawFunction(m3_wasi_unstable_proc_exit) +m3ApiRawFunction(m3_wasi_generic_proc_exit) { m3ApiGetArg (uint32_t, code) @@ -493,6 +899,8 @@ m3ApiRawFunction(m3_wasi_unstable_proc_exit) context->exit_code = code; } + //TODO: fprintf(stderr, "proc_exit code:%d\n", code); + m3ApiTrap(m3Err_trapExit); } @@ -534,7 +942,7 @@ M3Result m3_LinkWASI (IM3Module module) uvwasi_preopen_t preopens[PREOPENS_COUNT]; preopens[0].mapped_path = "/"; preopens[0].real_path = "."; - preopens[1].mapped_path = "."; + preopens[1].mapped_path = "./"; preopens[1].real_path = "."; uvwasi_options_t init_options; @@ -544,73 +952,74 @@ M3Result m3_LinkWASI (IM3Module module) init_options.preopenc = PREOPENS_COUNT; init_options.preopens = preopens; - uvwasi_errno_t ret = uvwasi_init(&uvwasi, &init_options); - - if (ret != UVWASI_ESUCCESS) { - return "uvwasi_init failed"; - } - if (!wasi_context) { wasi_context = (m3_wasi_context_t*)malloc(sizeof(m3_wasi_context_t)); wasi_context->exit_code = 0; wasi_context->argc = 0; wasi_context->argv = 0; + + uvwasi_errno_t ret = uvwasi_init(&uvwasi, &init_options); + + if (ret != UVWASI_ESUCCESS) { + return "uvwasi_init failed"; + } } static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" }; // fd_seek is incompatible -_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_filestat_get", "i(i*)", &m3_wasi_unstable_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_filestat_get", "i(i*)", &m3_wasi_snapshot_preview1_fd_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "path_filestat_get", "i(ii*i*)", &m3_wasi_snapshot_preview1_path_filestat_get))); for (int i=0; i<2; i++) { const char* wasi = namespaces[i]; -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_unstable_args_get, wasi_context))); -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_unstable_args_sizes_get, wasi_context))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_unstable_clock_res_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_unstable_clock_time_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_unstable_environ_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_unstable_environ_sizes_get))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_unstable_fd_close))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_unstable_fd_datasync))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_unstable_fd_fdstat_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_unstable_fd_fdstat_set_flags))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_get", "i(i*)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_unstable_fd_prestat_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_unstable_fd_prestat_dir_name))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",&m3_wasi_generic_fd_pread))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_unstable_fd_read))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",&m3_wasi_unstable_fd_readdir))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",&m3_wasi_generic_fd_readdir))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_unstable_fd_write))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", &m3_wasi_generic_path_create_directory))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_unstable_path_open))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", ))); - -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_unstable_poll_oneoff))); -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_unstable_proc_exit, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", &m3_wasi_generic_path_readlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", &m3_wasi_generic_path_remove_directory))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", &m3_wasi_generic_path_rename))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", &m3_wasi_generic_path_symlink))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", &m3_wasi_generic_path_unlink_file))); + +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_unstable_random_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", ))); diff --git a/Sources/CWasm3/m3_api_wasi.c b/Sources/CWasm3/m3_api_wasi.c index 39edae8..47cccff 100644 --- a/Sources/CWasm3/m3_api_wasi.c +++ b/Sources/CWasm3/m3_api_wasi.c @@ -9,7 +9,6 @@ #include "m3_api_wasi.h" -#include "m3_api_defs.h" #include "m3_env.h" #include "m3_exception.h" @@ -83,8 +82,8 @@ Preopen preopen[PREOPEN_CNT] = { { 0, "" , "" }, { 1, "", "" }, { 2, "", "" }, - { -1, "./" , "." }, { -1, "/" , "." }, + { -1, "./" , "." }, }; #if defined(APE) @@ -205,7 +204,7 @@ void copy_iov_to_host(void* _mem, struct iovec* host_iov, wasi_iovec_t* wasi_iov * WASI API implementation */ -m3ApiRawFunction(m3_wasi_unstable_args_get) +m3ApiRawFunction(m3_wasi_generic_args_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint32_t * , argv) @@ -232,7 +231,7 @@ m3ApiRawFunction(m3_wasi_unstable_args_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_args_sizes_get) +m3ApiRawFunction(m3_wasi_generic_args_sizes_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (__wasi_size_t * , argc) @@ -257,7 +256,7 @@ m3ApiRawFunction(m3_wasi_unstable_args_sizes_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_environ_get) +m3ApiRawFunction(m3_wasi_generic_environ_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint32_t * , env) @@ -267,7 +266,7 @@ m3ApiRawFunction(m3_wasi_unstable_environ_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_environ_sizes_get) +m3ApiRawFunction(m3_wasi_generic_environ_sizes_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (__wasi_size_t * , env_count) @@ -283,7 +282,7 @@ m3ApiRawFunction(m3_wasi_unstable_environ_sizes_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_fd_prestat_dir_name) +m3ApiRawFunction(m3_wasi_generic_fd_prestat_dir_name) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -293,26 +292,27 @@ m3ApiRawFunction(m3_wasi_unstable_fd_prestat_dir_name) m3ApiCheckMem(path, path_len); if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); } - size_t slen = strlen(preopen[fd].path); + size_t slen = strlen(preopen[fd].path) + 1; memcpy(path, preopen[fd].path, M3_MIN(slen, path_len)); m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_fd_prestat_get) +m3ApiRawFunction(m3_wasi_generic_fd_prestat_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) - m3ApiGetArgMem (uint32_t * , buf) // TODO: use actual struct + m3ApiGetArgMem (uint8_t * , buf) - m3ApiCheckMem(buf, 2*sizeof(uint32_t)); + m3ApiCheckMem(buf, 8); if (fd < 3 || fd >= PREOPEN_CNT) { m3ApiReturn(__WASI_ERRNO_BADF); } - m3ApiWriteMem32(buf, __WASI_PREOPENTYPE_DIR); - m3ApiWriteMem32(buf+1, strlen(preopen[fd].path)); + + m3ApiWriteMem32(buf+0, __WASI_PREOPENTYPE_DIR); + m3ApiWriteMem32(buf+4, strlen(preopen[fd].path) + 1); m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_get) +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -359,12 +359,18 @@ m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_get) #endif // APE fdstat->fs_rights_base = (uint64_t)-1; // all rights + + // Make descriptors 0,1,2 look like a TTY + if (fd <= 2) { + fdstat->fs_rights_base &= ~(__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL); + } + fdstat->fs_rights_inheriting = (uint64_t)-1; // all rights m3ApiReturn(__WASI_ERRNO_SUCCESS); #endif } -m3ApiRawFunction(m3_wasi_unstable_fd_fdstat_set_flags) +m3ApiRawFunction(m3_wasi_generic_fd_fdstat_set_flags) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -380,16 +386,47 @@ m3ApiRawFunction(m3_wasi_unstable_fd_seek) m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) m3ApiGetArg (__wasi_filedelta_t , offset) - m3ApiGetArg (__wasi_whence_t , wasi_whence) + m3ApiGetArg (uint32_t , wasi_whence) + m3ApiGetArgMem (__wasi_filesize_t * , result) + + m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); + + int whence; + + switch (wasi_whence) { + case 0: whence = SEEK_CUR; break; + case 1: whence = SEEK_END; break; + case 2: whence = SEEK_SET; break; + default: m3ApiReturn(__WASI_ERRNO_INVAL); + } + + int64_t ret; +#if defined(M3_COMPILER_MSVC) || defined(__MINGW32__) + ret = _lseeki64(fd, offset, whence); +#else + ret = lseek(fd, offset, whence); +#endif + if (ret < 0) { m3ApiReturn(errno_to_wasi(errno)); } + m3ApiWriteMem64(result, ret); + m3ApiReturn(__WASI_ERRNO_SUCCESS); +} + +m3ApiRawFunction(m3_wasi_snapshot_preview1_fd_seek) +{ + m3ApiReturnType (uint32_t) + m3ApiGetArg (__wasi_fd_t , fd) + m3ApiGetArg (__wasi_filedelta_t , offset) + m3ApiGetArg (uint32_t , wasi_whence) m3ApiGetArgMem (__wasi_filesize_t * , result) m3ApiCheckMem(result, sizeof(__wasi_filesize_t)); int whence; + switch (wasi_whence) { - case __WASI_WHENCE_CUR: whence = SEEK_CUR; break; - case __WASI_WHENCE_END: whence = SEEK_END; break; - case __WASI_WHENCE_SET: whence = SEEK_SET; break; + case 0: whence = SEEK_SET; break; + case 1: whence = SEEK_CUR; break; + case 2: whence = SEEK_END; break; default: m3ApiReturn(__WASI_ERRNO_INVAL); } @@ -405,7 +442,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_seek) } -m3ApiRawFunction(m3_wasi_unstable_path_open) +m3ApiRawFunction(m3_wasi_generic_path_open) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , dirfd) @@ -468,7 +505,8 @@ m3ApiRawFunction(m3_wasi_unstable_path_open) int flags = ((oflags & __WASI_OFLAGS_CREAT) ? _O_CREAT : 0) | ((oflags & __WASI_OFLAGS_EXCL) ? _O_EXCL : 0) | ((oflags & __WASI_OFLAGS_TRUNC) ? _O_TRUNC : 0) | - ((fs_flags & __WASI_FDFLAGS_APPEND) ? _O_APPEND : 0); + ((fs_flags & __WASI_FDFLAGS_APPEND) ? _O_APPEND : 0) | + _O_BINARY; if ((fs_rights_base & __WASI_RIGHTS_FD_READ) && (fs_rights_base & __WASI_RIGHTS_FD_WRITE)) { @@ -525,7 +563,7 @@ m3ApiRawFunction(m3_wasi_unstable_path_open) #endif } -m3ApiRawFunction(m3_wasi_unstable_fd_read) +m3ApiRawFunction(m3_wasi_generic_fd_read) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -561,7 +599,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_read) #endif } -m3ApiRawFunction(m3_wasi_unstable_fd_write) +m3ApiRawFunction(m3_wasi_generic_fd_write) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t , fd) @@ -597,7 +635,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_write) #endif } -m3ApiRawFunction(m3_wasi_unstable_fd_close) +m3ApiRawFunction(m3_wasi_generic_fd_close) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t, fd) @@ -606,7 +644,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_close) m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret); } -m3ApiRawFunction(m3_wasi_unstable_fd_datasync) +m3ApiRawFunction(m3_wasi_generic_fd_datasync) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_fd_t, fd) @@ -623,7 +661,7 @@ m3ApiRawFunction(m3_wasi_unstable_fd_datasync) m3ApiReturn(ret == 0 ? __WASI_ERRNO_SUCCESS : ret); } -m3ApiRawFunction(m3_wasi_unstable_random_get) +m3ApiRawFunction(m3_wasi_generic_random_get) { m3ApiReturnType (uint32_t) m3ApiGetArgMem (uint8_t * , buf) @@ -664,7 +702,7 @@ m3ApiRawFunction(m3_wasi_unstable_random_get) } } -m3ApiRawFunction(m3_wasi_unstable_clock_res_get) +m3ApiRawFunction(m3_wasi_generic_clock_res_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) @@ -685,7 +723,7 @@ m3ApiRawFunction(m3_wasi_unstable_clock_res_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_clock_time_get) +m3ApiRawFunction(m3_wasi_generic_clock_time_get) { m3ApiReturnType (uint32_t) m3ApiGetArg (__wasi_clockid_t , wasi_clk_id) @@ -706,7 +744,7 @@ m3ApiRawFunction(m3_wasi_unstable_clock_time_get) m3ApiReturn(__WASI_ERRNO_SUCCESS); } -m3ApiRawFunction(m3_wasi_unstable_proc_exit) +m3ApiRawFunction(m3_wasi_generic_proc_exit) { m3ApiGetArg (uint32_t, code) @@ -760,54 +798,57 @@ M3Result m3_LinkWASI (IM3Module module) static const char* namespaces[2] = { "wasi_unstable", "wasi_snapshot_preview1" }; + // fd_seek is incompatible +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_unstable", "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, "wasi_snapshot_preview1", "fd_seek", "i(iIi*)", &m3_wasi_snapshot_preview1_fd_seek))); + for (int i=0; i<2; i++) { const char* wasi = namespaces[i]; -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_unstable_args_get, wasi_context))); -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_unstable_args_sizes_get, wasi_context))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_unstable_clock_res_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_unstable_clock_time_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_unstable_environ_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_unstable_environ_sizes_get))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_get", "i(**)", &m3_wasi_generic_args_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "args_sizes_get", "i(**)", &m3_wasi_generic_args_sizes_get, wasi_context))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_res_get", "i(i*)", &m3_wasi_generic_clock_res_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "clock_time_get", "i(iI*)", &m3_wasi_generic_clock_time_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_get", "i(**)", &m3_wasi_generic_environ_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "environ_sizes_get", "i(**)", &m3_wasi_generic_environ_sizes_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_advise", "i(iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_allocate", "i(iII)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_unstable_fd_close))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_unstable_fd_datasync))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_unstable_fd_fdstat_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_unstable_fd_fdstat_set_flags))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_close", "i(i)", &m3_wasi_generic_fd_close))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_datasync", "i(i)", &m3_wasi_generic_fd_datasync))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_get", "i(i*)", &m3_wasi_generic_fd_fdstat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_flags", "i(ii)", &m3_wasi_generic_fd_fdstat_set_flags))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_fdstat_set_rights", "i(iII)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_get", "i(i*)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_size", "i(iI)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_filestat_set_times","i(iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pread", "i(i*iI*)",))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_unstable_fd_prestat_get))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_unstable_fd_prestat_dir_name))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_get", "i(i*)", &m3_wasi_generic_fd_prestat_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_prestat_dir_name", "i(i*i)", &m3_wasi_generic_fd_prestat_dir_name))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_pwrite", "i(i*iI*)",))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_unstable_fd_read))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_read", "i(i*i*)", &m3_wasi_generic_fd_read))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_readdir", "i(i*iI*)",))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_renumber", "i(ii)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_seek", "i(iIi*)", &m3_wasi_unstable_fd_seek))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_sync", "i(i)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_tell", "i(i*)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_unstable_fd_write))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "fd_write", "i(i*i*)", &m3_wasi_generic_fd_write))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_create_directory", "i(i*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get", "i(ii*i*)", &m3_wasi_unstable_path_filestat_get))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_get", "i(ii*i*)", &m3_wasi_generic_path_filestat_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_filestat_set_times", "i(ii*iIIi)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_link", "i(ii*ii*i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_unstable_path_open))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_open", "i(ii*iiIIi*)", &m3_wasi_generic_path_open))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_readlink", "i(i*i*i*)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_remove_directory", "i(i*i)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_rename", "i(i*ii*i)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_symlink", "i(*ii*i)", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "path_unlink_file", "i(i*i)", ))); -//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_unstable_poll_oneoff))); -_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_unstable_proc_exit, wasi_context))); +//_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "poll_oneoff", "i(**i*)", &m3_wasi_generic_poll_oneoff))); +_ (SuppressLookupFailure (m3_LinkRawFunctionEx (module, wasi, "proc_exit", "v(i)", &m3_wasi_generic_proc_exit, wasi_context))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "proc_raise", "i(i)", ))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_unstable_random_get))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "random_get", "i(*i)", &m3_wasi_generic_random_get))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sched_yield", "i()", ))); //_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasi, "sock_recv", "i(i*ii**)", ))); diff --git a/Sources/CWasm3/m3_bind.c b/Sources/CWasm3/m3_bind.c index 86ea9f5..368fe83 100644 --- a/Sources/CWasm3/m3_bind.c +++ b/Sources/CWasm3/m3_bind.c @@ -30,6 +30,7 @@ M3Result SignatureToFuncType (IM3FuncType * o_functionType, ccstr_t i_signatur M3Result result = m3Err_none; IM3FuncType funcType = NULL; + _try { if (not o_functionType) _throw ("null function type"); @@ -39,22 +40,26 @@ _try { cstr_t sig = i_signature; - int maxNumArgs = strlen (i_signature) - 2; // "()" - _throwif (m3Err_malformedFunctionSignature, maxNumArgs < 0); - _throwif ("insane argument count", maxNumArgs > d_m3MaxSaneFunctionArgCount); + size_t maxNumTypes = strlen (i_signature); + + // assume min signature is "()" + _throwif (m3Err_malformedFunctionSignature, maxNumTypes < 2); + maxNumTypes -= 2; - const unsigned umaxNumArgs = (unsigned)maxNumArgs; + _throwif (m3Err_tooManyArgsRets, maxNumTypes > d_m3MaxSaneFunctionArgRetCount); -_ (AllocFuncType (& funcType, umaxNumArgs)); +_ (AllocFuncType (& funcType, (u32) maxNumTypes)); - bool parsingArgs = false; + u8 * typelist = funcType->types; + + bool parsingRets = true; while (* sig) { char typeChar = * sig++; if (typeChar == '(') { - parsingArgs = true; + parsingRets = false; continue; } else if ( typeChar == ' ') @@ -69,24 +74,24 @@ _ (AllocFuncType (& funcType, umaxNumArgs)); if (type == c_m3Type_none) continue; - if (not parsingArgs) + if (parsingRets) { - _throwif ("malformed function signature; too many return types", funcType->numRets >= 1); - - d_FuncRetType(funcType, funcType->numRets++) = type; + _throwif ("malformed signature; return count overflow", funcType->numRets >= maxNumTypes); + funcType->numRets++; + *typelist++ = type; } else { - _throwif (m3Err_malformedFunctionSignature, funcType->numArgs >= umaxNumArgs); // forgot trailing ')' ? - - d_FuncArgType(funcType, funcType->numArgs++) = type; + _throwif ("malformed signature; arg count overflow", (u32)(funcType->numRets) + funcType->numArgs >= maxNumTypes); + funcType->numArgs++; + *typelist++ = type; } } } _catch: if (result) - m3Free (funcType); // nulls funcType + m3_Free (funcType); * o_functionType = funcType; @@ -112,7 +117,7 @@ _ (SignatureToFuncType (& ftype, i_linkingSignature)); _catch: - m3Free (ftype); + m3_Free (ftype); return result; } @@ -123,8 +128,9 @@ M3Result LinkRawFunction (IM3Module io_module, IM3Function io_function, ccstr M3Result result = m3Err_none; d_m3Assert (io_module->runtime); _try { -_ (ValidateSignature (io_function, signature)); - + if (signature) { +_ (ValidateSignature (io_function, signature)); + } IM3CodePage page = AcquireCodePageWithCapacity (io_module->runtime, 4); if (page) diff --git a/Sources/CWasm3/m3_code.c b/Sources/CWasm3/m3_code.c index f625caa..8d52ac2 100644 --- a/Sources/CWasm3/m3_code.c +++ b/Sources/CWasm3/m3_code.c @@ -7,12 +7,6 @@ #include "m3_code.h" -#if d_m3RecordBacktraces -// Code mapping page ops - -M3CodeMappingPage * NewCodeMappingPage (u32 i_minCapacity); -void FreeCodeMappingPage (M3CodeMappingPage * i_page); -#endif // d_m3RecordBacktraces //--------------------------------------------------------------------------------------------------------------------------------- @@ -26,7 +20,7 @@ IM3CodePage NewCodePage (u32 i_minNumLines) u32 pageSize = sizeof (M3CodePageHeader) + sizeof (code_t) * i_minNumLines; pageSize = (pageSize + (d_m3CodePageAlignSize-1)) & ~(d_m3CodePageAlignSize-1); // align - m3Alloc ((void **) & page, u8, pageSize); + page = (IM3CodePage)m3_Malloc (pageSize); if (page) { @@ -34,10 +28,17 @@ IM3CodePage NewCodePage (u32 i_minNumLines) page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t); #if d_m3RecordBacktraces - page->info.mapping = NewCodeMappingPage (page->info.numLines); - if (!page->info.mapping) + u32 pageSizeBt = sizeof (M3CodeMappingPage) + sizeof (M3CodeMapEntry) * page->info.numLines; + page->info.mapping = (M3CodeMappingPage *)m3_Malloc (pageSizeBt); + + if (page->info.mapping) { - m3Free (page); + page->info.mapping->size = 0; + page->info.mapping->capacity = page->info.numLines; + } + else + { + m3_Free (page); return NULL; } page->info.mapping->basePC = GetPageStartPC(page); @@ -60,9 +61,9 @@ void FreeCodePages (IM3CodePage * io_list) IM3CodePage next = page->info.next; #if d_m3RecordBacktraces - FreeCodeMappingPage (page->info.mapping); + m3_Free (page->info.mapping); #endif // d_m3RecordBacktraces - m3Free (page); + m3_Free (page); page = next; } @@ -230,26 +231,3 @@ bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset) //--------------------------------------------------------------------------------------------------------------------------------- -#if d_m3RecordBacktraces -M3CodeMappingPage * NewCodeMappingPage (u32 i_minCapacity) -{ - M3CodeMappingPage * page; - u32 pageSize = sizeof (M3CodeMappingPage) + sizeof (M3CodeMapEntry) * i_minCapacity; - - m3Alloc ((void **) & page, u8, pageSize); - - if (page) - { - page->size = 0; - page->capacity = i_minCapacity; - } - - return page; -} - - -void FreeCodeMappingPage (M3CodeMappingPage * i_page) -{ - m3Free (i_page); -} -#endif // d_m3RecordBacktraces diff --git a/Sources/CWasm3/m3_compile.c b/Sources/CWasm3/m3_compile.c index 03dd6ff..06446ef 100644 --- a/Sources/CWasm3/m3_compile.c +++ b/Sources/CWasm3/m3_compile.c @@ -16,7 +16,7 @@ #define d_indent " | %s" -// just want less letter and numbers to stare at down the way in the compiler table +// just want less letters and numbers to stare at down the way in the compiler table #define i_32 c_m3Type_i32 #define i_64 c_m3Type_i64 #define f_32 c_m3Type_f32 @@ -25,9 +25,9 @@ #define any (u8)-1 #if d_m3HasFloat -#define FPOP(x) x +# define FPOP(x) x #else -#define FPOP(x) NULL +# define FPOP(x) NULL #endif static const IM3Operation c_preserveSetSlot [] = { NULL, op_PreserveSetSlot_i32, op_PreserveSetSlot_i64, @@ -39,12 +39,8 @@ static const IM3Operation c_setGlobalOps [] = { NULL, op_SetGlobal_i32, static const IM3Operation c_setRegisterOps [] = { NULL, op_SetRegister_i32, op_SetRegister_i64, FPOP(op_SetRegister_f32), FPOP(op_SetRegister_f64) }; -static const IM3Operation c_ifOps [2] [2] = { { op_i32_BranchIf_ss, op_i32_BranchIf_rs }, - { op_i64_BranchIf_ss, op_i64_BranchIf_rs } }; - static const IM3Operation c_intSelectOps [2] [4] = { { op_Select_i32_rss, op_Select_i32_srs, op_Select_i32_ssr, op_Select_i32_sss }, { op_Select_i64_rss, op_Select_i64_srs, op_Select_i64_ssr, op_Select_i64_sss } }; - #if d_m3HasFloat static const IM3Operation c_fpSelectOps [2] [2] [3] = { { { op_Select_f32_sss, op_Select_f32_srs, op_Select_f32_ssr }, // selector in slot { op_Select_f32_rss, op_Select_f32_rrs, op_Select_f32_rsr } }, // selector in reg @@ -55,6 +51,8 @@ static const IM3Operation c_fpSelectOps [2] [2] [3] = { { { op_Select_f32_sss, o static const u16 c_m3RegisterUnallocated = 0; static const u16 c_slotUnused = 0xffff; +// all args & returns are 64-bit aligned, so use 2 slots for a d_m3Use32BitSlots=1 build +static const u16 c_ioSlotCount = sizeof (u64) / sizeof (m3slot_t); M3Result AcquireCompilationCodePage (IM3Compilation o, IM3CodePage * o_codePage) { @@ -76,7 +74,7 @@ _ (m3ReallocArray (& func->codePageRefs, IM3CodePage, func->numCod func->codePageRefs [index] = page; } } -# endif +# endif } else _throw (m3Err_mallocFailedCodePage); @@ -92,40 +90,51 @@ void ReleaseCompilationCodePage (IM3Compilation o) ReleaseCodePage (o->runtime, o->page); } -bool IsStackPolymorphic (IM3Compilation o) -{ - return o->block.isPolymorphic; -} - -bool IsRegisterLocation (i16 i_location) { return (i_location >= d_m3Reg0SlotAlias); } -bool IsFpRegisterLocation (i16 i_location) { return (i_location == d_m3Fp0SlotAlias); } -bool IsIntRegisterLocation (i16 i_location) { return (i_location == d_m3Reg0SlotAlias); } - -i16 GetNumBlockValues (IM3Compilation o) { return o->stackIndex - o->block.initStackIndex; } +bool IsRegisterSlotAlias (u16 i_slot) { return (i_slot >= d_m3Reg0SlotAlias and i_slot != c_slotUnused); } +bool IsFpRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Fp0SlotAlias); } +bool IsIntRegisterSlotAlias (u16 i_slot) { return (i_slot == d_m3Reg0SlotAlias); } u16 GetTypeNumSlots (u8 i_type) { # if d_m3Use32BitSlots - u16 n = Is64BitType (i_type) ? 2 : 1; - return n; + return Is64BitType (i_type) ? 2 : 1; # else return 1; # endif } + +void AlignSlotToType (u16 * io_slot, u8 i_type) +{ + // align 64-bit words to even slots (if d_m3Use32BitSlots) + u16 numSlots = GetTypeNumSlots (i_type); + + u16 mask = numSlots - 1; + * io_slot = (* io_slot + mask) & ~mask; +} + + i16 GetStackTopIndex (IM3Compilation o) -{ d_m3Assert (o->stackIndex > 0 or IsStackPolymorphic (o)); +{ d_m3Assert (o->stackIndex > o->stackFirstDynamicIndex or IsStackPolymorphic (o)); return o->stackIndex - 1; } -u8 GetStackTopTypeAtOffset (IM3Compilation o, u16 i_offset) +// Items in the static portion of the stack (args/locals) are hidden from GetStackTypeFromTop () +// In other words, only "real" Wasm stack items can be inspected. This is important when +// returning values, etc. and you need an accurate wasm-view of the stack. +u8 GetStackTypeFromTop (IM3Compilation o, u16 i_offset) { u8 type = c_m3Type_none; ++i_offset; if (o->stackIndex >= i_offset) - type = o->typeStack [o->stackIndex - i_offset]; + { + u16 index = o->stackIndex - i_offset; + + if (index >= o->stackFirstDynamicIndex) + type = o->typeStack [index]; + } return type; } @@ -133,11 +142,11 @@ u8 GetStackTopTypeAtOffset (IM3Compilation o, u16 i_offset) u8 GetStackTopType (IM3Compilation o) { - return GetStackTopTypeAtOffset (o, 0); + return GetStackTypeFromTop (o, 0); } -u8 GetStackBottomType (IM3Compilation o, u16 i_offset) +u8 GetStackTypeFromBottom (IM3Compilation o, u16 i_offset) { u8 type = c_m3Type_none; @@ -148,49 +157,30 @@ u8 GetStackBottomType (IM3Compilation o, u16 i_offset) } -bool IsStackIndexInRegister (IM3Compilation o, u16 i_stackIndex) -{ d_m3Assert (i_stackIndex < o->stackIndex or IsStackPolymorphic (o)); - if (i_stackIndex < o->stackIndex) - return (o->wasmStack [i_stackIndex] >= d_m3Reg0SlotAlias); - else - return false; -} +bool IsConstantSlot (IM3Compilation o, u16 i_slot) { return (i_slot >= o->slotFirstConstIndex and i_slot < o->slotMaxConstIndex); } +bool IsSlotAllocated (IM3Compilation o, u16 i_slot) { return o->m3Slots [i_slot]; } -bool IsStackTopIndexInRegister (IM3Compilation o, i16 i_stackTopOffset) -{ d_m3Assert (i_stackTopOffset >= 0 or IsStackPolymorphic (o)); - if (i_stackTopOffset >= 0) - return IsStackIndexInRegister (o, (u16) i_stackTopOffset); +bool IsStackIndexInRegister (IM3Compilation o, i32 i_stackIndex) +{ d_m3Assert (i_stackIndex < o->stackIndex or IsStackPolymorphic (o)); + if (i_stackIndex >= 0 and i_stackIndex < o->stackIndex) + return (o->wasmStack [i_stackIndex] >= d_m3Reg0SlotAlias); else return false; } +u16 GetNumBlockValuesOnStack (IM3Compilation o) { return o->stackIndex - o->block.blockStackIndex; } -bool IsStackTopInRegister (IM3Compilation o) -{ - return IsStackTopIndexInRegister (o, GetStackTopIndex (o)); -} - - -bool IsStackTopMinus1InRegister (IM3Compilation o) -{ - return IsStackTopIndexInRegister (o, GetStackTopIndex (o) - 1); -} - - -bool IsStackTopMinus2InRegister (IM3Compilation o) -{ - return IsStackTopIndexInRegister (o, GetStackTopIndex (o) - 2); -} +bool IsStackTopInRegister (IM3Compilation o) { return IsStackIndexInRegister (o, (i32) GetStackTopIndex (o)); } +bool IsStackTopMinus1InRegister (IM3Compilation o) { return IsStackIndexInRegister (o, (i32) GetStackTopIndex (o) - 1); } +bool IsStackTopMinus2InRegister (IM3Compilation o) { return IsStackIndexInRegister (o, (i32) GetStackTopIndex (o) - 2); } +bool IsStackTopInSlot (IM3Compilation o) { return not IsStackTopInRegister (o); } -bool IsStackTopInSlot (IM3Compilation o) -{ - return not IsStackTopInRegister (o); -} +bool IsValidSlot (u16 i_slot) { return (i_slot < d_m3MaxFunctionSlots); } -u16 GetStackTopSlotIndex (IM3Compilation o) +u16 GetStackTopSlotNumber (IM3Compilation o) { i16 i = GetStackTopIndex (o); @@ -203,6 +193,7 @@ u16 GetStackTopSlotIndex (IM3Compilation o) } +// from bottom u16 GetSlotForStackIndex (IM3Compilation o, u16 i_stackIndex) { d_m3Assert (i_stackIndex < o->stackIndex or IsStackPolymorphic (o)); u16 slot = c_slotUnused; @@ -214,15 +205,28 @@ u16 GetSlotForStackIndex (IM3Compilation o, u16 i_stackIndex) } -bool IsValidSlot (u16 i_slot) +u16 GetExtraSlotForStackIndex (IM3Compilation o, u16 i_stackIndex) { - return (i_slot < d_m3MaxFunctionSlots); + u16 baseSlot = GetSlotForStackIndex (o, i_stackIndex); + + if (baseSlot != c_slotUnused) + { + u16 extraSlot = GetTypeNumSlots (GetStackTypeFromBottom (o, i_stackIndex)) - 1; + baseSlot += extraSlot; + } + + return baseSlot; } -bool IsSlotAllocated (IM3Compilation o, u16 i_slot) + +void TouchSlot (IM3Compilation o, u16 i_slot) { - return o->m3Slots [i_slot]; + if (o->function) + { + // op_Entry uses this value to track and detect stack overflow + o->maxStackSlots = M3_MAX (o->maxStackSlots, i_slot + 1); + } } @@ -230,10 +234,26 @@ void MarkSlotAllocated (IM3Compilation o, u16 i_slot) { d_m3Assert (o->m3Slots [i_slot] == 0); // shouldn't be already allocated o->m3Slots [i_slot] = 1; - o->maxAllocatedSlotPlusOne = M3_MAX (o->maxAllocatedSlotPlusOne, i_slot + 1); + o->slotMaxAllocatedIndexPlusOne = M3_MAX (o->slotMaxAllocatedIndexPlusOne, i_slot + 1); + + TouchSlot (o, i_slot); +} + + +void MarkSlotsAllocated (IM3Compilation o, u16 i_slot, u16 i_numSlots) +{ + while (i_numSlots--) + MarkSlotAllocated (o, i_slot++); +} + +void MarkSlotsAllocatedByType (IM3Compilation o, u16 i_slot, u8 i_type) +{ + u16 numSlots = GetTypeNumSlots (i_type); + MarkSlotsAllocated (o, i_slot, numSlots); } + M3Result AllocateSlotsWithinRange (IM3Compilation o, u16 * o_slot, u8 i_type, u16 i_startSlot, u16 i_endSlot) { M3Result result = m3Err_functionStackOverflow; @@ -241,9 +261,7 @@ M3Result AllocateSlotsWithinRange (IM3Compilation o, u16 * o_slot, u8 i_type, u16 numSlots = GetTypeNumSlots (i_type); u16 searchOffset = numSlots - 1; - if (d_m3Use32BitSlots) { - AlignSlotIndexToType (& i_startSlot, i_type); - } + AlignSlotToType (& i_startSlot, i_type); // search for 1 or 2 consecutive slots in the execution stack u16 i = i_startSlot; @@ -251,10 +269,7 @@ M3Result AllocateSlotsWithinRange (IM3Compilation o, u16 * o_slot, u8 i_type, { if (o->m3Slots [i] == 0 and o->m3Slots [i + searchOffset] == 0) { - MarkSlotAllocated (o, i); - - if (numSlots == 2) - MarkSlotAllocated (o, i + 1); + MarkSlotsAllocated (o, i, numSlots); * o_slot = i; result = m3Err_none; @@ -271,16 +286,19 @@ M3Result AllocateSlotsWithinRange (IM3Compilation o, u16 * o_slot, u8 i_type, M3Result AllocateSlots (IM3Compilation o, u16 * o_slot, u8 i_type) { - return AllocateSlotsWithinRange (o, o_slot, i_type, o->firstDynamicSlotIndex, d_m3MaxFunctionSlots); + return AllocateSlotsWithinRange (o, o_slot, i_type, o->slotFirstDynamicIndex, d_m3MaxFunctionSlots); } M3Result AllocateConstantSlots (IM3Compilation o, u16 * o_slot, u8 i_type) { - return AllocateSlotsWithinRange (o, o_slot, i_type, o->firstConstSlotIndex, o->firstDynamicSlotIndex); + u16 maxTableIndex = o->slotFirstConstIndex + d_m3MaxConstantTableSize; + return AllocateSlotsWithinRange (o, o_slot, i_type, o->slotFirstConstIndex, M3_MIN(o->slotFirstDynamicIndex, maxTableIndex)); } +// TOQUE: this usage count system could be eliminated. real world code doesn't frequently trigger it. just copy to multiple +// unique slots. M3Result IncrementSlotUsageCount (IM3Compilation o, u16 i_slot) { d_m3Assert (i_slot < d_m3MaxFunctionSlots); M3Result result = m3Err_none; d_m3Assert (o->m3Slots [i_slot] > 0); @@ -297,12 +315,12 @@ M3Result IncrementSlotUsageCount (IM3Compilation o, u16 i_slot) } -void DeallocateSlot (IM3Compilation o, i16 i_slotIndex, u8 i_type) -{ d_m3Assert (i_slotIndex >= o->firstDynamicSlotIndex); - d_m3Assert (o->m3Slots [i_slotIndex]); - for (u16 i = 0; i < GetTypeNumSlots (i_type); ++i, ++i_slotIndex) - { - -- o->m3Slots [i_slotIndex]; +void DeallocateSlot (IM3Compilation o, i16 i_slot, u8 i_type) +{ d_m3Assert (i_slot >= o->slotFirstDynamicIndex); + d_m3Assert (i_slot < o->slotMaxAllocatedIndexPlusOne); + for (u16 i = 0; i < GetTypeNumSlots (i_type); ++i, ++i_slot) + { d_m3Assert (o->m3Slots [i_slot]); + -- o->m3Slots [i_slot]; } } @@ -319,40 +337,43 @@ bool IsRegisterTypeAllocated (IM3Compilation o, u8 i_type) } void AllocateRegister (IM3Compilation o, u32 i_register, u16 i_stackIndex) -{ - d_m3Assert (not IsRegisterAllocated (o, i_register)); - +{ d_m3Assert (not IsRegisterAllocated (o, i_register)); o->regStackIndexPlusOne [i_register] = i_stackIndex + 1; } void DeallocateRegister (IM3Compilation o, u32 i_register) -{ - d_m3Assert (IsRegisterAllocated (o, i_register)); - +{ d_m3Assert (IsRegisterAllocated (o, i_register)); o->regStackIndexPlusOne [i_register] = c_m3RegisterUnallocated; } u16 GetRegisterStackIndex (IM3Compilation o, u32 i_register) -{ - d_m3Assert (IsRegisterAllocated (o, i_register)); - +{ d_m3Assert (IsRegisterAllocated (o, i_register)); return o->regStackIndexPlusOne [i_register] - 1; } u16 GetMaxUsedSlotPlusOne (IM3Compilation o) { - while (o->maxAllocatedSlotPlusOne > o->firstDynamicSlotIndex) + while (o->slotMaxAllocatedIndexPlusOne > o->slotFirstDynamicIndex) { - if (IsSlotAllocated (o, o->maxAllocatedSlotPlusOne - 1)) + if (IsSlotAllocated (o, o->slotMaxAllocatedIndexPlusOne - 1)) break; - o->maxAllocatedSlotPlusOne--; + o->slotMaxAllocatedIndexPlusOne--; } - return o->maxAllocatedSlotPlusOne; +# ifdef DEBUG + u16 maxSlot = o->slotMaxAllocatedIndexPlusOne; + while (maxSlot < d_m3MaxFunctionSlots) + { + d_m3Assert (o->m3Slots [maxSlot] == 0); + maxSlot++; + } +# endif + + return o->slotMaxAllocatedIndexPlusOne; } @@ -367,7 +388,7 @@ M3Result PreserveRegisterIfOccupied (IM3Compilation o, u8 i_registerType) u16 stackIndex = GetRegisterStackIndex (o, regSelect); DeallocateRegister (o, regSelect); - u8 type = GetStackBottomType (o, stackIndex); + u8 type = GetStackTypeFromBottom (o, stackIndex); // and point to a exec slot u16 slot = c_slotUnused; @@ -382,7 +403,7 @@ _ (EmitOp (o, c_setSetOps [type])); } -// all values must be in slots befor entering loop, if, and else blocks +// all values must be in slots before entering loop, if, and else blocks // otherwise they'd end up preserve-copied in the block to probably different locations (if/else) M3Result PreserveRegisters (IM3Compilation o) { @@ -423,7 +444,7 @@ _ (PreserveRegisterIfOccupied (o, c_m3Type_f64)); //---------------------------------------------------------------------------------------------------------------------- -M3Result Push (IM3Compilation o, u8 i_type, u16 i_location) +M3Result Push (IM3Compilation o, u8 i_type, u16 i_slot) { M3Result result = m3Err_none; @@ -437,22 +458,14 @@ M3Result Push (IM3Compilation o, u8 i_type, u16 i_location) if (stackIndex < d_m3MaxFunctionStackHeight) { - o->wasmStack [stackIndex] = i_location; + o->wasmStack [stackIndex] = i_slot; o->typeStack [stackIndex] = i_type; - if (IsRegisterLocation (i_location)) + if (IsRegisterSlotAlias (i_slot)) { - u32 regSelect = IsFpRegisterLocation (i_location); + u32 regSelect = IsFpRegisterSlotAlias (i_slot); AllocateRegister (o, regSelect, stackIndex); } - else - { - if (o->function) - { - // op_Entry uses this value to track and detect stack overflow - o->function->maxStackSlots = M3_MAX (o->function->maxStackSlots, i_location + 1); - } - } if (d_m3LogWasmStack) dump_type_stack (o); } @@ -464,8 +477,12 @@ M3Result Push (IM3Compilation o, u8 i_type, u16 i_location) M3Result PushRegister (IM3Compilation o, u8 i_type) { - u16 location = IsFpType (i_type) ? d_m3Fp0SlotAlias : d_m3Reg0SlotAlias; d_m3Assert (i_type or IsStackPolymorphic (o)); - return Push (o, i_type, location); + M3Result result = m3Err_none; d_m3Assert ((u16) d_m3Reg0SlotAlias > (u16) d_m3MaxFunctionSlots); + u16 slot = IsFpType (i_type) ? d_m3Fp0SlotAlias : d_m3Reg0SlotAlias; d_m3Assert (i_type or IsStackPolymorphic (o)); + +_ (Push (o, i_type, slot)); + + _catch: return result; } @@ -473,19 +490,19 @@ M3Result Pop (IM3Compilation o) { M3Result result = m3Err_none; - if (o->stackIndex > o->block.initStackIndex) + if (o->stackIndex > o->block.blockStackIndex) { o->stackIndex--; // printf ("pop: %d\n", (i32) o->stackIndex); u16 slot = o->wasmStack [o->stackIndex]; u8 type = o->typeStack [o->stackIndex]; - if (IsRegisterLocation (slot)) + if (IsRegisterSlotAlias (slot)) { - u32 regSelect = IsFpRegisterLocation (slot); + u32 regSelect = IsFpRegisterSlotAlias (slot); DeallocateRegister (o, regSelect); } - else if (slot >= o->firstDynamicSlotIndex) + else if (slot >= o->slotFirstDynamicIndex) { DeallocateSlot (o, slot, type); } @@ -497,23 +514,20 @@ M3Result Pop (IM3Compilation o) } -M3Result UnwindBlockStack (IM3Compilation o) +M3Result PopType (IM3Compilation o, u8 i_type) { M3Result result = m3Err_none; - i16 initStackIndex = o->block.initStackIndex; + u8 topType = GetStackTopType (o); - u32 popCount = 0; - while (o->stackIndex > initStackIndex ) + if (i_type == topType or o->block.isPolymorphic) { _ (Pop (o)); - ++popCount; } + else _throw (m3Err_typeMismatch); - if (popCount) - m3log (compile, "unwound stack top: %d", popCount); - - _catch: return result; + _catch: + return result; } @@ -555,27 +569,22 @@ M3Result PushConst (IM3Compilation o, u64 i_word, u8 i_type) if (!o->page) return result; bool matchFound = false; - bool is64BitType = Is64BitType(i_type); + bool is64BitType = Is64BitType (i_type); - u32 numUsedConstSlots = o->maxConstSlotIndex - o->firstConstSlotIndex; u16 numRequiredSlots = GetTypeNumSlots (i_type); + u16 numUsedConstSlots = o->slotMaxConstIndex - o->slotFirstConstIndex; // search for duplicate matching constant slot to reuse if (numRequiredSlots == 2 and numUsedConstSlots >= 2) { - u16 firstConstSlot = o->firstConstSlotIndex; - AlignSlotIndexToType (& firstConstSlot, c_m3Type_i64); + u16 firstConstSlot = o->slotFirstConstIndex; + AlignSlotToType (& firstConstSlot, c_m3Type_i64); - for (int slot = firstConstSlot; slot < o->maxConstSlotIndex - 1; slot += 2) + for (u16 slot = firstConstSlot; slot < o->slotMaxConstIndex - 1; slot += 2) { if (IsSlotAllocated (o, slot) and IsSlotAllocated (o, slot + 1)) { - u64 constant; - if (is64BitType) { - constant = * (u64 *) & o->constants [slot - o->firstConstSlotIndex]; - } else { - constant = * (u32 *) & o->constants [slot - o->firstConstSlotIndex]; - } + u64 constant = * (u64 *) & o->constants [slot - o->slotFirstConstIndex]; if (constant == i_word) { @@ -588,9 +597,9 @@ _ (Push (o, i_type, slot)); } else if (numRequiredSlots == 1) { - for (u32 i = 0; i < numUsedConstSlots; ++i) + for (u16 i = 0; i < numUsedConstSlots; ++i) { - u16 slot = o->firstConstSlotIndex + i; + u16 slot = o->slotFirstConstIndex + i; if (IsSlotAllocated (o, slot)) { @@ -619,19 +628,21 @@ _ (Push (o, i_type, slot)); { result = m3Err_none; - if (Is64BitType (i_type)) { + if (is64BitType) { _ (EmitOp (o, op_Const64)); EmitWord64 (o->page, i_word); } else { _ (EmitOp (o, op_Const32)); - EmitWord32 (o->page, i_word); + EmitWord32 (o->page, (u32) i_word); } _ (PushAllocatedSlotAndEmit (o, i_type)); } else { - u16 constTableIndex = slot - o->firstConstSlotIndex; + u16 constTableIndex = slot - o->slotFirstConstIndex; + + d_m3Assert(constTableIndex < d_m3MaxConstantTableSize); if (is64BitType) { @@ -641,12 +652,12 @@ _ (PushAllocatedSlotAndEmit (o, i_type)); else { u32 * constant = (u32 *) & o->constants [constTableIndex]; - * constant = i_word; + * constant = (u32) i_word; } _ (Push (o, i_type, slot)); - o->maxConstSlotIndex = M3_MAX (slot + numRequiredSlots, o->maxConstSlotIndex); + o->slotMaxConstIndex = M3_MAX (slot + numRequiredSlots, o->slotMaxConstIndex); } } @@ -654,10 +665,11 @@ _ (Push (o, i_type, slot)); } -M3Result EmitTopSlotAndPop (IM3Compilation o) +M3Result EmitSlotNumOfStackTopAndPop (IM3Compilation o) { + // no emit if value is in register if (IsStackTopInSlot (o)) - EmitSlotOffset (o, GetStackTopSlotIndex (o)); + EmitSlotOffset (o, GetStackTopSlotNumber (o)); return Pop (o); } @@ -675,71 +687,64 @@ M3Result AddTrapRecord (IM3Compilation o) return result; } - -M3Result AcquirePatch (IM3Compilation o, IM3BranchPatch * o_patch) +M3Result UnwindBlockStack (IM3Compilation o) { M3Result result = m3Err_none; - IM3BranchPatch patch = o->releasedPatches; - - if (patch) + u32 popCount = 0; + while (o->stackIndex > o->block.blockStackIndex) { - o->releasedPatches = patch->next; - patch->next = NULL; +_ (Pop (o)); + ++popCount; } - else -_ (m3Alloc (& patch, M3BranchPatch, 1)); - * o_patch = patch; + if (popCount) + { + m3log (compile, "unwound stack top: %d", popCount); + } _catch: return result; } -bool PatchBranches (IM3Compilation o) +bool IsStackPolymorphic (IM3Compilation o) { - bool didPatch = false; - - M3CompilationScope * block = & o->block; - pc_t pc = GetPC (o); + return o->block.isPolymorphic; +} - IM3BranchPatch patches = block->patches; - IM3BranchPatch endPatch = patches; - while (patches) - { m3log (compile, "patching location: %p to pc: %p", patches->location, pc); - if (not patches->location) - break; +M3Result SetStackPolymorphic (IM3Compilation o) +{ + o->block.isPolymorphic = true; m3log (compile, "stack set polymorphic"); + return UnwindBlockStack (o); +} - * (patches->location) = pc; - endPatch = patches; - patches = patches->next; - } +void PatchBranches (IM3Compilation o) +{ + pc_t pc = GetPC (o); - if (block->patches) - { d_m3Assert (endPatch->next == NULL); - // return patches to pool - endPatch->next = o->releasedPatches; - o->releasedPatches = block->patches; - block->patches = NULL; + pc_t patches = o->block.patches; + o->block.patches = NULL; - didPatch = true; + while (patches) + { m3log (compile, "patching location: %p to pc: %p", patches, pc); + pc_t next = * (pc_t *) patches; + * (pc_t *) patches = pc; + patches = next; } - - return didPatch; } //------------------------------------------------------------------------------------------------------------------------- -M3Result CopyStackSlot (IM3Compilation o, u16 i_stackIndex, u16 i_destSlot) +M3Result CopyStackIndexToSlot (IM3Compilation o, u16 i_destSlot, u16 i_stackIndex) // NoPushPop { M3Result result = m3Err_none; IM3Operation op; - u8 type = GetStackBottomType (o, i_stackIndex); + u8 type = GetStackTypeFromBottom (o, i_stackIndex); bool inRegister = IsStackIndexInRegister (o, i_stackIndex); if (inRegister) @@ -761,12 +766,12 @@ _ (EmitOp (o, op)); } -M3Result CopyTopSlot (IM3Compilation o, u16 i_destSlot) +M3Result CopyStackTopToSlot (IM3Compilation o, u16 i_destSlot) // NoPushPop { M3Result result; i16 stackTop = GetStackTopIndex (o); -_ (CopyStackSlot (o, (u16) stackTop, i_destSlot)); +_ (CopyStackIndexToSlot (o, i_destSlot, (u16) stackTop)); _catch: return result; } @@ -777,7 +782,7 @@ _ (CopyStackSlot (o, (u16) stackTop, i_destSlot)); // then, when a previously referenced local is set, the current value needs to be preserved for those references // TODO: consider getting rid of these specialized operations: PreserveSetSlot & PreserveCopySlot. -// They likely just take up space without improving performance. +// They likely just take up space (which seems to reduce performance) without improving performance. M3Result PreservedCopyTopSlot (IM3Compilation o, u16 i_destSlot, u16 i_preserveSlot) { M3Result result = m3Err_none; d_m3Assert (i_destSlot != i_preserveSlot); @@ -796,7 +801,7 @@ _ (EmitOp (o, op)); EmitSlotOffset (o, i_destSlot); if (IsStackTopInSlot (o)) - EmitSlotOffset (o, GetStackTopSlotIndex (o)); + EmitSlotOffset (o, GetStackTopSlotNumber (o)); EmitSlotOffset (o, i_preserveSlot); @@ -804,8 +809,7 @@ _ (EmitOp (o, op)); } - -M3Result MoveStackTopToRegister (IM3Compilation o) +M3Result CopyStackTopToRegister (IM3Compilation o, bool i_updateStack) { M3Result result = m3Err_none; @@ -818,43 +822,26 @@ _ (PreserveRegisterIfOccupied (o, type)); IM3Operation op = c_setRegisterOps [type]; _ (EmitOp (o, op)); -_ (EmitTopSlotAndPop (o)); -_ (PushRegister (o, type)); - } - - _catch: return result; -} + EmitSlotOffset (o, GetStackTopSlotNumber (o)); - - -M3Result ReturnStackTop (IM3Compilation o) -{ - M3Result result = m3Err_none; - - i16 top = GetStackTopIndex (o); - - if (top >= 0) - { - const u16 returnSlot = 0; - - if (o->wasmStack [top] != returnSlot) - CopyTopSlot (o, returnSlot); + if (i_updateStack) + { +_ (PopType (o, type)); +_ (PushRegister (o, type)); + } } - else if (not IsStackPolymorphic (o)) - result = m3Err_functionStackUnderrun; - return result; + _catch: return result; } - -// if local is unreferenced, o_preservedSlotIndex will be equal to localIndex on return -M3Result FindReferencedLocalWithinCurrentBlock (IM3Compilation o, u16 * o_preservedSlotIndex, u32 i_localSlot) +// if local is unreferenced, o_preservedSlotNumber will be equal to localIndex on return +M3Result FindReferencedLocalWithinCurrentBlock (IM3Compilation o, u16 * o_preservedSlotNumber, u32 i_localSlot) { M3Result result = m3Err_none; IM3CompilationScope scope = & o->block; - i16 startIndex = scope->initStackIndex; + i16 startIndex = scope->blockStackIndex; while (scope->opcode == c_waOp_block) { @@ -862,25 +849,25 @@ M3Result FindReferencedLocalWithinCurrentBlock (IM3Compilation o, u16 * o_pres if (not scope) break; - startIndex = scope->initStackIndex; + startIndex = scope->blockStackIndex; } - * o_preservedSlotIndex = (u16) i_localSlot; + * o_preservedSlotNumber = (u16) i_localSlot; for (u32 i = startIndex; i < o->stackIndex; ++i) { if (o->wasmStack [i] == i_localSlot) { - if (* o_preservedSlotIndex == i_localSlot) + if (* o_preservedSlotNumber == i_localSlot) { - u8 localType = GetStackBottomType (o, i_localSlot); + u8 type = GetStackTypeFromBottom (o, i); d_m3Assert (type != c_m3Type_none) -_ (AllocateSlots (o, o_preservedSlotIndex, localType)); +_ (AllocateSlots (o, o_preservedSlotNumber, type)); } else -_ (IncrementSlotUsageCount (o, * o_preservedSlotIndex)); +_ (IncrementSlotUsageCount (o, * o_preservedSlotNumber)); - o->wasmStack [i] = * o_preservedSlotIndex; + o->wasmStack [i] = * o_preservedSlotNumber; } } @@ -888,21 +875,179 @@ _ (IncrementSlotUsageCount (o, * o_preservedSlotIndex)); } - M3Result GetBlockScope (IM3Compilation o, IM3CompilationScope * o_scope, i32 i_depth) { + M3Result result = m3Err_none; + IM3CompilationScope scope = & o->block; while (i_depth--) { scope = scope->outer; - if (not scope) - return "invalid block depth"; + _throwif ("invalid block depth", not scope); } * o_scope = scope; - return m3Err_none; + _catch: + return result; +} + + +M3Result CopyStackSlotsR (IM3Compilation o, u16 i_targetSlotStackIndex, u16 i_stackIndex, u16 i_endStackIndex, u16 i_tempSlot) +{ + M3Result result = m3Err_none; + + if (i_stackIndex < i_endStackIndex) + { + u16 srcSlot = GetSlotForStackIndex (o, i_stackIndex); + + u8 type = GetStackTypeFromBottom (o, i_stackIndex); + u16 numSlots = GetTypeNumSlots (type); + u16 extraSlot = numSlots - 1; + + u16 targetSlot = GetSlotForStackIndex (o, i_targetSlotStackIndex); + + u16 preserveIndex = i_stackIndex; + u16 collisionSlot = srcSlot; + + if (targetSlot != srcSlot) + { + // search for collisions + u16 checkIndex = i_stackIndex + 1; + while (checkIndex < i_endStackIndex) + { + u16 otherSlot1 = GetSlotForStackIndex (o, checkIndex); + u16 otherSlot2 = GetExtraSlotForStackIndex (o, checkIndex); + + if (targetSlot == otherSlot1 or + targetSlot == otherSlot2 or + targetSlot + extraSlot == otherSlot1) + { + _throwif (m3Err_functionStackOverflow, i_tempSlot >= d_m3MaxFunctionSlots); + +_ (CopyStackIndexToSlot (o, i_tempSlot, checkIndex)); + o->wasmStack [checkIndex] = i_tempSlot; + i_tempSlot += GetTypeNumSlots (c_m3Type_i64); + TouchSlot (o, i_tempSlot - 1); + + // restore this on the way back down + preserveIndex = checkIndex; + collisionSlot = otherSlot1; + + break; + } + + ++checkIndex; + } + +_ (CopyStackIndexToSlot (o, targetSlot, i_stackIndex)); m3log (compile, " copying slot: %d to slot: %d", srcSlot, targetSlot); + o->wasmStack [i_stackIndex] = targetSlot; + + } + +_ (CopyStackSlotsR (o, i_targetSlotStackIndex + 1, i_stackIndex + 1, i_endStackIndex, i_tempSlot)); + + // restore the stack state + o->wasmStack [i_stackIndex] = srcSlot; + o->wasmStack [preserveIndex] = collisionSlot; + } + + _catch: + return result; +} + + +M3Result ResolveBlockResults (IM3Compilation o, IM3CompilationScope i_targetBlock, bool i_isBranch) +{ + M3Result result = m3Err_none; if (d_m3LogWasmStack) dump_type_stack (o); + + bool isLoop = (i_targetBlock->opcode == c_waOp_loop and i_isBranch); + + u16 numParams = GetFuncTypeNumParams (i_targetBlock->type); + u16 numResults = GetFuncTypeNumResults (i_targetBlock->type); + + u16 slotRecords = i_targetBlock->exitStackIndex; + + u16 numValues; + + if (not isLoop) + { + numValues = numResults; + slotRecords += numParams; + } + else numValues = numParams; + + u16 blockHeight = GetNumBlockValuesOnStack (o); + + _throwif (m3Err_typeCountMismatch, i_isBranch ? (blockHeight < numValues) : (blockHeight != numValues)); + + if (numValues) + { + u16 endIndex = GetStackTopIndex (o) + 1; + + if (not isLoop and IsFpType (GetStackTopType (o))) + { +_ (CopyStackTopToRegister (o, false)); + --endIndex; + } + + // TODO: tempslot affects maxStackSlots, so can grow unnecess each time. + u16 tempSlot = o->maxStackSlots;// GetMaxUsedSlotPlusOne (o); doesn't work cause can collide with slotRecords + AlignSlotToType (& tempSlot, c_m3Type_i64); + +_ (CopyStackSlotsR (o, slotRecords, endIndex - numValues, endIndex, tempSlot)); + + if (d_m3LogWasmStack) dump_type_stack (o); + } + + _catch: return result; +} + + + +M3Result ReturnValues (IM3Compilation o, IM3CompilationScope i_functionBlock, bool i_isBranch) +{ + M3Result result = m3Err_none; if (d_m3LogWasmStack) dump_type_stack (o); + + u16 numReturns = GetFuncTypeNumResults (i_functionBlock->type); // could just o->function too... + u16 blockHeight = GetNumBlockValuesOnStack (o); + + if (not IsStackPolymorphic (o)) + _throwif (m3Err_typeCountMismatch, i_isBranch ? (blockHeight < numReturns) : (blockHeight != numReturns)); + + if (numReturns) + { + // return slots like args are 64-bit aligned + u16 returnSlot = numReturns * c_ioSlotCount; + u16 stackTop = GetStackTopIndex (o); + + for (u16 i = 0; i < numReturns; ++i) + { + u8 returnType = GetFuncTypeResultType (i_functionBlock->type, numReturns - 1 - i); + + u8 stackType = GetStackTypeFromTop (o, i); // using FromTop so that only dynamic items are checked + + if (IsStackPolymorphic (o) and stackType == c_m3Type_none) + stackType = returnType; + + _throwif (m3Err_typeMismatch, returnType != stackType); + + if (not IsStackPolymorphic (o)) + { + returnSlot -= c_ioSlotCount; +_ (CopyStackIndexToSlot (o, returnSlot, stackTop--)); + } + } + + if (not i_isBranch) + { + while (numReturns--) +_ (Pop (o)); + } + } + + _catch: return result; } @@ -931,7 +1076,7 @@ _ (PushConst (o, value, c_m3Type_i64)); m3log (compile, } -#if d_m3HasFloat +#if d_m3ImplementFloat M3Result Compile_Const_f32 (IM3Compilation o, m3opcode_t i_opcode) { M3Result result; @@ -958,14 +1103,13 @@ _ (PushConst (o, value.u, c_m3Type_f64)); } #endif -#ifdef d_m3CompileExtendedOpcode +#ifdef d_m3EnableExtendedOpcodes M3Result Compile_ExtendedOpcode (IM3Compilation o, m3opcode_t i_opcode) { - M3Result result; - - i32 value; + M3Result result = m3Err_none; +_try { u8 opcode; _ (Read_u8 (& opcode, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (FC: %" PRIi32 ")", get_indention_string (o), opcode); @@ -973,73 +1117,76 @@ _ (Read_u8 (& opcode, & o->wasm, o->wasmEnd)); m3log (compile, d_i //printf("Extended opcode: 0x%x\n", i_opcode); - M3Compiler compiler = GetOpInfo(i_opcode)->compiler; + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); - if (compiler) - result = (* compiler) (o, i_opcode); - else - result = m3Err_noCompiler; + M3Compiler compiler = opInfo->compiler; + _throwif (m3Err_noCompiler, not compiler); + +_ ((* compiler) (o, i_opcode)); o->previousOpcode = i_opcode; - _catch: return result; + } _catch: return result; } - #endif + M3Result Compile_Return (IM3Compilation o, m3opcode_t i_opcode) { - M3Result result; + M3Result result = m3Err_none; - if (GetFunctionNumReturns (o->function)) + if (not IsStackPolymorphic (o)) { -_ (ReturnStackTop (o)); -_ (Pop (o)); - } + IM3CompilationScope functionScope; +_ (GetBlockScope (o, & functionScope, o->block.depth)); -_ (EmitOp (o, op_Return)); +_ (ReturnValues (o, functionScope, true)); - o->block.isPolymorphic = true; +_ (EmitOp (o, op_Return)); + +_ (SetStackPolymorphic (o)); + } _catch: return result; } -M3Result Compile_End (IM3Compilation o, m3opcode_t i_opcode) +M3Result ValidateBlockEnd (IM3Compilation o) { M3Result result = m3Err_none; +/* + u16 numResults = GetFuncTypeNumResults (o->block.type); + u16 blockHeight = GetNumBlockValuesOnStack (o); - // function end: - if (o->block.depth == 0) + if (IsStackPolymorphic (o)) + { + } + else { - u8 valueType = GetSingleRetType(o->block.type); + } - if (valueType) - { - // if there are branches to the function end, then their values are in a register - // if the block happens to have its top in a register too, then we can patch the branch - // to here. Otherwise, an ReturnStackTop is appended to the end of the function (at B) and - // branches patched there. - if (IsStackTopInRegister (o)) - PatchBranches (o); + _catch: */ return result; +} -_ (ReturnStackTop (o)); - } - else PatchBranches (o); // for no return type, branch to op_End -_ (EmitOp (o, op_Return)); +M3Result Compile_End (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; //dump_type_stack (o); -_ (UnwindBlockStack (o)); + // function end: + if (o->block.depth == 0) + { + ValidateBlockEnd (o); - // B: move register to return slot for branchehs - if (valueType) +// if (not IsStackPolymorphic (o)) { - if (PatchBranches (o)) + if (o->function) { -_ (PushRegister (o, valueType)); - ReturnStackTop (o); -_ (EmitOp (o, op_Return)); +_ (ReturnValues (o, & o->block, false)); } + +_ (EmitOp (o, op_Return)); } } @@ -1063,7 +1210,7 @@ _ (ReadLEB_u32 (& localIndex, & o->wasm, o->wasmEnd)); // printf _ (FindReferencedLocalWithinCurrentBlock (o, & preserveSlot, localSlot)); // preserve will be different than local, if referenced if (preserveSlot == localSlot) -_ (CopyTopSlot (o, localSlot)) +_ (CopyStackTopToSlot (o, localSlot)) else _ (PreservedCopyTopSlot (o, localSlot, preserveSlot)) @@ -1088,7 +1235,7 @@ _ (ReadLEB_u32 (& localIndex, & o->wasm, o->wasmEnd)); if (localIndex >= GetFunctionNumArgsAndLocals (o->function)) _throw ("local index out of bounds"); - u8 type = GetStackBottomType (o, localIndex); + u8 type = GetStackTypeFromBottom (o, localIndex); u16 slot = GetSlotForStackIndex (o, localIndex); _ (Push (o, type, slot)); @@ -1129,11 +1276,11 @@ _ (EmitOp (o, op)); EmitPointer (o, & i_global->intValue); if (IsStackTopInSlot (o)) - EmitSlotOffset (o, GetStackTopSlotIndex (o)); + EmitSlotOffset (o, GetStackTopSlotNumber (o)); _ (Pop (o)); } - else result = m3Err_settingImmutableGlobal; + else _throw (m3Err_settingImmutableGlobal); _catch: return result; } @@ -1152,11 +1299,29 @@ _ (ReadLEB_u32 (& globalIndex, & o->wasm, o->wasmEnd)); { M3Global * global = & o->module->globals [globalIndex]; - result = (i_opcode == 0x23) ? Compile_GetGlobal (o, global) : Compile_SetGlobal (o, global); +_ ((i_opcode == 0x23) ? Compile_GetGlobal (o, global) : Compile_SetGlobal (o, global)); } - else result = ErrorCompile (m3Err_globalMemoryNotAllocated, o, "module '%s' is missing global memory", o->module->name); + else _throw (ErrorCompile (m3Err_globalMemoryNotAllocated, o, "module '%s' is missing global memory", o->module->name)); } - else result = m3Err_globaIndexOutOfBounds; + else _throw (m3Err_globaIndexOutOfBounds); + + _catch: return result; +} + + +void EmitPatchingBranchPointer (IM3Compilation o, IM3CompilationScope i_scope) +{ + pc_t patch = EmitPointer (o, i_scope->patches); m3log (compile, "branch patch required at: %p", patch); + i_scope->patches = patch; +} + + +M3Result EmitPatchingBranch (IM3Compilation o, IM3CompilationScope i_scope) +{ + M3Result result = m3Err_none; + +_ (EmitOp (o, op_Branch)); + EmitPatchingBranchPointer (o, i_scope); _catch: return result; } @@ -1167,92 +1332,109 @@ M3Result Compile_Branch (IM3Compilation o, m3opcode_t i_opcode) M3Result result; u32 depth; -_ (ReadLEB_u32 (& depth, & o->wasm, o->wasmEnd)); // printf ("depth: %d \n", depth); +_ (ReadLEB_u32 (& depth, & o->wasm, o->wasmEnd)); IM3CompilationScope scope; _ (GetBlockScope (o, & scope, depth)); - IM3Operation op; - // branch target is a loop (continue) if (scope->opcode == c_waOp_loop) { if (i_opcode == c_waOp_branchIf) { -_ (MoveStackTopToRegister (o)); - op = op_ContinueLoopIf; -_ (Pop (o)); + if (GetFuncTypeNumParams (scope->type)) + { + IM3Operation op = IsStackTopInRegister (o) ? op_BranchIfPrologue_r : op_BranchIfPrologue_s; + +_ (EmitOp (o, op)); +_ (EmitSlotNumOfStackTopAndPop (o)); + + pc_t * jumpTo = (pc_t *) ReservePointer (o); + +_ (ResolveBlockResults (o, scope, /* isBranch: */ true)); + +_ (EmitOp (o, op_ContinueLoop)); + EmitPointer (o, scope->pc); + + * jumpTo = GetPC (o); + } + else + { + // move the condition to a register +_ (CopyStackTopToRegister (o, false)); +_ (PopType (o, c_m3Type_i32)); + +_ (EmitOp (o, op_ContinueLoopIf)); + EmitPointer (o, scope->pc); + } + +// dump_type_stack(o); } - else + else // is c_waOp_branch { - op = op_ContinueLoop; + _ (EmitOp (o, op_ContinueLoop)); + EmitPointer (o, scope->pc); o->block.isPolymorphic = true; } - -_ (EmitOp (o, op)); - EmitPointer (o, scope->pc); } - else + else // forward branch { - u16 conditionSlot = c_slotUnused; - u16 valueSlot = c_slotUnused; - u8 valueType = GetSingleRetType(scope->type); + pc_t * jumpTo = NULL; + + bool isReturn = (scope->depth == 0); + bool targetHasResults = GetFuncTypeNumResults (scope->type); if (i_opcode == c_waOp_branchIf) { - bool conditionInRegister = IsStackTopInRegister (o); - - op = conditionInRegister ? op_BranchIf_r : op_BranchIf_s; + if (targetHasResults or isReturn) + { + IM3Operation op = IsStackTopInRegister (o) ? op_BranchIfPrologue_r : op_BranchIfPrologue_s; - conditionSlot = GetStackTopSlotIndex (o); -_ (Pop (o)); // condition + _ (EmitOp (o, op)); + _ (EmitSlotNumOfStackTopAndPop (o)); // condition - // no Pop of values here 'cause the next statement in block can consume this value - if (IsFpType (valueType)) - { -_ (MoveStackTopToRegister (o)); + // this is continuation point, if the branch isn't taken + jumpTo = (pc_t *) ReservePointer (o); } - else if (IsIntType (valueType)) + else { - // need to deal with int value in slot. it needs to be copied to _r0 during the branch - if (IsStackTopInSlot (o)) - { - valueSlot = GetStackTopSlotIndex (o); + IM3Operation op = IsStackTopInRegister (o) ? op_BranchIf_r : op_BranchIf_s; - op = c_ifOps [valueType - c_m3Type_i32] [conditionInRegister]; - } + _ (EmitOp (o, op)); + _ (EmitSlotNumOfStackTopAndPop (o)); // condition + + EmitPatchingBranchPointer (o, scope); + goto _catch; } } - else - { - op = op_Branch; - if (valueType != c_m3Type_none and not IsStackPolymorphic (o)) -_ (MoveStackTopToRegister (o)); - - o->block.isPolymorphic = true; + if (not IsStackPolymorphic (o)) + { + if (isReturn) + { +_ (ReturnValues (o, scope, true)); +_ (EmitOp (o, op_Return)); + } + else + { +_ (ResolveBlockResults (o, scope, true)); +_ (EmitPatchingBranch (o, scope)); + } } -_ (EmitOp (o, op)); - if (IsValidSlot (conditionSlot)) - EmitSlotOffset (o, conditionSlot); - if (IsValidSlot (valueSlot)) - EmitSlotOffset (o, valueSlot); - - IM3BranchPatch patch; -_ (AcquirePatch (o, & patch)); + if (jumpTo) + { + * jumpTo = GetPC (o); + } - patch->location = (pc_t *) ReservePointer (o); - patch->next = scope->patches; - scope->patches = patch; + if (i_opcode == c_waOp_branch) +_ (SetStackPolymorphic (o)); } _catch: return result; } - - M3Result Compile_BranchTable (IM3Compilation o, m3opcode_t i_opcode) { M3Result result; @@ -1262,14 +1444,12 @@ _try { _ (ReadLEB_u32 (& targetCount, & o->wasm, o->wasmEnd)); _ (PreserveRegisterIfOccupied (o, c_m3Type_i64)); // move branch operand to a slot - u16 slot = GetStackTopSlotIndex (o); + u16 slot = GetStackTopSlotNumber (o); _ (Pop (o)); // OPTZ: according to spec: "forward branches that target a control instruction with a non-empty // result type consume matching operands first and push them back on the operand stack after unwinding" // So, this move-to-reg is only necessary if the target scopes have a type. - if (GetNumBlockValues (o) > 0) -_ (MoveStackTopToRegister (o)); u32 numCodeLines = targetCount + 4; // 3 => IM3Operation + slot + target_count + default_target _ (EnsureCodePageNumLines (o, numCodeLines)); @@ -1289,51 +1469,52 @@ _ (ReadLEB_u32 (& target, & o->wasm, o->wasmEnd)); IM3CompilationScope scope; _ (GetBlockScope (o, & scope, target)); - if (scope->opcode == c_waOp_loop) - { - // create a ContinueLoop operation on a fresh page -_ (AcquireCompilationCodePage (o, & continueOpPage)); + // TODO: don't need codepage rigmarole for + // no-param forward-branch targets + +_ (AcquireCompilationCodePage (o, & continueOpPage)); - pc_t startPC = GetPagePC (continueOpPage); - EmitPointer (o, startPC); + pc_t startPC = GetPagePC (continueOpPage); + IM3CodePage savedPage = o->page; + o->page = continueOpPage; - IM3CodePage savedPage = o->page; - o->page = continueOpPage; + if (scope->opcode == c_waOp_loop) + { +_ (ResolveBlockResults (o, scope, true)); _ (EmitOp (o, op_ContinueLoop)); EmitPointer (o, scope->pc); - - ReleaseCompilationCodePage (o); // FIX: continueOpPage can get lost if thrown - o->page = savedPage; } else { - IM3BranchPatch patch; -_ (AcquirePatch (o, & patch)); + // TODO: this could be fused with equivalent targets + if (not IsStackPolymorphic (o)) + { + if (scope->depth == 0) + { +_ (ReturnValues (o, scope, true)); +_ (EmitOp (o, op_Return)); + } + else + { +_ (ResolveBlockResults (o, scope, true)); - patch->location = (pc_t *) ReservePointer (o); - patch->next = scope->patches; - scope->patches = patch; +_ (EmitPatchingBranch (o, scope)); + } + } } - } - o->block.isPolymorphic = true; + ReleaseCompilationCodePage (o); // FIX: continueOpPage can get lost if thrown + o->page = savedPage; + EmitPointer (o, startPC); } - _catch: - - return result; -} +_ (SetStackPolymorphic (o)); + } -void AlignSlotIndexToType (u16 * io_slotIndex, u8 i_type) -{ - // align 64-bit words to even slots - u16 numSlots = GetTypeNumSlots (i_type); - - u16 mask = numSlots - 1; - * io_slotIndex = (* io_slotIndex + mask) & ~mask; + _catch: return result; } @@ -1351,7 +1532,7 @@ _try { topSlot = M3_MAX (1, topSlot); // stack frame is 64-bit aligned - AlignSlotIndexToType (& topSlot, c_m3Type_i64); + AlignSlotToType (& topSlot, c_m3Type_i64); * o_stackOffset = topSlot; @@ -1359,23 +1540,26 @@ _try { if (i_isIndirect) _ (Pop (o)); - u32 numArgs = i_type->numArgs; + u16 numArgs = GetFuncTypeNumParams (i_type); + u16 numRets = GetFuncTypeNumResults (i_type); - u32 slotsPerArg = sizeof (u64) / sizeof (m3slot_t); - - // args are 64-bit aligned - u16 argTop = topSlot + numArgs * slotsPerArg; + u16 argTop = topSlot + (numArgs + numRets) * c_ioSlotCount; while (numArgs--) { -_ (CopyTopSlot (o, argTop -= slotsPerArg)); +_ (CopyStackTopToSlot (o, argTop -= c_ioSlotCount)); _ (Pop (o)); } - if (i_type->numRets) + u16 i = 0; + while (numRets--) { - MarkSlotAllocated (o, topSlot); -_ (Push (o, GetSingleRetType(i_type), topSlot)); + u8 type = GetFuncTypeResultType (i_type, i++); + +_ (Push (o, type, topSlot)); + MarkSlotsAllocatedByType (o, topSlot, type); + + topSlot += c_ioSlotCount; } } _catch: return result; @@ -1393,12 +1577,10 @@ _ (ReadLEB_u32 (& functionIndex, & o->wasm, o->wasmEnd)); IM3Function function = Module_GetFunction (o->module, functionIndex); if (function) - { m3log (compile, d_indent " (func= '%s'; args= %d)", - get_indention_string (o), m3_GetFunctionName (function), function->funcType->numArgs); + { m3log (compile, d_indent " (func= [%d] '%s'; args= %d)", + get_indention_string (o), functionIndex, m3_GetFunctionName (function), function->funcType->numArgs); if (function->module) { - // OPTZ: could avoid arg copy when args are already sequential and at top - u16 slotTop; _ (CompileCallArgsAndReturn (o, & slotTop, function->funcType, false)); @@ -1411,7 +1593,7 @@ _ (CompileCallArgsAndReturn (o, & slotTop, function->funcType, false)) operand = function->compiled; } else - { d_m3Assert (function->module); // not linked + { op = op_Compile; operand = function; } @@ -1422,10 +1604,10 @@ _ (EmitOp (o, op)); } else { - result = ErrorCompile (m3Err_functionImportMissing, o, "'%s.%s'", GetFunctionImportModuleName (function), m3_GetFunctionName (function)); + _throw (ErrorCompile (m3Err_functionImportMissing, o, "'%s.%s'", GetFunctionImportModuleName (function), m3_GetFunctionName (function))); } } - else result = m3Err_functionLookupFailed; + else _throw (m3Err_functionLookupFailed); } _catch: return result; } @@ -1442,12 +1624,12 @@ _ (ReadLEB_u32 (& typeIndex, & o->wasm, o->wasmEnd)); i8 reserved; _ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); - _throwif ("function type index out of range", typeIndex >= o->module->numFuncTypes); + _throwif ("function call type index out of range", typeIndex >= o->module->numFuncTypes); if (IsStackTopInRegister (o)) _ (PreserveRegisterIfOccupied (o, c_m3Type_i32)); - u16 tableIndexSlot = GetStackTopSlotIndex (o); + u16 tableIndexSlot = GetStackTopSlotNumber (o); u16 execTop; IM3FuncType type = o->module->funcTypes [typeIndex]; @@ -1464,14 +1646,16 @@ _ (EmitOp (o, op_CallIndirect)); } -M3Result Compile_Memory_Current (IM3Compilation o, m3opcode_t i_opcode) +M3Result Compile_Memory_Size (IM3Compilation o, m3opcode_t i_opcode) { M3Result result; i8 reserved; _ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); -_ (EmitOp (o, op_MemCurrent)); +_ (PreserveRegisterIfOccupied (o, c_m3Type_i32)); + +_ (EmitOp (o, op_MemSize)); _ (PushRegister (o, c_m3Type_i32)); @@ -1486,8 +1670,8 @@ M3Result Compile_Memory_Grow (IM3Compilation o, m3opcode_t i_opcode) i8 reserved; _ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); -_ (MoveStackTopToRegister (o)); // a stack flavor of Grow would get rid of this -_ (Pop (o)); +_ (CopyStackTopToRegister (o, false)); +_ (PopType (o, c_m3Type_i32)); _ (EmitOp (o, op_MemGrow)); @@ -1496,10 +1680,38 @@ _ (PushRegister (o, c_m3Type_i32)); _catch: return result; } + +M3Result Compile_Memory_CopyFill (IM3Compilation o, m3opcode_t i_opcode) +{ + M3Result result = m3Err_none; + + i8 reserved; +_ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); + + IM3Operation op; + if (i_opcode == c_waOp_memoryCopy) + { +_ (ReadLEB_i7 (& reserved, & o->wasm, o->wasmEnd)); + op = op_MemCopy; + } + else op = op_MemFill; + +_ (CopyStackTopToRegister (o, false)); + +_ (EmitOp (o, op)); +_ (PopType (o, c_m3Type_i32)); +_ (EmitSlotNumOfStackTopAndPop (o)); +_ (EmitSlotNumOfStackTopAndPop (o)); + + _catch: return result; +} + + static M3Result ReadBlockType (IM3Compilation o, IM3FuncType * o_blockType) { M3Result result; + i64 type; _ (ReadLebSigned (& type, 33, & o->wasm, o->wasmEnd)); @@ -1518,32 +1730,29 @@ _ (NormalizeType (&valueType, type)); m3log } -// This preemptively preserves args and locals on the stack that might be written-to in the subsequent block -// (versus the COW strategy that happens in SetLocal within a block). Initially, I thought I'd have to be clever and -// retroactively insert preservation code to avoid impacting general performance, but this compilation pattern doesn't -// really occur in compiled Wasm code, so PreserveArgsAndLocals generally does nothing. Still waiting on a real-world case! M3Result PreserveArgsAndLocals (IM3Compilation o) { M3Result result = m3Err_none; - if (o->stackIndex > o->firstDynamicStackIndex) + if (o->stackIndex > o->stackFirstDynamicIndex) { u32 numArgsAndLocals = GetFunctionNumArgsAndLocals (o->function); for (u32 i = 0; i < numArgsAndLocals; ++i) { - u16 localSlot = GetSlotForStackIndex (o, i); - u16 preservedSlotIndex; -_ (FindReferencedLocalWithinCurrentBlock (o, & preservedSlotIndex, localSlot)); + u16 slot = GetSlotForStackIndex (o, i); + + u16 preservedSlotNumber; +_ (FindReferencedLocalWithinCurrentBlock (o, & preservedSlotNumber, slot)); - if (preservedSlotIndex != localSlot) + if (preservedSlotNumber != slot) { - u8 type = GetStackBottomType (o, i); + u8 type = GetStackTypeFromBottom (o, i); d_m3Assert (type != c_m3Type_none) IM3Operation op = Is64BitType (type) ? op_CopySlot_64 : op_CopySlot_32; EmitOp (o, op); - EmitSlotOffset (o, preservedSlotIndex); - EmitSlotOffset (o, localSlot); + EmitSlotOffset (o, preservedSlotNumber); + EmitSlotOffset (o, slot); } } } @@ -1557,6 +1766,7 @@ M3Result Compile_LoopOrBlock (IM3Compilation o, m3opcode_t i_opcode) { M3Result result; + // TODO: these shouldn't be necessary for non-loop blocks? _ (PreserveRegisters (o)); _ (PreserveArgsAndLocals (o)); @@ -1564,7 +1774,38 @@ _ (PreserveArgsAndLocals (o)); _ (ReadBlockType (o, & blockType)); if (i_opcode == c_waOp_loop) + { + u16 numParams = GetFuncTypeNumParams (blockType); + if (numParams) + { + // instantiate constants + u16 numValues = GetNumBlockValuesOnStack (o); // CompileBlock enforces this at comptime + d_m3Assert (numValues >= numParams); + if (numValues >= numParams) + { + u16 stackTop = GetStackTopIndex (o) + 1; + + for (u16 i = stackTop - numParams; i < stackTop; ++i) + { + u16 slot = GetSlotForStackIndex (o, i); + u8 type = GetStackTypeFromBottom (o, i); + + if (IsConstantSlot (o, slot)) + { + u16 newSlot; +_ (AllocateSlots (o, & newSlot, type)); +_ (CopyStackIndexToSlot (o, newSlot, i)); + o->wasmStack [i] = newSlot; + } + } + } + } + _ (EmitOp (o, op_Loop)); + } + else + { + } _ (CompileBlock (o, blockType, i_opcode)); @@ -1577,6 +1818,7 @@ M3Result CompileElseBlock (IM3Compilation o, pc_t * o_startPC, IM3FuncType i_b M3Result result; _try { + IM3CodePage elsePage; _ (AcquireCompilationCodePage (o, & elsePage)); @@ -1603,34 +1845,55 @@ M3Result Compile_If (IM3Compilation o, m3opcode_t i_opcode) { M3Result result; + /* [ op_If ] + [ ] ----> [ ..else.. ] + [ ..if.. ] [ ..block.. ] + [ ..block.. ] [ op_Branch ] + [ end ] <----- [ ] */ + _try { + _ (PreserveNonTopRegisters (o)); _ (PreserveArgsAndLocals (o)); IM3Operation op = IsStackTopInRegister (o) ? op_If_r : op_If_s; _ (EmitOp (o, op)); -_ (EmitTopSlotAndPop (o)); - - i32 stackIndex = o->stackIndex; +_ (EmitSlotNumOfStackTopAndPop (o)); pc_t * pc = (pc_t *) ReservePointer (o); IM3FuncType blockType; _ (ReadBlockType (o, & blockType)); +// dump_type_stack (o); + + u16 stackIndex = o->stackIndex; + _ (CompileBlock (o, blockType, i_opcode)); if (o->previousOpcode == c_waOp_else) { - if (blockType and o->stackIndex > stackIndex) + o->stackIndex = stackIndex; +_ (CompileElseBlock (o, pc, blockType)); + } + else + { + // if block produces values and there isn't a defined else + // case, then we need to make one up so that the pass-through + // results end up in the right place + if (GetFuncTypeNumResults (blockType)) { -_ (Pop (o)); - } + // rewind to the if's end to create a fake else block + o->wasm--; + o->stackIndex = stackIndex; -_ (CompileElseBlock (o, pc, blockType)); +// dump_type_stack (o); + +_ (CompileElseBlock (o, pc, blockType)); + } + else * pc = GetPC (o); } - else * pc = GetPC (o); } _catch: return result; } @@ -1642,13 +1905,13 @@ M3Result Compile_Select (IM3Compilation o, m3opcode_t i_opcode) u16 slots [3] = { c_slotUnused, c_slotUnused, c_slotUnused }; - u8 type = GetStackTopTypeAtOffset (o, 1); // get type of selection + u8 type = GetStackTypeFromTop (o, 1); // get type of selection IM3Operation op = NULL; if (IsFpType (type)) { -#if d_m3HasFloat +# if d_m3HasFloat // not consuming a fp reg, so preserve if (not IsStackTopMinus1InRegister (o) and not IsStackTopMinus2InRegister (o)) @@ -1657,7 +1920,7 @@ _ (PreserveRegisterIfOccupied (o, type)); } bool selectorInReg = IsStackTopInRegister (o); - slots [0] = GetStackTopSlotIndex (o); + slots [0] = GetStackTopSlotNumber (o); _ (Pop (o)); u32 opIndex = 0; @@ -1667,15 +1930,15 @@ _ (Pop (o)); if (IsStackTopInRegister (o)) opIndex = i; else - slots [i] = GetStackTopSlotIndex (o); + slots [i] = GetStackTopSlotNumber (o); _ (Pop (o)); } op = c_fpSelectOps [type - c_m3Type_f32] [selectorInReg] [opIndex]; -#else +# else _throw (m3Err_unknownOpcode); -#endif +# endif } else if (IsIntType (type)) { @@ -1694,7 +1957,7 @@ _ (PreserveRegisterIfOccupied (o, type)); if (IsStackTopInRegister (o)) opIndex = i; else - slots [i] = GetStackTopSlotIndex (o); + slots [i] = GetStackTopSlotNumber (o); _ (Pop (o)); } @@ -1736,22 +1999,23 @@ M3Result Compile_Unreachable (IM3Compilation o, m3opcode_t i_opcode) _ (AddTrapRecord (o)); _ (EmitOp (o, op_Unreachable)); - o->block.isPolymorphic = true; +_ (SetStackPolymorphic (o)); _catch: return result; } -// TODO OPTZ: currently all stack slot indicies take up a full word, but +// OPTZ: currently all stack slot indices take up a full word, but // dual stack source operands could be packed together M3Result Compile_Operator (IM3Compilation o, m3opcode_t i_opcode) { M3Result result; - const M3OpInfo * op = GetOpInfo(i_opcode); + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); - IM3Operation operation; + IM3Operation op; // This preserve is for for FP compare operations. // either need additional slot destination operations or the @@ -1759,67 +2023,68 @@ M3Result Compile_Operator (IM3Compilation o, m3opcode_t i_opcode) // moving out the way might be the optimal solution most often? // otherwise, the _r0 reg can get buried down in the stack // and be idle & wasted for a moment. - if (IsFpType (GetStackTopType (o)) and IsIntType (op->type)) + if (IsFpType (GetStackTopType (o)) and IsIntType (opInfo->type)) { -_ (PreserveRegisterIfOccupied (o, op->type)); +_ (PreserveRegisterIfOccupied (o, opInfo->type)); } - if (op->stackOffset == 0) + if (opInfo->stackOffset == 0) { if (IsStackTopInRegister (o)) { - operation = op->operations [0]; // _s + op = opInfo->operations [0]; // _s } else { -_ (PreserveRegisterIfOccupied (o, op->type)); - operation = op->operations [1]; // _r +_ (PreserveRegisterIfOccupied (o, opInfo->type)); + op = opInfo->operations [1]; // _r } } else { if (IsStackTopInRegister (o)) { - operation = op->operations [0]; // _rs + op = opInfo->operations [0]; // _rs if (IsStackTopMinus1InRegister (o)) { d_m3Assert (i_opcode == 0x38 or i_opcode == 0x39); - operation = op->operations [3]; // _rr for fp.store + op = opInfo->operations [3]; // _rr for fp.store } } else if (IsStackTopMinus1InRegister (o)) { - operation = op->operations [1]; // _sr + op = opInfo->operations [1]; // _sr - if (not operation) // must be commutative, then - operation = op->operations [0]; + if (not op) // must be commutative, then + op = opInfo->operations [0]; } else { -_ (PreserveRegisterIfOccupied (o, op->type)); // _ss - operation = op->operations [2]; +_ (PreserveRegisterIfOccupied (o, opInfo->type)); // _ss + op = opInfo->operations [2]; } } - if (operation) + if (op) { -_ (EmitOp (o, operation)); +_ (EmitOp (o, op)); -_ (EmitTopSlotAndPop (o)); +_ (EmitSlotNumOfStackTopAndPop (o)); - if (op->stackOffset < 0) -_ (EmitTopSlotAndPop (o)); + if (opInfo->stackOffset < 0) +_ (EmitSlotNumOfStackTopAndPop (o)); - if (op->type != c_m3Type_none) -_ (PushRegister (o, op->type)); + if (opInfo->type != c_m3Type_none) +_ (PushRegister (o, opInfo->type)); } else { # ifdef DEBUG - result = ErrorCompile ("no operation found for opcode", o, "'%s'", op->name); + result = ErrorCompile ("no operation found for opcode", o, "'%s'", opInfo->name); # else - result = ErrorCompile ("no operation found for opcode", o, ""); + result = ErrorCompile ("no operation found for opcode", o, "%x", i_opcode); # endif + _throw (result); } _catch: return result; @@ -1830,7 +2095,9 @@ M3Result Compile_Convert (IM3Compilation o, m3opcode_t i_opcode) { M3Result result = m3Err_none; - const M3OpInfo * opInfo = GetOpInfo(i_opcode); +_try { + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); bool destInSlot = IsRegisterTypeAllocated (o, opInfo->type); bool sourceInSlot = IsStackTopInSlot (o); @@ -1838,13 +2105,14 @@ M3Result Compile_Convert (IM3Compilation o, m3opcode_t i_opcode) IM3Operation op = opInfo->operations [destInSlot * 2 + sourceInSlot]; _ (EmitOp (o, op)); -_ (EmitTopSlotAndPop (o)); +_ (EmitSlotNumOfStackTopAndPop (o)); if (destInSlot) _ (PushAllocatedSlotAndEmit (o, opInfo->type)) else _ (PushRegister (o, opInfo->type)) +} _catch: return result; } @@ -1859,9 +2127,10 @@ _try { _ (ReadLEB_u32 (& alignHint, & o->wasm, o->wasmEnd)); _ (ReadLEB_u32 (& memoryOffset, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (offset = %d)", get_indention_string (o), memoryOffset); - const M3OpInfo * op = GetOpInfo(i_opcode); + IM3OpInfo opInfo = GetOpInfo (i_opcode); + _throwif (m3Err_unknownOpcode, not opInfo); - if (IsFpType (op->type)) + if (IsFpType (opInfo->type)) _ (PreserveRegisterIfOccupied (o, c_m3Type_f64)); _ (Compile_Operator (o, i_opcode)); @@ -1950,8 +2219,8 @@ const M3OpInfo c_operations [] = M3OP( "i64.store16", -2, none, d_binOpList (i64, Store_i16), Compile_Load_Store ), // 0x3d M3OP( "i64.store32", -2, none, d_binOpList (i64, Store_i32), Compile_Load_Store ), // 0x3e - M3OP( "memory.current", 1, i_32, d_logOp (MemCurrent), Compile_Memory_Current ), // 0x3f - M3OP( "memory.grow", 1, i_32, d_logOp (MemGrow), Compile_Memory_Grow ), // 0x40 + M3OP( "memory.size", 1, i_32, d_logOp (MemSize), Compile_Memory_Size ), // 0x3f + M3OP( "memory.grow", 1, i_32, d_logOp (MemGrow), Compile_Memory_Grow ), // 0x40 M3OP( "i32.const", 1, i_32, d_logOp (Const32), Compile_Const_i32 ), // 0x41 M3OP( "i64.const", 1, i_64, d_logOp (Const64), Compile_Const_i64 ), // 0x42 @@ -2107,37 +2376,45 @@ const M3OpInfo c_operations [] = M3OP( "i64.extend16_s", 0, i_64, d_unaryOpList (i64, Extend16_s), NULL ), // 0xc3 M3OP( "i64.extend32_s", 0, i_64, d_unaryOpList (i64, Extend32_s), NULL ), // 0xc4 -# ifdef DEBUG // for codepage logging: +# ifdef DEBUG // for codepage logging. the order doesn't matter: # define d_m3DebugOp(OP) M3OP (#OP, 0, none, { op_##OP }) -# define d_m3DebugTypedOp(OP) M3OP (#OP, 0, none, { op_##OP##_i32, op_##OP##_i64, op_##OP##_f32, op_##OP##_f64, }) - d_m3DebugOp (Entry), d_m3DebugOp (Compile), d_m3DebugOp (End), +# if d_m3HasFloat +# define d_m3DebugTypedOp(OP) M3OP (#OP, 0, none, { op_##OP##_i32, op_##OP##_i64, op_##OP##_f32, op_##OP##_f64, }) +# else +# define d_m3DebugTypedOp(OP) M3OP (#OP, 0, none, { op_##OP##_i32, op_##OP##_i64 }) +# endif - d_m3DebugOp (Unsupported), - d_m3DebugOp (CallRawFunction), + d_m3DebugOp (Compile), d_m3DebugOp (Entry), d_m3DebugOp (End), + d_m3DebugOp (Unsupported), d_m3DebugOp (CallRawFunction), d_m3DebugOp (GetGlobal_s32), d_m3DebugOp (GetGlobal_s64), d_m3DebugOp (ContinueLoop), d_m3DebugOp (ContinueLoopIf), - d_m3DebugOp (CopySlot_32), d_m3DebugOp (PreserveCopySlot_32), - d_m3DebugOp (CopySlot_64), d_m3DebugOp (PreserveCopySlot_64), - - d_m3DebugOp (i32_BranchIf_rs), d_m3DebugOp (i32_BranchIf_ss), d_m3DebugOp (i64_BranchIf_rs), d_m3DebugOp (i64_BranchIf_ss), + d_m3DebugOp (CopySlot_32), d_m3DebugOp (PreserveCopySlot_32), d_m3DebugOp (If_s), d_m3DebugOp (BranchIfPrologue_s), + d_m3DebugOp (CopySlot_64), d_m3DebugOp (PreserveCopySlot_64), d_m3DebugOp (If_r), d_m3DebugOp (BranchIfPrologue_r), d_m3DebugOp (Select_i32_rss), d_m3DebugOp (Select_i32_srs), d_m3DebugOp (Select_i32_ssr), d_m3DebugOp (Select_i32_sss), d_m3DebugOp (Select_i64_rss), d_m3DebugOp (Select_i64_srs), d_m3DebugOp (Select_i64_ssr), d_m3DebugOp (Select_i64_sss), +# if d_m3HasFloat d_m3DebugOp (Select_f32_sss), d_m3DebugOp (Select_f32_srs), d_m3DebugOp (Select_f32_ssr), d_m3DebugOp (Select_f32_rss), d_m3DebugOp (Select_f32_rrs), d_m3DebugOp (Select_f32_rsr), d_m3DebugOp (Select_f64_sss), d_m3DebugOp (Select_f64_srs), d_m3DebugOp (Select_f64_ssr), d_m3DebugOp (Select_f64_rss), d_m3DebugOp (Select_f64_rrs), d_m3DebugOp (Select_f64_rsr), +# endif d_m3DebugTypedOp (SetGlobal), d_m3DebugOp (SetGlobal_s32), d_m3DebugOp (SetGlobal_s64), d_m3DebugTypedOp (SetRegister), d_m3DebugTypedOp (SetSlot), d_m3DebugTypedOp (PreserveSetSlot), # endif -# ifdef d_m3CompileExtendedOpcode +# ifdef d_m3EnableExtendedOpcodes +# ifdef DEBUG + d_m3DebugOp (MemFill), + d_m3DebugOp (MemCopy), +#endif + [0xFC] = M3OP( "0xFC", 0, c_m3Type_unknown, d_emptyOpList, Compile_ExtendedOpcode ), # endif @@ -2157,90 +2434,117 @@ const M3OpInfo c_operationsFC [] = M3OP_F( "i64.trunc_s:sat/f64",0, i_64, d_convertOpList (i64_TruncSat_f64), Compile_Convert ), // 0x06 M3OP_F( "i64.trunc_u:sat/f64",0, i_64, d_convertOpList (u64_TruncSat_f64), Compile_Convert ), // 0x07 + M3OP_RESERVED, M3OP_RESERVED, + + M3OP( "memory.copy", 0, none, d_emptyOpList, Compile_Memory_CopyFill ), // 0x0a + M3OP( "memory.fill", 0, none, d_emptyOpList, Compile_Memory_CopyFill ), // 0x0b + + # ifdef DEBUG M3OP( "termination", 0, c_m3Type_unknown ) // for find_operation_info # endif }; -M3Result Compile_BlockStatements (IM3Compilation o) + +IM3OpInfo GetOpInfo (m3opcode_t opcode) +{ + switch (opcode >> 8) { + case 0x00: + if (opcode < M3_COUNT_OF(c_operations)) { + return &c_operations[opcode]; + } + break; + case 0xFC: + opcode &= 0xFF; + if (opcode < M3_COUNT_OF(c_operationsFC)) { + return &c_operationsFC[opcode]; + } + break; + } + return NULL; +} + +M3Result CompileBlockStatements (IM3Compilation o) { M3Result result = m3Err_none; + bool validEnd = false; while (o->wasm < o->wasmEnd) { emit_stack_dump (o); + m3opcode_t opcode; o->lastOpcodeStart = o->wasm; - m3opcode_t opcode = * (o->wasm++); log_opcode (o, opcode); - -#ifndef d_m3CompileExtendedOpcode - if (UNLIKELY(opcode == 0xFC)) { - opcode = (opcode << 8) | (* (o->wasm++)); +_ (Read_opcode (& opcode, & o->wasm, o->wasmEnd)); log_opcode (o, opcode); + + // Restrict opcodes when evaluating expressions + if (not o->function) { + switch (opcode) { + case c_waOp_i32_const: case c_waOp_i64_const: + case c_waOp_f32_const: case c_waOp_f64_const: + case c_waOp_getGlobal: case c_waOp_end: + break; + default: + _throw(m3Err_restictedOpcode); + } } -#endif - IM3OpInfo opinfo = GetOpInfo(opcode); - _throwif(m3Err_unknownOpcode, opinfo == NULL); + IM3OpInfo opinfo = GetOpInfo (opcode); + + if (opinfo == NULL) + _throw (ErrorCompile (m3Err_unknownOpcode, o, "opcode '%x' not available", opcode)); if (opinfo->compiler) { - result = (* opinfo->compiler) (o, opcode); +_ ((* opinfo->compiler) (o, opcode)) } else { - result = Compile_Operator(o, opcode); +_ (Compile_Operator (o, opcode)); } - o->previousOpcode = opcode; // m3logif (stack, dump_type_stack (o)) - - if (o->stackIndex > d_m3MaxFunctionStackHeight) // TODO: is this only place to check? - result = m3Err_functionStackOverflow; + o->previousOpcode = opcode; - if (result) + if (opcode == c_waOp_else) + { + _throwif (m3Err_wasmMalformed, o->block.opcode != c_waOp_if); + validEnd = true; break; - - if (opcode == c_waOp_end or opcode == c_waOp_else) + } + else if (opcode == c_waOp_end) + { + validEnd = true; break; + } } + _throwif(m3Err_wasmMalformed, !(validEnd)); + _catch: return result; } -M3Result ValidateBlockEnd (IM3Compilation o, bool * o_copyStackTopToRegister) +M3Result PushBlockResults (IM3Compilation o) { M3Result result = m3Err_none; - * o_copyStackTopToRegister = false; + u16 numResults = GetFuncTypeNumResults (o->block.type); - u8 valueType = GetSingleRetType(o->block.type); - - if (valueType != c_m3Type_none) + for (u16 i = 0; i < numResults; ++i) { - if (IsStackPolymorphic (o)) + u8 type = GetFuncTypeResultType (o->block.type, i); + + if (i == numResults - 1 and IsFpType (type)) { -_ (UnwindBlockStack (o)); -_ (PushRegister (o, valueType)); +_ (PushRegister (o, type)); } else - { - i16 initStackIndex = o->block.initStackIndex; - - if (o->block.depth > 0 and initStackIndex != o->stackIndex) - { - if (o->stackIndex == initStackIndex + 1) - { - * o_copyStackTopToRegister = IsStackTopInSlot (o); - } - else _throw ("unexpected block stack offset"); - } - } +_ (PushAllocatedSlot (o, type)); } - else -_ (UnwindBlockStack (o)); _catch: return result; } -M3Result CompileBlock (IM3Compilation o, /*pc_t * o_startPC,*/ IM3FuncType i_blockType, u8 i_blockOpcode) + +M3Result CompileBlock (IM3Compilation o, IM3FuncType i_blockType, m3opcode_t i_blockOpcode) { - M3Result result; d_m3Assert (not IsRegisterAllocated (o, 0)); + M3Result result = m3Err_none; d_m3Assert (not IsRegisterAllocated (o, 0)); d_m3Assert (not IsRegisterAllocated (o, 1)); M3CompilationScope outerScope = o->block; M3CompilationScope * block = & o->block; @@ -2249,24 +2553,101 @@ M3Result CompileBlock (IM3Compilation o, /*pc_t * o_startPC,*/ IM3FuncType i_b block->pc = GetPagePC (o->page); block->patches = NULL; block->type = i_blockType; - block->initStackIndex = o->stackIndex; block->depth ++; -// block->loopDepth += (i_blockOpcode == c_waOp_loop); block->opcode = i_blockOpcode; -_ (Compile_BlockStatements (o)); + /* + The block stack frame is a little strange but for good reasons. Because blocks need to be restarted to + compile different pathways (if/else), the incoming params must be saved. The parameters are popped + and validated. But, then the stack top is readjusted so they aren't subsequently overwritten. + Next, the result are preallocated to find destination slots. But again these are immediately popped + (deallocated) and the stack top is readjusted to keep these records in pace. This allows branch instructions + to find their result landing pads. Finally, the params are copied from the "dead" records and pushed back + onto the stack as active stack items for the CompileBlockStatements () call. + + [ block ] + [ params ] + ------------------ + [ result ] <---- blockStackIndex + [ slots ] + ------------------ + [ saved param ] + [ records ] + <----- exitStackIndex + */ + +_try { + // validate and dealloc params ---------------------------- + + u16 stackIndex = o->stackIndex; + + u16 numParams = GetFuncTypeNumParams (i_blockType); + + if (i_blockOpcode != c_waOp_else) + { + for (u16 i = 0; i < numParams; ++i) + { + u8 type = GetFuncTypeParamType (i_blockType, numParams - 1 - i); +_ (PopType (o, type)); + } + } + else o->stackIndex -= numParams; + + u16 paramIndex = o->stackIndex; + block->exitStackIndex = paramIndex; // consume the params at block exit + + // keep copies of param slots in the stack + o->stackIndex = stackIndex; + + // find slots for the results ---------------------------- + PushBlockResults (o); + + stackIndex = o->stackIndex; + + // dealloc but keep record of the result slots in the stack + u16 numResults = GetFuncTypeNumResults (i_blockType); + while (numResults--) + Pop (o); + + block->blockStackIndex = o->stackIndex = stackIndex; + + // push the params back onto the stack ------------------- + for (u16 i = 0; i < numParams; ++i) + { + u8 type = GetFuncTypeParamType (i_blockType, i); + + u16 slot = GetSlotForStackIndex (o, paramIndex + i); + Push (o, type, slot); + + if (slot >= o->slotFirstDynamicIndex) + MarkSlotsAllocatedByType (o, slot, type); + } + + //-------------------------------------------------------- - bool moveStackTopToRegister; -_ (ValidateBlockEnd (o, & moveStackTopToRegister)); +_ (CompileBlockStatements (o)); - if (moveStackTopToRegister) -_ (MoveStackTopToRegister (o)); +_ (ValidateBlockEnd (o)); + + if (o->function) // skip for expressions + { + if (not IsStackPolymorphic (o)) +_ (ResolveBlockResults (o, & o->block, /* isBranch: */ false)); + +_ (UnwindBlockStack (o)) + + if (not ((i_blockOpcode == c_waOp_if and numResults) or o->previousOpcode == c_waOp_else)) + { + o->stackIndex = o->block.exitStackIndex; +_ (PushBlockResults (o)); + } + } PatchBranches (o); o->block = outerScope; - _catch: return result; +} _catch: return result; } @@ -2274,6 +2655,7 @@ M3Result CompileLocals (IM3Compilation o) { M3Result result; + u32 numLocals = 0; u32 numLocalBlocks; _ (ReadLEB_u32 (& numLocalBlocks, & o->wasm, o->wasmEnd)); @@ -2286,134 +2668,141 @@ _ (ReadLEB_u32 (& numLocalBlocks, & o->wasm, o->wasmEnd)); _ (ReadLEB_u32 (& varCount, & o->wasm, o->wasmEnd)); _ (ReadLEB_i7 (& waType, & o->wasm, o->wasmEnd)); _ (NormalizeType (& localType, waType)); - m3log (compile, "pushing locals. count: %d; type: %s", varCount, c_waTypes [localType]); + numLocals += varCount; m3log (compile, "pushing locals. count: %d; type: %s", varCount, c_waTypes [localType]); while (varCount--) _ (PushAllocatedSlot (o, localType)); } + if (o->function) + o->function->numLocals = numLocals; + _catch: return result; } -M3Result Compile_ReserveConstants (IM3Compilation o) +M3Result ReserveConstants (IM3Compilation o) { M3Result result = m3Err_none; // in the interest of speed, this blindly scans the Wasm code looking for any byte // that looks like an const opcode. - u32 numConstantSlots = 0; + u16 numConstantSlots = 0; bytes_t wa = o->wasm; while (wa < o->wasmEnd) { u8 code = * wa++; - if (code == 0x41 or code == 0x43) // i32, f32 + if (code == c_waOp_i32_const or code == c_waOp_f32_const) numConstantSlots += 1; - else if (code == 0x42 or code == 0x44) // i64, f64 + else if (code == c_waOp_i64_const or code == c_waOp_f64_const) numConstantSlots += GetTypeNumSlots (c_m3Type_i64); - } m3log (compile, "estimated constant slots: %d", numConstantSlots) + + if (numConstantSlots >= d_m3MaxConstantTableSize) + break; + } // if constants overflow their reserved stack space, the compiler simply emits op_Const // operations as needed. Compiled expressions (global inits) don't pass through this - // ReserveConstants function and thus always produce inline contants. - numConstantSlots = M3_MIN (numConstantSlots, d_m3MaxConstantTableSize); + // ReserveConstants function and thus always produce inline constants. + + AlignSlotToType (& numConstantSlots, c_m3Type_i64); m3log (compile, "reserved constant slots: %d", numConstantSlots); - o->firstDynamicSlotIndex = o->firstConstSlotIndex + numConstantSlots; + o->slotFirstDynamicIndex = o->slotFirstConstIndex + numConstantSlots; - if (o->firstDynamicSlotIndex >= d_m3MaxFunctionSlots) - result = m3Err_functionStackOverflow; + if (o->slotFirstDynamicIndex >= d_m3MaxFunctionSlots) + _throw (m3Err_functionStackOverflow); + _catch: return result; } -void SetupCompilation (IM3Compilation o) +M3Result CompileFunction (IM3Function io_function) { - IM3BranchPatch patches = o->releasedPatches; - memset (o, 0x0, sizeof (M3Compilation)); - o->releasedPatches = patches; -} - + M3Result result = m3Err_none; -M3Result Compile_Function (IM3Function io_function) -{ - IM3FuncType ft = io_function->funcType; + if (!io_function->wasm) return "function body is missing"; - M3Result result = m3Err_none; m3log (compile, "compiling: '%s'; wasm-size: %d; numArgs: %d; return: %s", - m3_GetFunctionName(io_function), (u32) (io_function->wasmEnd - io_function->wasm), GetFunctionNumArgs (io_function), c_waTypes [GetSingleRetType(ft)]); + IM3FuncType funcType = io_function->funcType; m3log (compile, "compiling: [%d] %s %s; wasm-size: %d", + io_function->index, m3_GetFunctionName (io_function), SPrintFuncTypeSignature (funcType), (u32) (io_function->wasmEnd - io_function->wasm)); IM3Runtime runtime = io_function->module->runtime; - IM3Compilation o = & runtime->compilation; - SetupCompilation (o); + IM3Compilation o = & runtime->compilation; d_m3Assert (d_m3MaxFunctionSlots >= d_m3MaxFunctionStackHeight * (d_m3Use32BitSlots + 1)) // need twice as many slots in 32-bit mode + memset (o, 0x0, sizeof (M3Compilation)); o->runtime = runtime; o->module = io_function->module; o->function = io_function; o->wasm = io_function->wasm; o->wasmEnd = io_function->wasmEnd; + o->block.type = funcType; _try { + // skip over code size. the end was already calculated during parse phase + u32 size; +_ (ReadLEB_u32 (& size, & o->wasm, o->wasmEnd)); d_m3Assert (size == (o->wasmEnd - o->wasm)) + _ (AcquireCompilationCodePage (o, & o->page)); pc_t pc = GetPagePC (o->page); - // push the arg types to the type stack - o->block.type = ft; + u16 numRetSlots = GetFunctionNumReturns (o->function) * c_ioSlotCount; + + for (u16 i = 0; i < numRetSlots; ++i) + MarkSlotAllocated (o, i); - // all args are 64-bit aligned - const u32 argSlotCount = sizeof (u64) / sizeof (m3slot_t); - u32 numArgs = GetFunctionNumArgs (o->function); - u32 numRets = GetFunctionNumReturns(o->function); + o->function->numRetSlots = o->slotFirstDynamicIndex = numRetSlots; - for (u32 i = 0; i < numArgs; ++i) + u16 numArgs = GetFunctionNumArgs (o->function); + + // push the arg types to the type stack + for (u16 i = 0; i < numArgs; ++i) { - u8 type = ft->types [numRets + i]; + u8 type = GetFunctionArgType (o->function, i); _ (PushAllocatedSlot (o, type)); - if (i < numArgs - 1) - { - // prevent allocator fill-in - o->firstDynamicSlotIndex += argSlotCount; - } - else - { - // final arg only allocates its natural width when using 32-bit slots - o->firstDynamicSlotIndex += GetTypeNumSlots (type); - } + // prevent allocator fill-in + o->slotFirstDynamicIndex += c_ioSlotCount; } - o->function->numArgSlots = o->firstLocalSlotIndex = o->firstDynamicSlotIndex; + o->slotMaxAllocatedIndexPlusOne = o->function->numRetAndArgSlots = o->slotFirstLocalIndex = o->slotFirstDynamicIndex; + _ (CompileLocals (o)); u16 maxSlot = GetMaxUsedSlotPlusOne (o); - o->function->numLocalBytes = (maxSlot - o->firstLocalSlotIndex) * sizeof (m3slot_t); + o->function->numLocalBytes = (maxSlot - o->slotFirstLocalIndex) * sizeof (m3slot_t); - o->firstConstSlotIndex = o->maxConstSlotIndex = maxSlot; + o->slotFirstConstIndex = o->slotMaxConstIndex = maxSlot; - // ReserveConstants initializes o->firstDynamicSlotIndex -_ (Compile_ReserveConstants (o)); + // ReserveConstants initializes o->firstDynamicSlotNumber +_ (ReserveConstants (o)); // start tracking the max stack used (Push() also updates this value) so that op_Entry can precisely detect stack overflow - o->function->maxStackSlots = o->maxAllocatedSlotPlusOne = o->firstDynamicSlotIndex; - - o->block.initStackIndex = o->firstDynamicStackIndex = o->stackIndex; m3log (compile, "start stack index: %d", (u32) o->firstDynamicStackIndex); + o->maxStackSlots = o->slotMaxAllocatedIndexPlusOne = o->slotFirstDynamicIndex; + o->block.blockStackIndex = o->stackFirstDynamicIndex = o->stackIndex; m3log (compile, "start stack index: %d", + (u32) o->stackFirstDynamicIndex); _ (EmitOp (o, op_Entry)); EmitPointer (o, io_function); -_ (Compile_BlockStatements (o)); +_ (CompileBlockStatements (o)); - io_function->compiled = pc; + // TODO: validate opcode sequences + _throwif(m3Err_wasmMalformed, o->previousOpcode != c_waOp_end); - u32 numConstantSlots = o->maxConstSlotIndex - o->firstConstSlotIndex; m3log (compile, "unique constant slots: %d; unused slots: %d", numConstantSlots, o->firstDynamicSlotIndex - o->maxConstSlotIndex); + io_function->compiled = pc; + io_function->maxStackSlots = o->maxStackSlots; + u16 numConstantSlots = o->slotMaxConstIndex - o->slotFirstConstIndex; m3log (compile, "unique constant slots: %d; unused slots: %d", + numConstantSlots, o->slotFirstDynamicIndex - o->slotMaxConstIndex); io_function->numConstantBytes = numConstantSlots * sizeof (m3slot_t); if (numConstantSlots) { -_ (m3CopyMem (& io_function->constants, o->constants, io_function->numConstantBytes)); + io_function->constants = m3_CopyMem (o->constants, io_function->numConstantBytes); + _throwifnull(io_function->constants); } } _catch: diff --git a/Sources/CWasm3/m3_core.c b/Sources/CWasm3/m3_core.c index 1b6bf84..e21faf3 100644 --- a/Sources/CWasm3/m3_core.c +++ b/Sources/CWasm3/m3_core.c @@ -37,7 +37,7 @@ static u8* fixedHeapLast = NULL; # define HEAP_ALIGN_PTR(P) #endif -M3Result m3_Malloc (void ** o_ptr, size_t i_size) +void * m3_Malloc (size_t i_size) { u8 * ptr = fixedHeapPtr; @@ -46,117 +46,105 @@ M3Result m3_Malloc (void ** o_ptr, size_t i_size) if (fixedHeapPtr >= fixedHeapEnd) { - * o_ptr = NULL; - - return m3Err_mallocFailed; + return NULL; } memset (ptr, 0x0, i_size); - * o_ptr = ptr; fixedHeapLast = ptr; //printf("== alloc %d => %p\n", i_size, ptr); - return m3Err_none; + return ptr; } -void m3_Free (void ** io_ptr) +void m3_FreeImpl (void * i_ptr) { - if (!io_ptr) return; - // Handle the last chunk - if (io_ptr == fixedHeapLast) { + if (i_ptr && i_ptr == fixedHeapLast) { fixedHeapPtr = fixedHeapLast; fixedHeapLast = NULL; //printf("== free %p\n", io_ptr); } else { //printf("== free %p [failed]\n", io_ptr); } - - * io_ptr = NULL; } -M3Result m3_Realloc (void ** io_ptr, size_t i_newSize, size_t i_oldSize) +void * m3_Realloc (void * i_ptr, size_t i_newSize, size_t i_oldSize) { //printf("== realloc %p => %d\n", io_ptr, i_newSize); - void * ptr = *io_ptr; - if (i_newSize == i_oldSize) return m3Err_none; + if (UNLIKELY(i_newSize == i_oldSize)) return i_ptr; + + void * newPtr; // Handle the last chunk - if (ptr && ptr == fixedHeapLast) { + if (i_ptr && i_ptr == fixedHeapLast) { fixedHeapPtr = fixedHeapLast + i_newSize; HEAP_ALIGN_PTR(fixedHeapPtr); - return m3Err_none; + if (fixedHeapPtr >= fixedHeapEnd) + { + return NULL; + } + newPtr = i_ptr; + } else { + newPtr = m3_Malloc(i_newSize); + if (!newPtr) { + return NULL; + } + if (i_ptr) { + memcpy(newPtr, i_ptr, i_oldSize); + } } - M3Result result = m3_Malloc(&ptr, i_newSize); - if (result) return result; - - if (*io_ptr) { - memcpy(ptr, *io_ptr, i_oldSize); + if (i_newSize > i_oldSize) { + memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); } - *io_ptr = ptr; - return m3Err_none; + return newPtr; } #else -M3Result m3_Malloc (void ** o_ptr, size_t i_size) +void * m3_Malloc (size_t i_size) { - M3Result result = m3Err_none; - void * ptr = calloc (i_size, 1); - if (not ptr) - result = m3Err_mallocFailed; - - * o_ptr = ptr; // printf("== alloc %d => %p\n", (u32) i_size, ptr); - return result; + return ptr; } -void m3_Free (void ** io_ptr) +void m3_FreeImpl (void * io_ptr) { // if (io_ptr) printf("== free %p\n", io_ptr); - free (* io_ptr); - * io_ptr = NULL; + free (io_ptr); } -M3Result m3_Realloc (void ** io_ptr, size_t i_newSize, size_t i_oldSize) +void * m3_Realloc (void * i_ptr, size_t i_newSize, size_t i_oldSize) { - M3Result result = m3Err_none; + if (UNLIKELY(i_newSize == i_oldSize)) return i_ptr; - if (i_newSize != i_oldSize) - { - void * newPtr = realloc (* io_ptr, i_newSize); + void * newPtr = realloc (i_ptr, i_newSize); - if (newPtr) - { - if (i_newSize > i_oldSize) - memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); - - * io_ptr = newPtr; + if (LIKELY(newPtr)) + { + if (i_newSize > i_oldSize) { + memset ((u8 *) newPtr + i_oldSize, 0x0, i_newSize - i_oldSize); } - else result = m3Err_mallocFailed; - -// printf("== realloc %p -> %p => %d\n", io_ptr, io_ptr, (u32) i_newSize); + return newPtr; } - - return result; + return NULL; } #endif -M3Result m3_CopyMem (void ** o_to, const void * i_from, size_t i_size) +void * m3_CopyMem (const void * i_from, size_t i_size) { - M3Result result = m3_Malloc(o_to, i_size); - if (!result) { - memcpy (*o_to, i_from, i_size); + void * ptr = m3_Malloc(i_size); + if (ptr) { + memcpy (ptr, i_from, i_size); } - return result; + return ptr; } //-------------------------------------------------------------------------------------------- @@ -275,7 +263,7 @@ M3Result Read_u32 (u32 * o_value, bytes_t * io_bytes, cbytes_t i_end) else return m3Err_wasmUnderrun; } -#if d_m3HasFloat || d_m3NoFloatDynamic +#if d_m3ImplementFloat M3Result Read_f64 (f64 * o_value, bytes_t * io_bytes, cbytes_t i_end) { @@ -317,7 +305,32 @@ M3Result Read_u8 (u8 * o_value, bytes_t * io_bytes, cbytes_t i_end) if (ptr < i_end) { * o_value = * ptr; - ptr += sizeof (u8); + * io_bytes = ptr + 1; + + return m3Err_none; + } + else return m3Err_wasmUnderrun; +} + +M3Result Read_opcode (m3opcode_t * o_value, bytes_t * io_bytes, cbytes_t i_end) +{ + const u8 * ptr = * io_bytes; + + if (ptr < i_end) + { + m3opcode_t opcode = * ptr++; + +#ifndef d_m3EnableExtendedOpcodes + if (UNLIKELY(opcode == 0xFC)) + { + if (ptr < i_end) + { + opcode = (opcode << 8) | (* ptr++); + } + else return m3Err_wasmUnderrun; + } +#endif + * o_value = opcode; * io_bytes = ptr; return m3Err_none; @@ -471,10 +484,9 @@ M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) if (end <= i_end) { - char * utf8; - result = m3_Malloc ((void **) & utf8, utf8Length + 1); + char * utf8 = (char *)m3_Malloc (utf8Length + 1); - if (not result) + if (utf8) { memcpy (utf8, ptr, utf8Length); utf8 [utf8Length] = 0; @@ -541,8 +553,7 @@ void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) if (UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) return; - M3BacktraceFrame * newFrame; - m3Alloc ((void **) & newFrame, M3BacktraceFrame, 1); + M3BacktraceFrame * newFrame = m3_AllocStruct(M3BacktraceFrame); if (!newFrame) { @@ -580,7 +591,7 @@ void ClearBacktrace (IM3Runtime io_runtime) while (currentFrame) { M3BacktraceFrame * nextFrame = currentFrame->next; - m3Free (currentFrame); + m3_Free (currentFrame); currentFrame = nextFrame; } diff --git a/Sources/CWasm3/m3_emit.c b/Sources/CWasm3/m3_emit.c index 46d7824..4a96a75 100644 --- a/Sources/CWasm3/m3_emit.c +++ b/Sources/CWasm3/m3_emit.c @@ -48,15 +48,15 @@ M3Result BridgeToNewPageIfNecessary (IM3Compilation o) M3Result EmitOp (IM3Compilation o, IM3Operation i_operation) { - M3Result result = m3Err_none; d_m3Assert (i_operation or IsStackPolymorphic(o)); + M3Result result = m3Err_none; d_m3Assert (i_operation or IsStackPolymorphic (o)); // it's OK for page to be null; when compile-walking the bytecode without emitting if (o->page) { # if d_m3EnableOpTracing if (i_operation != op_DumpStack) -# endif o->numEmits++; +# endif result = BridgeToNewPageIfNecessary (o); @@ -87,10 +87,14 @@ void EmitSlotOffset (IM3Compilation o, const i32 i_offset) } -void EmitPointer (IM3Compilation o, const void * const i_pointer) +pc_t EmitPointer (IM3Compilation o, const void * const i_pointer) { + pc_t ptr = GetPagePC (o->page); + if (o->page) EmitWord (o->page, i_pointer); + + return ptr; } void * ReservePointer (IM3Compilation o) diff --git a/Sources/CWasm3/m3_env.c b/Sources/CWasm3/m3_env.c index 6940152..3a159fd 100644 --- a/Sources/CWasm3/m3_env.c +++ b/Sources/CWasm3/m3_env.c @@ -6,6 +6,7 @@ // #include +#include #include "m3_env.h" #include "m3_compile.h" @@ -14,183 +15,39 @@ #include "m3_info.h" -M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes) -{ - return m3Alloc (o_functionType, u8, sizeof (M3FuncType) + i_numTypes); -} - - -bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB) -{ - if (i_typeA->numRets == i_typeB->numRets && i_typeA->numArgs == i_typeB->numArgs) - { - return (memcmp (i_typeA->types, i_typeB->types, i_typeA->numRets + i_typeA->numArgs) == 0); - } - - return false; -} - - -void Runtime_ReleaseCodePages (IM3Runtime i_runtime) +IM3Environment m3_NewEnvironment () { + M3Result result = m3Err_none; -} - - -void Function_Release (IM3Function i_function) -{ - m3Free (i_function->constants); + IM3Environment env = m3_AllocStruct (M3Environment); - for (int i = 0; i < i_function->numNames; i++) + if (env) { - // name can be an alias of fieldUtf8 - if (i_function->names[i] != i_function->import.fieldUtf8) + _try { - m3Free (i_function->names[i]); - } - } - - FreeImportInfo (& i_function->import); - - //if (i_function->ownsWasmCode) - // m3Free (i_function->wasm); - - // Function_FreeCompiledCode (func); - -# if (d_m3EnableCodePageRefCounting) - { - m3Free (i_function->codePageRefs); - i_function->numCodePageRefs = 0; - } -# endif -} - + // create FuncTypes for all simple block return ValueTypes + for (u8 t = c_m3Type_none; t <= c_m3Type_f64; t++) + { + IM3FuncType ftype; +_ (AllocFuncType (& ftype, 1)); -void Function_FreeCompiledCode (IM3Function i_function) -{ -# if (d_m3EnableCodePageRefCounting) - { - i_function->compiled = NULL; + ftype->numArgs = 0; + ftype->numRets = (t == c_m3Type_none) ? 0 : 1; + ftype->types [0] = t; - while (i_function->numCodePageRefs--) - { - IM3CodePage page = i_function->codePageRefs [i_function->numCodePageRefs]; + Environment_AddFuncType (env, & ftype); - if (--(page->info.usageCount) == 0) - { -// printf ("free %p\n", page); + d_m3Assert (t < 5); + env->retFuncTypes [t] = ftype; } } - m3Free (i_function->codePageRefs); - - Runtime_ReleaseCodePages (i_function->module->runtime); - } -# endif -} - - - -cstr_t m3_GetFunctionName (IM3Function i_function) -{ - u16 numNames = 0; - cstr_t *names = GetFunctionNames(i_function, &numNames); - if (numNames > 0) - return names[0]; - else - return ""; -} - -IM3Module m3_GetFunctionModule (IM3Function i_function) -{ - return i_function ? i_function->module : NULL; -} - - -cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames) -{ - if (!i_function || !o_numNames) - return NULL; - - if (i_function->import.fieldUtf8) - { - *o_numNames = 1; - return &i_function->import.fieldUtf8; - } - else - { - *o_numNames = i_function->numNames; - return i_function->names; - } -} - - -cstr_t GetFunctionImportModuleName (IM3Function i_function) -{ - return (i_function->import.moduleUtf8) ? i_function->import.moduleUtf8 : ""; -} - - -u32 GetFunctionNumArgs (IM3Function i_function) -{ - u32 numArgs = 0; - - if (i_function) - { - if (i_function->funcType) - numArgs = i_function->funcType->numArgs; - } - - return numArgs; -} - - -u32 GetFunctionNumReturns (IM3Function i_function) -{ - u32 numReturns = 0; - - if (i_function) - { - if (i_function->funcType) - numReturns = i_function->funcType->numRets; - } - - return numReturns; -} - -u32 GetFunctionNumArgsAndLocals (IM3Function i_function) -{ - if (i_function) - return i_function->numLocals + GetFunctionNumArgs (i_function); - else - return 0; -} - - -void FreeImportInfo (M3ImportInfo * i_info) -{ - m3Free (i_info->moduleUtf8); - m3Free (i_info->fieldUtf8); -} - - -IM3Environment m3_NewEnvironment () -{ - IM3Environment env = NULL; - m3Alloc (& env, M3Environment, 1); - - // create FuncTypes for all simple block return ValueTypes - for (int t = c_m3Type_none; t <= c_m3Type_f64; t++) - { - d_m3Assert (t < 5); - - IM3FuncType ftype; - AllocFuncType (& ftype, 1); - ftype->numArgs = 0; - ftype->numRets = (t == c_m3Type_none) ? 0 : 1; - ftype->types[0] = t; - - env->retFuncTypes[t] = ftype; + _catch: + if (result) + { + m3_FreeEnvironment (env); + env = NULL; + } } return env; @@ -204,16 +61,10 @@ void Environment_Release (IM3Environment i_environment) while (ftype) { IM3FuncType next = ftype->next; - m3Free (ftype); + m3_Free (ftype); ftype = next; } - for (int t = c_m3Type_none; t <= c_m3Type_f64; t++) - { - d_m3Assert (t < 5); - ftype = i_environment->retFuncTypes[t]; - d_m3Assert (ftype->next == NULL); - m3Free (ftype); - } + m3log (runtime, "freeing %d pages from environment", CountCodePages (i_environment->pagesReleased)); FreeCodePages (& i_environment->pagesReleased); } @@ -224,11 +75,12 @@ void m3_FreeEnvironment (IM3Environment i_environment) if (i_environment) { Environment_Release (i_environment); - m3Free (i_environment); + m3_Free (i_environment); } } +// returns the same io_funcType or replaces it with an equivalent that's already in the type linked list void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType) { IM3FuncType addType = * io_funcType; @@ -238,7 +90,7 @@ void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_f { if (AreFuncTypesEqual (newType, addType)) { - m3Free (addType); + m3_Free (addType); break; } @@ -255,6 +107,7 @@ void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_f * io_funcType = newType; } + IM3CodePage RemoveCodePageOfCapacity (M3CodePage ** io_list, u32 i_minimumLineCount) { IM3CodePage prev = NULL; @@ -316,8 +169,7 @@ void Environment_ReleaseCodePages (IM3Environment i_environment, IM3CodePage i IM3Runtime m3_NewRuntime (IM3Environment i_environment, u32 i_stackSizeInBytes, void * i_userdata) { - IM3Runtime runtime = NULL; - m3Alloc (& runtime, M3Runtime, 1); + IM3Runtime runtime = m3_AllocStruct (M3Runtime); if (runtime) { @@ -326,13 +178,13 @@ IM3Runtime m3_NewRuntime (IM3Environment i_environment, u32 i_stackSizeInBytes runtime->environment = i_environment; runtime->userdata = i_userdata; - m3Alloc (& runtime->stack, u8, i_stackSizeInBytes); + runtime->stack = m3_Malloc (i_stackSizeInBytes + 4*sizeof (m3slot_t)); // TODO: more precise stack checks if (runtime->stack) { runtime->numStackSlots = i_stackSizeInBytes / sizeof (m3slot_t); m3log (runtime, "new stack: %p", runtime->stack); } - else m3Free (runtime); + else m3_Free (runtime); } return runtime; @@ -343,6 +195,7 @@ void * m3_GetUserData (IM3Runtime i_runtime) return i_runtime ? i_runtime->userdata : NULL; } + void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info) { void * r = NULL; @@ -370,20 +223,6 @@ void * _FreeModule (IM3Module i_module, void * i_info) } - -void FreeCompilationPatches (IM3Compilation o) -{ - IM3BranchPatch patches = o->releasedPatches; - - while (patches) - { - IM3BranchPatch next = patches->next; - m3Free (patches); - patches = next; - } -} - - void Runtime_Release (IM3Runtime i_runtime) { ForEachModule (i_runtime, _FreeModule, NULL); d_m3Assert (i_runtime->numActiveCodePages == 0); @@ -391,10 +230,8 @@ void Runtime_Release (IM3Runtime i_runtime) Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesOpen); Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesFull); - FreeCompilationPatches (& i_runtime->compilation); - - m3Free (i_runtime->stack); - m3Free (i_runtime->memory.mallocated); + m3_Free (i_runtime->stack); + m3_Free (i_runtime->memory.mallocated); } @@ -405,15 +242,16 @@ void m3_FreeRuntime (IM3Runtime i_runtime) m3_PrintProfilerInfo (); Runtime_Release (i_runtime); - m3Free (i_runtime); + m3_Free (i_runtime); } } - M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type, bytes_t * io_bytes, cbytes_t i_end) { M3Result result = m3Err_none; + // OPTZ: use a simplified interpreter for expressions + // create a temporary runtime context #if defined(d_m3PreferStaticAlloc) static M3Runtime runtime; @@ -450,12 +288,16 @@ M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type pc_t m3code = GetPagePC (o->page); result = CompileBlock (o, ftype, c_waOp_block); + if (not result && o->maxStackSlots >= runtime.numStackSlots) { + result = m3Err_trapStackOverflow; + } + if (not result) { m3ret_t r = Call (m3code, stack, NULL, d_m3OpDefaultArgs); if (r == 0) - { + { m3log (runtime, "expression result: %s", SPrintValue (stack, i_type)); if (SizeOfType (i_type) == sizeof (u32)) { * (u32 *) o_expressed = * ((u32 *) stack); @@ -520,7 +362,11 @@ M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages) { size_t numPageBytes = numPagesToAlloc * d_m3MemPageSize; - // Limit the amount of memory that gets allocated +#if d_m3MaxLinearMemoryPages > 0 + _throwif("linear memory limitation exceeded", numPagesToAlloc > d_m3MaxLinearMemoryPages); +#endif + + // Limit the amount of memory that gets actually allocated if (io_runtime->memoryLimit) { numPageBytes = M3_MIN (numPageBytes, io_runtime->memoryLimit); } @@ -531,7 +377,10 @@ M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages) if (numPreviousBytes) numPreviousBytes += sizeof (M3MemoryHeader); -_ (m3Reallocate (& memory->mallocated, numBytes, numPreviousBytes)); + void* newMem = m3_Realloc (memory->mallocated, numBytes, numPreviousBytes); + _throwifnull(newMem); + + memory->mallocated = (M3MemoryHeader*)newMem; # if d_m3LogRuntime M3MemoryHeader * oldMallocated = memory->mallocated; @@ -597,6 +446,8 @@ M3Result InitDataSegments (M3Memory * io_memory, IM3Module io_module) { M3Result result = m3Err_none; + _throwif ("unallocated linear memory", !(io_memory->mallocated)); + for (u32 i = 0; i < io_module->numDataSegments; ++i) { M3DataSegment * segment = & io_module->dataSegments [i]; @@ -607,16 +458,13 @@ _ (EvaluateExpression (io_module, & segmentOffset, c_m3Type_i32, & start, m3log (runtime, "loading data segment: %d; size: %d; offset: %d", i, segment->size, segmentOffset); - if (io_memory->mallocated) + if (segmentOffset >= 0 && (size_t)(segmentOffset) + segment->size <= io_memory->mallocated->length) { u8 * dest = m3MemData (io_memory->mallocated) + segmentOffset; - - if ((size_t) segmentOffset + segment->size <= io_memory->mallocated->length) - memcpy (dest, segment->data, segment->size); - else - _throw ("data segment overflowing linear memory"); + memcpy (dest, segment->data, segment->size); + } else { + _throw ("data segment out of bounds"); } - else _throw ("unallocated linear memory"); } _catch: return result; @@ -639,16 +487,22 @@ _ (ReadLEB_u32 (& index, & bytes, end)); { i32 offset; _ (EvaluateExpression (io_module, & offset, c_m3Type_i32, & bytes, end)); + _throwif ("table underflow", offset < 0); u32 numElements; _ (ReadLEB_u32 (& numElements, & bytes, end)); - u32 endElement = numElements + offset; - - _throwif ("table overflow", offset >= endElement); // TODO: check this, endElement depends on offset -_ (m3ReallocArray (& io_module->table0, IM3Function, endElement, io_module->table0Size)); + size_t endElement = (size_t) numElements + offset; + _throwif ("table overflow", endElement > d_m3MaxSaneTableSize); - io_module->table0Size = endElement; + // is there any requirement that elements must be in increasing sequence? + // make sure the table isn't shrunk. + if (endElement > io_module->table0Size) + { + io_module->table0 = m3_ReallocArray (IM3Function, io_module->table0, endElement, io_module->table0Size); + io_module->table0Size = (u32) endElement; + } + _throwifnull(io_module->table0); for (u32 e = 0; e < numElements; ++e) { @@ -665,8 +519,29 @@ _ (ReadLEB_u32 (& functionIndex, & bytes, end)); _catch: return result; } +M3Result m3_CompileModule (IM3Module io_module) +{ + M3Result result = m3Err_none; + + for (u32 i = 0; i < io_module->numFunctions; ++i) + { + IM3Function f = & io_module->functions [i]; + if (f->wasm and not f->compiled) + { +_ (CompileFunction (f)); + } + } + + _catch: return result; +} + M3Result m3_RunStart (IM3Module io_module) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // Execution disabled for fuzzing builds + return m3Err_none; +#endif + M3Result result = m3Err_none; if (io_module and io_module->startFunction >= 0) @@ -675,7 +550,7 @@ M3Result m3_RunStart (IM3Module io_module) if (not function->compiled) { -_ (Compile_Function (function)); +_ (CompileFunction (function)); } IM3FuncType ftype = function->funcType; @@ -698,28 +573,106 @@ M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module) { M3Result result = m3Err_none; - if (not io_module->runtime) + if (UNLIKELY(io_module->runtime)) { + return m3Err_moduleAlreadyLinked; + } + + io_module->runtime = io_runtime; + M3Memory * memory = & io_runtime->memory; + +_ (InitMemory (io_runtime, io_module)); +_ (InitGlobals (io_module)); +_ (InitDataSegments (memory, io_module)); +_ (InitElements (io_module)); + + // Start func might use imported functions, which are not liked here yet, + // so it will be called before a function call is attempted (in m3_FindFunction) + +#ifdef DEBUG + Module_GenerateNames(io_module); +#endif + + io_module->next = io_runtime->modules; + io_runtime->modules = io_module; + return result; // ok + +_catch: + io_module->runtime = NULL; + return result; +} + +IM3Global m3_FindGlobal (IM3Module io_module, + const char * const i_globalName) +{ + // Search exports + for (u32 i = 0; i < io_module->numGlobals; ++i) { - io_module->runtime = io_runtime; - M3Memory * memory = & io_runtime->memory; + IM3Global g = & io_module->globals [i]; + if (g->name and strcmp (g->name, i_globalName) == 0) + { + return g; + } + } -_ (InitMemory (io_runtime, io_module)); -_ (InitGlobals (io_module)); -_ (InitDataSegments (memory, io_module)); -_ (InitElements (io_module)); + // Search imports + for (u32 i = 0; i < io_module->numGlobals; ++i) + { + IM3Global g = & io_module->globals [i]; - io_module->next = io_runtime->modules; - io_runtime->modules = io_module; + if (g->import.moduleUtf8 and g->import.fieldUtf8) + { + if (strcmp (g->import.fieldUtf8, i_globalName) == 0) + { + return g; + } + } + } + return NULL; +} - // Start func might use imported functions, which are not liked here yet, - // so it will be called before a function call is attempted (in m3_FindFuSnction) +M3Result m3_GetGlobal (IM3Global i_global, + IM3TaggedValue o_value) +{ + if (not i_global) return m3Err_globalLookupFailed; + + switch (i_global->type) { + case c_m3Type_i32: o_value->value.i32 = i_global->intValue; break; + case c_m3Type_i64: o_value->value.i64 = i_global->intValue; break; +# if d_m3HasFloat + case c_m3Type_f32: o_value->value.f32 = i_global->f32Value; break; + case c_m3Type_f64: o_value->value.f64 = i_global->f64Value; break; +# endif + default: return m3Err_invalidTypeId; } - else result = m3Err_moduleAlreadyLinked; - if (result) - io_module->runtime = NULL; + o_value->type = (M3ValueType)(i_global->type); + return m3Err_none; +} - _catch: return result; +M3Result m3_SetGlobal (IM3Global i_global, + const IM3TaggedValue i_value) +{ + if (not i_global) return m3Err_globalLookupFailed; + // TODO: if (not g->isMutable) return m3Err_globalNotMutable; + + if (i_global->type != i_value->type) return m3Err_globalTypeMismatch; + + switch (i_value->type) { + case c_m3Type_i32: i_global->intValue = i_value->value.i32; break; + case c_m3Type_i64: i_global->intValue = i_value->value.i64; break; +# if d_m3HasFloat + case c_m3Type_f32: i_global->f32Value = i_value->value.f32; break; + case c_m3Type_f64: i_global->f64Value = i_value->value.f64; break; +# endif + default: return m3Err_invalidTypeId; + } + + return m3Err_none; +} + +M3ValueType m3_GetGlobalType (IM3Global i_global) +{ + return (i_global) ? (M3ValueType)(i_global->type) : c_m3Type_none; } @@ -747,37 +700,41 @@ void * v_FindFunction (IM3Module i_module, const char * const i_name) M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, const char * const i_functionName) { - M3Result result = m3Err_none; + M3Result result = m3Err_none; d_m3Assert (o_function and i_runtime and i_functionName); - if (!i_runtime->modules) { - return "no modules loaded"; + IM3Function function = NULL; + + if (not i_runtime->modules) { + _throw ("no modules loaded"); } - IM3Function function = (IM3Function) ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName); + function = (IM3Function) ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName); if (function) { if (not function->compiled) { - result = Compile_Function (function); - if (result) - function = NULL; +_ (CompileFunction (function)) } - } - else result = ErrorModule (m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName); - // Check if start function needs to be called - if (function and function->module->startFunction) { - result = m3_RunStart (function->module); - if (result) - return result; + // Check if start function needs to be called + if (function->module->startFunction) + { +_ (m3_RunStart (function->module)) + } } + else _throw (ErrorModule (m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName)); + + _catch: + if (result) + function = NULL; * o_function = function; return result; } + uint32_t m3_GetArgCount (IM3Function i_function) { if (i_function) { @@ -817,12 +774,24 @@ M3ValueType m3_GetRetType (IM3Function i_function, uint32_t index) if (i_function) { IM3FuncType ft = i_function->funcType; if (ft and index < ft->numRets) { - return (M3ValueType)d_FuncRetType(ft, index); + return (M3ValueType) d_FuncRetType (ft, index); } } return c_m3Type_none; } + +u8 * GetStackPointerForArgs (IM3Function i_function) +{ + u64 * stack = (u64 *) i_function->module->runtime->stack; + IM3FuncType ftype = i_function->funcType; + + stack += ftype->numRets; + + return (u8 *) stack; +} + + M3Result m3_CallV (IM3Function i_function, ...) { va_list ap; @@ -832,6 +801,16 @@ M3Result m3_CallV (IM3Function i_function, ...) return r; } +static +void ReportNativeStackUsage () +{ +# if d_m3LogNativeStack + int stackUsed = m3StackGetMax(); + fprintf (stderr, "Native stack used: %d\n", stackUsed); +# endif +} + + M3Result m3_CallVL (IM3Function i_function, va_list i_args) { IM3Runtime runtime = i_function->module->runtime; @@ -845,27 +824,26 @@ M3Result m3_CallVL (IM3Function i_function, va_list i_args) ClearBacktrace (runtime); # endif - u8* s = (u8*) runtime->stack; + u8* s = GetStackPointerForArgs (i_function); + for (u32 i = 0; i < ftype->numArgs; ++i) { switch (d_FuncArgType(ftype, i)) { case c_m3Type_i32: *(i32*)(s) = va_arg(i_args, i32); s += 8; break; case c_m3Type_i64: *(i64*)(s) = va_arg(i_args, i64); s += 8; break; +# if d_m3HasFloat case c_m3Type_f32: *(f32*)(s) = va_arg(i_args, f64); s += 8; break; // f32 is passed as f64 case c_m3Type_f64: *(f64*)(s) = va_arg(i_args, f64); s += 8; break; +# endif default: return "unknown argument type"; } } m3StackCheckInit(); M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + ReportNativeStackUsage (); runtime->lastCalled = r ? NULL : i_function; -#if d_m3LogNativeStack - int stackUsed = m3StackGetMax(); - fprintf (stderr, "Native stack used: %d\n", stackUsed); -#endif - return r; } @@ -885,27 +863,27 @@ M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argp ClearBacktrace (runtime); # endif - u8* s = (u8*) runtime->stack; + u8* s = GetStackPointerForArgs (i_function); + for (u32 i = 0; i < ftype->numArgs; ++i) { switch (d_FuncArgType(ftype, i)) { case c_m3Type_i32: *(i32*)(s) = *(i32*)i_argptrs[i]; s += 8; break; case c_m3Type_i64: *(i64*)(s) = *(i64*)i_argptrs[i]; s += 8; break; +# if d_m3HasFloat case c_m3Type_f32: *(f32*)(s) = *(f32*)i_argptrs[i]; s += 8; break; case c_m3Type_f64: *(f64*)(s) = *(f64*)i_argptrs[i]; s += 8; break; +# endif default: return "unknown argument type"; } } m3StackCheckInit(); M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + ReportNativeStackUsage (); runtime->lastCalled = r ? NULL : i_function; -#if d_m3LogNativeStack - int stackUsed = m3StackGetMax(); - fprintf (stderr, "Native stack used: %d\n", stackUsed); -#endif return r; } @@ -926,31 +904,38 @@ M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_ ClearBacktrace (runtime); # endif - u8* s = (u8*) runtime->stack; + u8* s = GetStackPointerForArgs (i_function); + for (u32 i = 0; i < ftype->numArgs; ++i) { switch (d_FuncArgType(ftype, i)) { case c_m3Type_i32: *(i32*)(s) = strtoul(i_argv[i], NULL, 10); s += 8; break; case c_m3Type_i64: *(i64*)(s) = strtoull(i_argv[i], NULL, 10); s += 8; break; +# if d_m3HasFloat case c_m3Type_f32: *(f32*)(s) = strtod(i_argv[i], NULL); s += 8; break; // strtof would be less portable case c_m3Type_f64: *(f64*)(s) = strtod(i_argv[i], NULL); s += 8; break; +# endif default: return "unknown argument type"; } } m3StackCheckInit(); M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + ReportNativeStackUsage (); runtime->lastCalled = r ? NULL : i_function; -#if d_m3LogNativeStack - int stackUsed = m3StackGetMax(); - fprintf (stderr, "Native stack used: %d\n", stackUsed); -#endif - return r; } + +//u8 * AlignStackPointerTo64Bits (const u8 * i_stack) +//{ +// uintptr_t ptr = (uintptr_t) i_stack; +// return (u8 *) ((ptr + 7) & ~7); +//} + + M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]) { IM3FuncType ftype = i_function->funcType; @@ -964,13 +949,16 @@ M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * } u8* s = (u8*) runtime->stack; + for (u32 i = 0; i < ftype->numRets; ++i) { switch (d_FuncRetType(ftype, i)) { case c_m3Type_i32: *(i32*)o_retptrs[i] = *(i32*)(s); s += 8; break; case c_m3Type_i64: *(i64*)o_retptrs[i] = *(i64*)(s); s += 8; break; +# if d_m3HasFloat case c_m3Type_f32: *(f32*)o_retptrs[i] = *(f32*)(s); s += 8; break; case c_m3Type_f64: *(f64*)o_retptrs[i] = *(f64*)(s); s += 8; break; +# endif default: return "unknown return type"; } } @@ -1001,8 +989,10 @@ M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets) switch (d_FuncRetType(ftype, i)) { case c_m3Type_i32: *va_arg(o_rets, i32*) = *(i32*)(s); s += 8; break; case c_m3Type_i64: *va_arg(o_rets, i64*) = *(i64*)(s); s += 8; break; +# if d_m3HasFloat case c_m3Type_f32: *va_arg(o_rets, f32*) = *(f32*)(s); s += 8; break; case c_m3Type_f64: *va_arg(o_rets, f64*) = *(f64*)(s); s += 8; break; +# endif default: return "unknown argument type"; } } @@ -1079,13 +1069,14 @@ void ReleaseCodePage (IM3Runtime i_runtime, IM3CodePage i_codePage) } -#if d_m3VerboseLogs +#if d_m3VerboseErrorMessages M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...) { if (i_runtime) { - i_runtime->error = (M3ErrorInfo){ i_result, i_runtime, i_module, i_function, i_file, i_lineNum }; + i_runtime->error = (M3ErrorInfo){ .result = i_result, .runtime = i_runtime, .module = i_module, + .function = i_function, .file = i_file, .line = i_lineNum }; i_runtime->error.message = i_runtime->error_message; va_list args; @@ -1120,8 +1111,7 @@ void m3_ResetErrorInfo (IM3Runtime i_runtime) uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, uint32_t i_memoryIndex) { - uint8_t * memory = NULL; - d_m3Assert (i_memoryIndex == 0); + uint8_t * memory = NULL; d_m3Assert (i_memoryIndex == 0); if (i_runtime) { @@ -1137,6 +1127,13 @@ uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, return memory; } + +uint32_t m3_GetMemorySize (IM3Runtime i_runtime) +{ + return i_runtime->memory.mallocated->length; +} + + M3BacktraceInfo * m3_GetBacktrace (IM3Runtime i_runtime) { # if d_m3RecordBacktraces diff --git a/Sources/CWasm3/m3_extensions.c b/Sources/CWasm3/m3_extensions.c new file mode 100644 index 0000000..17ba6ee --- /dev/null +++ b/Sources/CWasm3/m3_extensions.c @@ -0,0 +1,112 @@ +// +// m3_extensions.c +// +// Created by Steven Massey on 3/30/21. +// Copyright © 2021 Steven Massey. All rights reserved. +// + +#include "wasm3_ext.h" + +#include "m3_env.h" +#include "m3_bind.h" +#include "m3_exception.h" + + +IM3Module m3_NewModule (IM3Environment i_environment) +{ + IM3Module module = m3_AllocStruct (M3Module); + + if (module) + { + module->name = ".unnamed"; + module->startFunction = -1; + module->environment = i_environment; + + module->wasmStart = NULL; + module->wasmEnd = NULL; + } + + return module; +} + + + +M3Result m3_InjectFunction (IM3Module i_module, + int32_t * io_functionIndex, + const char * const i_signature, + const uint8_t * const i_wasmBytes, + bool i_doCompilation) +{ + M3Result result = m3Err_none; d_m3Assert (io_functionIndex); + + IM3Function function = NULL; + IM3FuncType ftype = NULL; +_ (SignatureToFuncType (& ftype, i_signature)); + + i32 index = * io_functionIndex; + + bytes_t bytes = i_wasmBytes; + bytes_t end = i_wasmBytes + 5; + + u32 size; +_ (ReadLEB_u32 (& size, & bytes, end)); + end = bytes + size; + + if (index >= 0) + { + _throwif ("function index out of bounds", index >= i_module->numFunctions); + + function = & i_module->functions [index]; + + if (not AreFuncTypesEqual (ftype, function->funcType)) + _throw ("function type mismatch"); + } + else + { + // add slot to function type table in the module + u32 funcTypeIndex = i_module->numFuncTypes++; + i_module->funcTypes = m3_ReallocArray (IM3FuncType, i_module->funcTypes, i_module->numFuncTypes, funcTypeIndex); + _throwifnull (i_module->funcTypes); + + // add functype object to the environment + Environment_AddFuncType (i_module->environment, & ftype); + i_module->funcTypes [funcTypeIndex] = ftype; + ftype = NULL; // prevent freeing below + + index = (i32) i_module->numFunctions; +_ (Module_AddFunction (i_module, funcTypeIndex, NULL)); + function = Module_GetFunction (i_module, index); + + * io_functionIndex = index; + } + + function->compiled = NULL; + + if (function->ownsWasmCode) + m3_Free (function->wasm); + + size_t numBytes = end - i_wasmBytes; + function->wasm = m3_CopyMem (i_wasmBytes, numBytes); + _throwifnull (function->wasm); + + function->wasmEnd = function->wasm + numBytes; + function->ownsWasmCode = true; + + function->module = i_module; + + if (i_doCompilation and not i_module->runtime) + _throw ("module must be loaded into runtime to compile function"); + +_ (CompileFunction (function)); + + _catch: + m3_Free (ftype); + + return result; +} + + +IM3Function m3_GetFunctionByIndex (IM3Module i_module, uint32_t i_index) +{ + return Module_GetFunction (i_module, i_index); +} diff --git a/Sources/CWasm3/m3_function.c b/Sources/CWasm3/m3_function.c new file mode 100644 index 0000000..8823db4 --- /dev/null +++ b/Sources/CWasm3/m3_function.c @@ -0,0 +1,233 @@ +// +// m3_function.c +// +// Created by Steven Massey on 4/7/21. +// Copyright © 2021 Steven Massey. All rights reserved. +// + +#include "m3_function.h" +#include "m3_env.h" + + +M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numTypes) +{ + *o_functionType = (IM3FuncType) m3_Malloc (sizeof (M3FuncType) + i_numTypes); + return (*o_functionType) ? m3Err_none : m3Err_mallocFailed; +} + + +bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB) +{ + if (i_typeA->numRets == i_typeB->numRets && i_typeA->numArgs == i_typeB->numArgs) + { + return (memcmp (i_typeA->types, i_typeB->types, i_typeA->numRets + i_typeA->numArgs) == 0); + } + + return false; +} + +u16 GetFuncTypeNumParams (const IM3FuncType i_funcType) +{ + return i_funcType ? i_funcType->numArgs : 0; +} + + +u8 GetFuncTypeParamType (const IM3FuncType i_funcType, u16 i_index) +{ + u8 type = c_m3Type_unknown; + + if (i_funcType) + { + if (i_index < i_funcType->numArgs) + { + type = i_funcType->types [i_funcType->numRets + i_index]; + } + } + + return type; +} + + + +u16 GetFuncTypeNumResults (const IM3FuncType i_funcType) +{ + return i_funcType ? i_funcType->numRets : 0; +} + + +u8 GetFuncTypeResultType (const IM3FuncType i_funcType, u16 i_index) +{ + u8 type = c_m3Type_unknown; + + if (i_funcType) + { + if (i_index < i_funcType->numRets) + { + type = i_funcType->types [i_index]; + } + } + + return type; +} + + +//--------------------------------------------------------------------------------------------------------------- + + +void FreeImportInfo (M3ImportInfo * i_info) +{ + m3_Free (i_info->moduleUtf8); + m3_Free (i_info->fieldUtf8); +} + + +void Function_Release (IM3Function i_function) +{ + m3_Free (i_function->constants); + + for (int i = 0; i < i_function->numNames; i++) + { + // name can be an alias of fieldUtf8 + if (i_function->names[i] != i_function->import.fieldUtf8) + { + m3_Free (i_function->names[i]); + } + } + + FreeImportInfo (& i_function->import); + + if (i_function->ownsWasmCode) + m3_Free (i_function->wasm); + + // Function_FreeCompiledCode (func); + +# if (d_m3EnableCodePageRefCounting) + { + m3_Free (i_function->codePageRefs); + i_function->numCodePageRefs = 0; + } +# endif +} + + +void Function_FreeCompiledCode (IM3Function i_function) +{ +# if (d_m3EnableCodePageRefCounting) + { + i_function->compiled = NULL; + + while (i_function->numCodePageRefs--) + { + IM3CodePage page = i_function->codePageRefs [i_function->numCodePageRefs]; + + if (--(page->info.usageCount) == 0) + { +// printf ("free %p\n", page); + } + } + + m3_Free (i_function->codePageRefs); + + Runtime_ReleaseCodePages (i_function->module->runtime); + } +# endif +} + + +cstr_t m3_GetFunctionName (IM3Function i_function) +{ + u16 numNames = 0; + cstr_t *names = GetFunctionNames(i_function, &numNames); + if (numNames > 0) + return names[0]; + else + return ""; +} + + +IM3Module m3_GetFunctionModule (IM3Function i_function) +{ + return i_function ? i_function->module : NULL; +} + + +cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames) +{ + if (!i_function || !o_numNames) + return NULL; + + if (i_function->import.fieldUtf8) + { + *o_numNames = 1; + return &i_function->import.fieldUtf8; + } + else + { + *o_numNames = i_function->numNames; + return i_function->names; + } +} + + +cstr_t GetFunctionImportModuleName (IM3Function i_function) +{ + return (i_function->import.moduleUtf8) ? i_function->import.moduleUtf8 : ""; +} + + +u16 GetFunctionNumArgs (IM3Function i_function) +{ + u16 numArgs = 0; + + if (i_function) + { + if (i_function->funcType) + numArgs = i_function->funcType->numArgs; + } + + return numArgs; +} + +u8 GetFunctionArgType (IM3Function i_function, u32 i_index) +{ + u8 type = c_m3Type_none; + + if (i_index < GetFunctionNumArgs (i_function)) + { + u32 numReturns = i_function->funcType->numRets; + + type = i_function->funcType->types [numReturns + i_index]; + } + + return type; +} + + +u16 GetFunctionNumReturns (IM3Function i_function) +{ + u16 numReturns = 0; + + if (i_function) + { + if (i_function->funcType) + numReturns = i_function->funcType->numRets; + } + + return numReturns; +} + + +u8 GetFunctionReturnType (const IM3Function i_function, u16 i_index) +{ + return i_function ? GetFuncTypeResultType (i_function->funcType, i_index) : c_m3Type_unknown; +} + + +u32 GetFunctionNumArgsAndLocals (IM3Function i_function) +{ + if (i_function) + return i_function->numLocals + GetFunctionNumArgs (i_function); + else + return 0; +} + diff --git a/Sources/CWasm3/m3_info.c b/Sources/CWasm3/m3_info.c index 7ce7dc7..2364296 100644 --- a/Sources/CWasm3/m3_info.c +++ b/Sources/CWasm3/m3_info.c @@ -13,6 +13,14 @@ #ifdef DEBUG +// a central function you can be breakpoint: +void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message) +{ + printf ("\nexception: '%s' @ %s\n", i_exception, i_message); + return; +} + + typedef struct OpInfo { IM3OpInfo info; @@ -26,6 +34,7 @@ void m3_PrintM3Info () // printf (" sizeof M3CodePage : %zu bytes (%d slots) \n", sizeof (M3CodePage), c_m3CodePageNumSlots); printf (" sizeof M3MemPage : %u bytes \n", d_m3MemPageSize); printf (" sizeof M3Compilation : %zu bytes \n", sizeof (M3Compilation)); + printf (" sizeof M3Function : %zu bytes \n", sizeof (M3Function)); printf ("----------------------------------------------------------------\n\n"); } @@ -60,6 +69,9 @@ cstr_t GetTypeName (u8 i_m3Type) } +// TODO: these 'static char string []' aren't thread-friendly. though these functions are +// mainly for simple diagnostics during development, it'd be nice if they were fully reliable. + cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType) { static char string [256]; @@ -88,21 +100,21 @@ cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType) } -size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type) +size_t SPrintArg (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type) { int len = 0; * o_string = 0; if (i_type == c_m3Type_i32) - len = snprintf (o_string, i_n, "%" PRIi32, * (i32 *) i_sp); + len = snprintf (o_string, i_stringBufferSize, "%" PRIi32, * (i32 *) i_sp); else if (i_type == c_m3Type_i64) - len = snprintf (o_string, i_n, "%" PRIi64, * (i64 *) i_sp); + len = snprintf (o_string, i_stringBufferSize, "%" PRIi64, * (i64 *) i_sp); #if d_m3HasFloat else if (i_type == c_m3Type_f32) - len = snprintf (o_string, i_n, "%" PRIf32, * (f32 *) i_sp); + len = snprintf (o_string, i_stringBufferSize, "%" PRIf32, * (f32 *) i_sp); else if (i_type == c_m3Type_f64) - len = snprintf (o_string, i_n, "%" PRIf64, * (f64 *) i_sp); + len = snprintf (o_string, i_stringBufferSize, "%" PRIf64, * (f64 *) i_sp); #endif len = M3_MAX (0, len); @@ -111,6 +123,14 @@ size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type) } +cstr_t SPrintValue (void * i_value, u8 i_type) +{ + static char string [100]; + SPrintArg (string, 100, (m3stack_t) i_value, i_type); + return string; +} + + cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp) { int ret; @@ -122,7 +142,7 @@ cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp) ret = snprintf (s, e-s, "("); s += M3_MAX (0, ret); - m3stack_t argSp = i_sp; + u64 * argSp = (u64 *) i_sp; IM3FuncType funcType = i_function->funcType; if (funcType) @@ -162,7 +182,7 @@ OpInfo find_operation_info (IM3Operation i_operation) // TODO: find also extended opcodes for (u32 i = 0; i <= 0xff; ++i) { - IM3OpInfo oi = GetOpInfo(i); + IM3OpInfo oi = GetOpInfo (i); if (oi->type != c_m3Type_unknown) { @@ -230,24 +250,20 @@ d_m3Decoder (BranchTable) { u32 slot = fetch (u32); - sprintf (o_string, "slot: %" PRIu32 "; targets: ", slot); + o_string += sprintf (o_string, "slot: %" PRIu32 "; targets: ", slot); // IM3Function function = fetch2 (IM3Function); i32 targets = fetch (i32); - char str [1000]; - for (i32 i = 0; i < targets; ++i) { pc_t addr = fetch (pc_t); - sprintf (str, "%" PRIi32 "=%p, ", i, addr); - strcat (o_string, str); + o_string += sprintf (o_string, "%" PRIi32 "=%p, ", i, addr); } pc_t addr = fetch (pc_t); - sprintf (str, "def=%p ", addr); - strcat (o_string, str); + sprintf (o_string, "def=%p ", addr); } @@ -267,21 +283,14 @@ void DecodeOperation (char * o_string, u8 i_opcode, IM3Operation i_operation, switch (i_opcode) { // d_m3Decode (0xc0, Const) -// d_m3Decode (0xc1, Entry) + d_m3Decode (0xc5, Entry) d_m3Decode (c_waOp_call, Call) d_m3Decode (c_waOp_branch, Branch) d_m3Decode (c_waOp_branchTable, BranchTable) d_m3Decode (0x39, f64_Store) } - - #undef d_m3Decode - #define d_m3Decode(FUNC) if (i_operation == op_##FUNC) Decode_##FUNC (o_string, i_opcode, i_operation, i_opInfo, o_pc); - - d_m3Decode (Entry) } - -# ifdef DEBUG // WARNING/TODO: this isn't fully implemented. it blindly assumes each word is a Operation pointer // and, if an operation happens to missing from the c_operations table it won't be recognized here void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC) @@ -302,7 +311,7 @@ void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC) if (i.info) { - char infoString [1000] = { 0 }; + char infoString [8*1024] = { 0 }; DecodeOperation (infoString, i.opcode, op, i.info, & pc); @@ -317,7 +326,6 @@ void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC) m3log (code, "free-lines: %d", i_codePage->info.numLines - i_codePage->info.lineIndex); } -# endif void dump_type_stack (IM3Compilation o) @@ -341,44 +349,102 @@ void dump_type_stack (IM3Compilation o) i32 regAllocated [2] = { (i32) IsRegisterAllocated (o, 0), (i32) IsRegisterAllocated (o, 1) }; // display whether r0 or fp0 is allocated. these should then also be reflected somewhere in the stack too. - d_m3Log(stack, ""); - printf (" "); + d_m3Log(stack, "\n"); + d_m3Log(stack, " "); printf ("%s %s ", regAllocated [0] ? "(r0)" : " ", regAllocated [1] ? "(fp0)" : " "); + printf("\n"); - for (u32 i = o->firstDynamicStackIndex; i < o->stackIndex; ++i) + for (u32 p = 1; p <= 2; ++p) { - printf (" %s", c_waCompactTypes [o->typeStack [i]]); - - u16 slot = o->wasmStack [i]; + d_m3Log(stack, " "); - if (IsRegisterLocation (slot)) + for (u32 i = 0; i < o->stackIndex; ++i) { - bool isFp = IsFpRegisterLocation (slot); - printf ("%s", isFp ? "f0" : "r0"); + if (i > 0 and i == o->stackFirstDynamicIndex) + printf ("#"); - regAllocated [isFp]--; - } - else - { - if (slot < o->firstDynamicSlotIndex) + if (i == o->block.blockStackIndex) + printf (">"); + + const char * type = c_waCompactTypes [o->typeStack [i]]; + + const char * location = ""; + + i32 slot = o->wasmStack [i]; + + if (IsRegisterSlotAlias (slot)) { - if (slot >= o->firstConstSlotIndex) - printf ("c"); - else if (slot >= o->function->numArgSlots) - printf ("L"); - else - printf ("a"); + bool isFp = IsFpRegisterSlotAlias (slot); + location = isFp ? "/f" : "/r"; + + regAllocated [isFp]--; + slot = -1; + } + else + { + if (slot < o->slotFirstDynamicIndex) + { + if (slot >= o->slotFirstConstIndex) + location = "c"; + else if (slot >= o->function->numRetAndArgSlots) + location = "L"; + else + location = "a"; + } } - printf ("%d", (i32) slot); // slot - } + char item [100]; + + if (slot >= 0) + sprintf (item, "%s%s%d", type, location, slot); + else + sprintf (item, "%s%s", type, location); + + if (p == 1) + { + size_t s = strlen (item); - printf (" "); + sprintf (item, "%d", i); + + while (strlen (item) < s) + strcat (item, " "); + } + + printf ("|%s ", item); + + } + printf ("\n"); } - printf ("\n"); - for (u32 r = 0; r < 2; ++r) - d_m3Assert (regAllocated [r] == 0); // reg allocation & stack out of sync +// for (u32 r = 0; r < 2; ++r) +// d_m3Assert (regAllocated [r] == 0); // reg allocation & stack out of sync + + u16 maxSlot = GetMaxUsedSlotPlusOne (o); + + if (maxSlot > o->slotFirstDynamicIndex) + { + d_m3Log (stack, " -"); + + for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) + printf ("----"); + + printf ("\n"); + + d_m3Log (stack, " slot |"); + for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) + printf ("%3d|", i); + + printf ("\n"); + d_m3Log (stack, " alloc |"); + + for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i) + { + printf ("%3d|", o->m3Slots [i]); + } + + printf ("\n"); + } + d_m3Log(stack, "\n"); } @@ -405,17 +471,13 @@ const char * get_indention_string (IM3Compilation o) } -void log_opcode (IM3Compilation o, u8 i_opcode) +void log_opcode (IM3Compilation o, m3opcode_t i_opcode) { i32 depth = o->block.depth; if (i_opcode == c_waOp_end or i_opcode == c_waOp_else) depth--; -# ifdef DEBUG - m3log (compile, "%4d | 0x%02x %s %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth), c_operations [i_opcode].name); -# else - m3log (compile, "%4d | 0x%02x %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth)); -# endif + m3log (compile, "%4d | 0x%02x %s %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth), GetOpInfo(i_opcode)->name); } @@ -437,7 +499,6 @@ void emit_stack_dump (IM3Compilation o) void log_emit (IM3Compilation o, IM3Operation i_operation) { -# ifdef DEBUG OpInfo i = find_operation_info (i_operation); d_m3Log(emit, ""); @@ -446,7 +507,6 @@ void log_emit (IM3Compilation o, IM3Operation i_operation) printf ("%p: %s\n", GetPC (o), i.info->name); } else printf ("not found: %p\n", i_operation); -# endif } #endif // DEBUG diff --git a/Sources/CWasm3/m3_module.c b/Sources/CWasm3/m3_module.c index 849856f..d6ba8d8 100644 --- a/Sources/CWasm3/m3_module.c +++ b/Sources/CWasm3/m3_module.c @@ -28,16 +28,20 @@ void m3_FreeModule (IM3Module i_module) Module_FreeFunctions (i_module); - m3Free (i_module->functions); - //m3Free (i_module->imports); - m3Free (i_module->funcTypes); - m3Free (i_module->dataSegments); - m3Free (i_module->table0); - - // TODO: free importinfo - m3Free (i_module->globals); - - m3Free (i_module); + m3_Free (i_module->functions); + //m3_Free (i_module->imports); + m3_Free (i_module->funcTypes); + m3_Free (i_module->dataSegments); + m3_Free (i_module->table0); + + for (u32 i = 0; i < i_module->numGlobals; ++i) + { + m3_Free (i_module->globals[i].name); + FreeImportInfo(&(i_module->globals[i].import)); + } + m3_Free (i_module->globals); + + m3_Free (i_module); } } @@ -47,8 +51,8 @@ M3Result Module_AddGlobal (IM3Module io_module, IM3Global * o_global, u8 i_typ M3Result result = m3Err_none; _try { u32 index = io_module->numGlobals++; -_ (m3ReallocArray (& io_module->globals, M3Global, io_module->numGlobals, index)); - + io_module->globals = m3_ReallocArray (M3Global, io_module->globals, io_module->numGlobals, index); + _throwifnull(io_module->globals); M3Global * global = & io_module->globals [index]; global->type = i_type; @@ -66,40 +70,75 @@ _ (m3ReallocArray (& io_module->globals, M3Global, io_module->numGlobals, inde M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo) { M3Result result = m3Err_none; + _try { + u32 index = io_module->numFunctions++; -_ (m3ReallocArray (& io_module->functions, M3Function, io_module->numFunctions, index)); + io_module->functions = m3_ReallocArray (M3Function, io_module->functions, io_module->numFunctions, index); - _throwif("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); + _throwifnull (io_module->functions); + _throwif ("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); IM3FuncType ft = io_module->funcTypes [i_typeIndex]; IM3Function func = Module_GetFunction (io_module, index); func->funcType = ft; -#if d_m3EnableStrace >= 2 + +# ifdef DEBUG func->index = index; -#endif +# endif if (i_importInfo and func->numNames == 0) { func->import = * i_importInfo; - func->numNames = 1; func->names[0] = i_importInfo->fieldUtf8; + func->numNames = 1; } - // m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); + m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); } _catch: return result; } +#ifdef DEBUG +void Module_GenerateNames (IM3Module i_module) +{ + for (u32 i = 0; i < i_module->numFunctions; ++i) + { + IM3Function func = & i_module->functions [i]; + + if (func->numNames == 0) + { + char* buff = m3_AllocArray(char, 16); + snprintf(buff, 16, "$func%d", i); + func->names[0] = buff; + func->numNames = 1; + } + } + for (u32 i = 0; i < i_module->numGlobals; ++i) + { + IM3Global global = & i_module->globals [i]; + + if (global->name == NULL) + { + char* buff = m3_AllocArray(char, 16); + snprintf(buff, 16, "$global%d", i); + global->name = buff; + } + } +} +#endif IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) { IM3Function func = NULL; if (i_functionIndex < i_module->numFunctions) + { func = & i_module->functions [i_functionIndex]; + //func->module = i_module; + } return func; } @@ -108,11 +147,16 @@ IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) const char* m3_GetModuleName (IM3Module i_module) { if (!i_module || !i_module->name) - return ""; + return ".unnamed"; return i_module->name; } +void m3_SetModuleName (IM3Module i_module, const char* name) +{ + if (i_module) i_module->name = name; +} + IM3Runtime m3_GetModuleRuntime (IM3Module i_module) { return i_module ? i_module->runtime : NULL; diff --git a/Sources/CWasm3/m3_parse.c b/Sources/CWasm3/m3_parse.c index 369bc45..00e01b0 100644 --- a/Sources/CWasm3/m3_parse.c +++ b/Sources/CWasm3/m3_parse.c @@ -46,27 +46,29 @@ _try { u32 numTypes; _ (ReadLEB_u32 (& numTypes, & i_bytes, i_end)); m3log (parse, "** Type [%d]", numTypes); + _throwif("too many types", numTypes > d_m3MaxSaneTypesCount); + if (numTypes) { // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment) -_ (m3Alloc (& io_module->funcTypes, IM3FuncType, numTypes)); + io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes); + _throwifnull (io_module->funcTypes); io_module->numFuncTypes = numTypes; for (u32 i = 0; i < numTypes; ++i) { i8 form; _ (ReadLEB_i7 (& form, & i_bytes, i_end)); - _throwif (m3Err_wasmMalformed, form != -32); // for WA MVP + _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP u32 numArgs; _ (ReadLEB_u32 (& numArgs, & i_bytes, i_end)); - _throwif ("insane argument count", numArgs > d_m3MaxSaneFunctionArgCount); - + _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount); #if defined(M3_COMPILER_MSVC) - u8 argTypes[d_m3MaxSaneFunctionArgCount]; + u8 argTypes [d_m3MaxSaneFunctionArgRetCount]; #else - u8 argTypes[numArgs]; + u8 argTypes[numArgs+1]; // make ubsan happy #endif for (u32 a = 0; a < numArgs; ++a) { @@ -80,8 +82,7 @@ _ (NormalizeType (& argType, wasmType)); u32 numRets; _ (ReadLEB_u32 (& numRets, & i_bytes, i_end)); - - _throwif ("insane returns count", numRets > d_m3MaxSaneFunctionArgCount); + _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount); _ (AllocFuncType (& ftype, numRets + numArgs)); ftype->numArgs = numArgs; @@ -96,10 +97,11 @@ _ (NormalizeType (& retType, wasmType)); ftype->types[r] = retType; } - memcpy(ftype->types + numRets, argTypes, numArgs); m3log (parse, " type %2d: %s", i, SPrintFuncTypeSignature (ftype)); + memcpy (ftype->types + numRets, argTypes, numArgs); m3log (parse, " type %2d: %s", i, SPrintFuncTypeSignature (ftype)); Environment_AddFuncType (io_module->environment, & ftype); io_module->funcTypes [i] = ftype; + ftype = NULL; // ownership transfered to environment } } @@ -107,8 +109,9 @@ _ (NormalizeType (& retType, wasmType)); if (result) { - m3Free (ftype); - m3Free (io_module->funcTypes); + m3_Free (ftype); + // FIX: M3FuncTypes in the table are leaked + m3_Free (io_module->funcTypes); io_module->numFuncTypes = 0; } @@ -123,6 +126,10 @@ M3Result ParseSection_Function (IM3Module io_module, bytes_t i_bytes, cbytes_t u32 numFunctions; _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Function [%d]", numFunctions); + _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount); + + // TODO: prealloc functions + for (u32 i = 0; i < numFunctions; ++i) { u32 funcTypeIndex; @@ -144,6 +151,8 @@ M3Result ParseSection_Import (IM3Module io_module, bytes_t i_bytes, cbytes_t i u32 numImports; _ (ReadLEB_u32 (& numImports, & i_bytes, i_end)); m3log (parse, "** Import [%d]", numImports); + _throwif("too many imports", numImports > d_m3MaxSaneImportsCount); + for (u32 i = 0; i < numImports; ++i) { u8 importKind; @@ -162,7 +171,7 @@ _ (ReadLEB_u32 (& typeIndex, & i_bytes, i_end)) _ (Module_AddFunction (io_module, typeIndex, & import)) import = clearImport; - io_module->numImports++; + io_module->numFuncImports++; } break; @@ -211,13 +220,15 @@ _ (Module_AddGlobal (io_module, & global, type, isMutable, true /* M3Result ParseSection_Export (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end) { M3Result result = m3Err_none; + const char * utf8 = NULL; u32 numExports; _ (ReadLEB_u32 (& numExports, & i_bytes, i_end)); m3log (parse, "** Export [%d]", numExports); + _throwif("too many exports", numExports > d_m3MaxSaneExportsCount); + for (u32 i = 0; i < numExports; ++i) { - const char * utf8; u8 exportKind; u32 index; @@ -228,19 +239,28 @@ _ (ReadLEB_u32 (& index, & i_bytes, i_end)); if (exportKind == d_externalKind_function) { _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions); - u16 numNames = io_module->functions [index].numNames; - if (numNames < d_m3MaxDuplicateFunctionImpl) + IM3Function func = &(io_module->functions [index]); + if (func->numNames < d_m3MaxDuplicateFunctionImpl) { - io_module->functions [index].numNames++; - io_module->functions [index].names[numNames] = utf8; - utf8 = NULL; // ownership transfered to M3Function + func->names[func->numNames++] = utf8; + utf8 = NULL; // ownership transferred to M3Function } } + else if (exportKind == d_externalKind_global) + { + _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals); + IM3Global global = &(io_module->globals [index]); + m3_Free (global->name); + global->name = utf8; + utf8 = NULL; // ownership transferred to M3Global + } - m3Free (utf8); + m3_Free (utf8); } - _catch: return result; +_catch: + m3_Free (utf8); + return result; } @@ -272,9 +292,9 @@ M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_ #else M3Compilation compilation; #endif - compilation = (M3Compilation){ NULL, io_module, * io_bytes, i_end }; + compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end }; - result = Compile_BlockStatements (& compilation); + result = CompileBlockStatements (& compilation); * io_bytes = compilation.wasm; @@ -291,6 +311,8 @@ M3Result ParseSection_Element (IM3Module io_module, bytes_t i_bytes, cbytes_t _throwif ("error parsing Element section", result); + _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments); + io_module->elementSection = i_bytes; io_module->elementSectionEnd = i_end; io_module->numElementSegments = numSegments; @@ -306,13 +328,15 @@ M3Result ParseSection_Code (M3Module * io_module, bytes_t i_bytes, cbytes_t i_ u32 numFunctions; _ (ReadLEB_u32 (& numFunctions, & i_bytes, i_end)); m3log (parse, "** Code [%d]", numFunctions); - if (numFunctions != io_module->numFunctions - io_module->numImports) + if (numFunctions != io_module->numFunctions - io_module->numFuncImports) { _throw ("mismatched function count in code section"); } for (u32 f = 0; f < numFunctions; ++f) { + const u8 * start = i_bytes; + u32 size; _ (ReadLEB_u32 (& size, & i_bytes, i_end)); @@ -323,8 +347,7 @@ _ (ReadLEB_u32 (& size, & i_bytes, i_end)); if (i_bytes <= i_end) { - const u8 * start = ptr; - + /* u32 numLocalBlocks; _ (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end)); m3log (parse, " code size: %-4d", size); @@ -342,14 +365,15 @@ _ (NormalizeType (& normalType, wasmType)); numLocals += varCount; m3log (parse, " %2d locals; type: '%s'", varCount, c_waTypes [normalType]); } + */ - IM3Function func = Module_GetFunction (io_module, f + io_module->numImports); + IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports); func->module = io_module; func->wasm = start; func->wasmEnd = i_bytes; //func->ownsWasmCode = io_module->hasWasmCodeCopy; - func->numLocals = numLocals; +// func->numLocals = numLocals; } else _throw (m3Err_wasmSectionOverrun); } @@ -371,8 +395,10 @@ M3Result ParseSection_Data (M3Module * io_module, bytes_t i_bytes, cbytes_t i_ u32 numDataSegments; _ (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end)); m3log (parse, "** Data [%d]", numDataSegments); -_ (m3Alloc (& io_module->dataSegments, M3DataSegment, numDataSegments)); + _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments); + io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments); + _throwifnull(io_module->dataSegments); io_module->numDataSegments = numDataSegments; for (u32 i = 0; i < numDataSegments; ++i) @@ -391,6 +417,8 @@ _ (ReadLEB_u32 (& segment->size, & i_bytes, i_end)); segment->data = i_bytes; m3log (parse, " segment [%u] memory: %u; expr-size: %d; size: %d", i, segment->memoryRegion, segment->initExprSize, segment->size); i_bytes += segment->size; + + _throwif("data segment underflow", i_bytes > i_end); } _catch: @@ -423,6 +451,8 @@ M3Result ParseSection_Global (M3Module * io_module, bytes_t i_bytes, cbytes_t u32 numGlobals; _ (ReadLEB_u32 (& numGlobals, & i_bytes, i_end)); m3log (parse, "** Global [%d]", numGlobals); + _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount); + for (u32 i = 0; i < numGlobals; ++i) { i8 waType; @@ -456,7 +486,7 @@ _ (Read_utf8 (& name, & i_bytes, i_end)); if (strcmp (name, "name") != 0) i_bytes = i_end; - m3Free (name); + m3_Free (name); while (i_bytes < i_end) { @@ -472,6 +502,8 @@ _ (ReadLEB_u32 (& payloadLength, & i_bytes, i_end)); u32 numNames; _ (ReadLEB_u32 (& numNames, & i_bytes, i_end)); + _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount); + for (u32 i = 0; i < numNames; ++i) { u32 index; @@ -480,16 +512,17 @@ _ (Read_utf8 (& name, & i_bytes, i_end)); if (index < io_module->numFunctions) { - if (io_module->functions [index].numNames == 0) + IM3Function func = &(io_module->functions [index]); + if (func->numNames == 0) { - io_module->functions [index].numNames = 1; - io_module->functions [index].names[0] = name; m3log (parse, " naming function%5d: %s", index, name); + func->names[0] = name; m3log (parse, " naming function%5d: %s", index, name); + func->numNames = 1; name = NULL; // transfer ownership } // else m3log (parse, "prenamed: %s", io_module->functions [index].name); } - m3Free (name); + m3_Free (name); } } @@ -544,12 +577,12 @@ M3Result ParseModuleSection (M3Module * o_module, u8 i_sectionType, bytes_t i_ M3Result m3_ParseModule (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes) { - M3Result result; + M3Result result; m3log (parse, "load module: %d bytes", i_numBytes); IM3Module module; _try { -_ (m3Alloc (& module, M3Module, 1)); - + module = m3_AllocStruct (M3Module); + _throwifnull (module); module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes); module->startFunction = -1; //module->hasWasmCodeCopy = false; @@ -567,33 +600,32 @@ _ (Read_u32 (& version, & pos, end)); _throwif (m3Err_wasmMalformed, magic != 0x6d736100); _throwif (m3Err_incompatibleWasmVersion, version != 1); - m3log (parse, "found magic + version"); - u8 previousSection = 0; + + static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder + u8 expectedSection = 0; while (pos < end) { u8 section; _ (ReadLEB_u7 (& section, & pos, end)); - if (section > previousSection or // from the spec: sections must appear in order - section == 0 or // custom section - (section == 12 and previousSection == 9) or // if present, DataCount goes after Element - (section == 10 and previousSection == 12)) // and before Code - { - u32 sectionLength; -_ (ReadLEB_u32 (& sectionLength, & pos, end)); - _throwif(m3Err_wasmMalformed, pos + sectionLength > end); -_ (ParseModuleSection (module, section, pos, sectionLength)); + if (section != 0) { + // Ensure sections appear only once and in order + while (sectionsOrder[expectedSection++] != section) { + _throwif(m3Err_misorderedWasmSection, expectedSection >= 12); + } + } - pos += sectionLength; + u32 sectionLength; +_ (ReadLEB_u32 (& sectionLength, & pos, end)); + _throwif(m3Err_wasmMalformed, pos + sectionLength > end); - if (section) - previousSection = section; - } - else _throw (m3Err_misorderedWasmSection); +_ (ParseModuleSection (module, section, pos, sectionLength)); + + pos += sectionLength; } - } _catch: +} _catch: if (result) { diff --git a/Tests/CWasm3Tests/CWasm3Tests.swift b/Tests/CWasm3Tests/CWasm3Tests.swift index e949dcc..ce552dd 100644 --- a/Tests/CWasm3Tests/CWasm3Tests.swift +++ b/Tests/CWasm3Tests/CWasm3Tests.swift @@ -257,10 +257,15 @@ private func importedAdd( return UnsafeRawPointer(m3Err_trapUnreachable) } - let first = stackPointer.load(as: Int32.self) - // wasm3 always aligns the stack to 64 bits - let second = stackPointer.load(fromByteOffset: MemoryLayout.stride, as: Int64.self) - let sum = Int32(Int64(first) + second) + // Arguments and return values are passed in and out through the stack pointer _sp. + // Placeholder return value slots are first and arguments after. So, the first + // argument is at _sp [numReturns]. + // Return values should be written into _sp [0] to _sp [num_returns - 1]. + // Wasm3 always aligns the stack to 64 bits. + let offset = MemoryLayout.stride + let first = stackPointer.load(fromByteOffset: offset, as: Int32.self) + let second = stackPointer.load(fromByteOffset: offset + offset, as: Int32.self) + let sum = first + second stackPointer.storeBytes(of: sum, as: Int32.self) return nil diff --git a/VERSION b/VERSION index 4ea4ea4..b043aa6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.4.9 +v0.5.0 diff --git a/update-wasm3 b/update-wasm3 index a613f57..6a4f645 100755 --- a/update-wasm3 +++ b/update-wasm3 @@ -21,8 +21,11 @@ function replace_wasm3_with_branch() { git clone --depth 1 --branch $1 https://github.com/wasm3/wasm3.git &>/dev/null find wasm3/source -regex ".*\.c" -exec cp {} ../Sources/CWasm3/ \; - find wasm3/source | grep -E "source/(m3|was).*\.h" | xargs -I INPUT_FILE cp INPUT_FILE ../Sources/CWasm3/include/ + find wasm3/source | \ + grep -E "(source/|source/extensions/)(m3|was).*\.h" | \ + xargs -I INPUT_FILE cp INPUT_FILE ../Sources/CWasm3/include/ rm ../Sources/CWasm3/include/m3.h &>/dev/null # remove this deprecated header + rm ../Sources/CWasm3/include/m3_api_defs.h &>/dev/null # remove this deprecated header popd >/dev/null }