From 0821f8a695d11880587598da94c6919d4023bf24 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Fri, 12 Mar 2021 14:03:33 +0200 Subject: [PATCH] Update to v0.4.9 --- src/m3_api_defs.h | 8 +- src/m3_api_libc.c | 53 ++-- src/m3_bind.c | 14 +- src/m3_code.c | 110 +++++++ src/m3_code.h | 30 ++ src/m3_compile.c | 32 +- src/m3_compile.h | 6 +- src/m3_config.h | 19 +- src/m3_config_platforms.h | 5 +- src/m3_core.c | 105 ++++++- src/m3_core.h | 32 +- src/m3_emit.c | 3 + src/m3_env.c | 434 +++++++++++++++++--------- src/m3_env.h | 49 +-- src/m3_exec.c | 445 -------------------------- src/m3_exec.h | 641 +++++++++++++++++++++++++++++++++----- src/m3_exec_defs.h | 21 +- src/m3_info.c | 27 +- src/m3_info.h | 6 +- src/m3_math_utils.h | 28 +- src/m3_module.c | 48 ++- src/m3_optimize.c | 12 - src/m3_parse.c | 30 +- src/wasm3.h | 75 ++++- 24 files changed, 1375 insertions(+), 858 deletions(-) delete mode 100644 src/m3_optimize.c diff --git a/src/m3_api_defs.h b/src/m3_api_defs.h index 33d028e..1b9c159 100644 --- a/src/m3_api_defs.h +++ b/src/m3_api_defs.h @@ -18,7 +18,13 @@ #define m3ApiGetArg(TYPE, NAME) TYPE NAME = * ((TYPE *) (_sp++)); #define m3ApiGetArgMem(TYPE, NAME) TYPE NAME = (TYPE)m3ApiOffsetToPtr(* ((u32 *) (_sp++))); -#define m3ApiRawFunction(NAME) const void * NAME (IM3Runtime runtime, uint64_t * _sp, void * _mem, void * userdata) +#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; } diff --git a/src/m3_api_libc.c b/src/m3_api_libc.c index d34e967..c0ae406 100644 --- a/src/m3_api_libc.c +++ b/src/m3_api_libc.c @@ -39,6 +39,8 @@ m3ApiRawFunction(m3_libc_memset) m3ApiGetArg (int32_t, i_value) m3ApiGetArg (int32_t, i_size) + m3ApiCheckMem(i_ptr, i_size); + u32 result = m3ApiPtrToOffset(memset (i_ptr, i_value, i_size)); m3ApiReturn(result); } @@ -51,15 +53,33 @@ m3ApiRawFunction(m3_libc_memmove) m3ApiGetArgMem (void*, i_src) m3ApiGetArg (int32_t, i_size) + m3ApiCheckMem(o_dst, i_size); + m3ApiCheckMem(i_src, i_size); + u32 result = m3ApiPtrToOffset(memmove (o_dst, i_src, i_size)); m3ApiReturn(result); } -m3ApiRawFunction(m3_libc_clock) +m3ApiRawFunction(m3_libc_print) { m3ApiReturnType (uint32_t) - m3ApiReturn(clock()); + m3ApiGetArgMem (void*, i_ptr) + m3ApiGetArg (uint32_t, i_size) + + m3ApiCheckMem(i_ptr, i_size); + + fwrite(i_ptr, i_size, 1, stdout); + fflush(stdout); + + m3ApiReturn(i_size); +} + +m3ApiRawFunction(m3_libc_clock_ms) +{ + m3ApiReturnType (uint32_t) + + m3ApiReturn(clock() / (CLOCKS_PER_SEC/1000)); } static @@ -76,33 +96,11 @@ m3ApiRawFunction(m3_spectest_dummy) m3ApiSuccess(); } -m3ApiRawFunction(m3_wasm3_raw_sum) -{ - m3ApiReturnType (int64_t) - m3ApiGetArg (int32_t, val1) - m3ApiGetArg (int32_t, val2) - m3ApiGetArg (int32_t, val3) - m3ApiGetArg (int32_t, val4) - - m3ApiReturn(val1 + val2 + val3 + val4); - - m3ApiSuccess(); -} - -/* TODO: implement direct native function calls (using libffi?) -i64 m3_wasm3_native_sum(i32 val1, i32 val2, i32 val3, i32 val4) -{ - return val1 + val2 + val3 + val4; -} -*/ - - M3Result m3_LinkSpecTest (IM3Module module) { M3Result result = m3Err_none; const char* spectest = "spectest"; - const char* wasm3 = "wasm3"; _ (SuppressLookupFailure (m3_LinkRawFunction (module, spectest, "print", "v()", &m3_spectest_dummy))); _ (SuppressLookupFailure (m3_LinkRawFunction (module, spectest, "print_i32", "v(i)", &m3_spectest_dummy))); @@ -112,10 +110,6 @@ _ (SuppressLookupFailure (m3_LinkRawFunction (module, spectest, "print_f64", _ (SuppressLookupFailure (m3_LinkRawFunction (module, spectest, "print_i32_f32", "v(if)", &m3_spectest_dummy))); _ (SuppressLookupFailure (m3_LinkRawFunction (module, spectest, "print_i64_f64", "v(IF)", &m3_spectest_dummy))); - -_ (SuppressLookupFailure (m3_LinkRawFunction (module, wasm3, "raw_sum", "I(iiii)", &m3_wasm3_raw_sum))); -//_ (SuppressLookupFailure (m3_LinkCFunction (module, wasm3, "native_sum", "I(iiii)", &m3_wasm3_native_sum))); - _catch: return result; } @@ -127,12 +121,13 @@ M3Result m3_LinkLibC (IM3Module module) const char* env = "env"; +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_debug", "i(*i)", &m3_libc_print))); _ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_memset", "*(*ii)", &m3_libc_memset))); _ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_memmove", "*(**i)", &m3_libc_memmove))); _ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_memcpy", "*(**i)", &m3_libc_memmove))); // just alias of memmove _ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_abort", "v()", &m3_libc_abort))); _ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_exit", "v(i)", &m3_libc_exit))); -_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "_clock", "i()", &m3_libc_clock))); +_ (SuppressLookupFailure (m3_LinkRawFunction (module, env, "clock_ms", "i()", &m3_libc_clock_ms))); _catch: return result; diff --git a/src/m3_bind.c b/src/m3_bind.c index 4df95d6..86ea9f5 100644 --- a/src/m3_bind.c +++ b/src/m3_bind.c @@ -43,7 +43,9 @@ _try { _throwif (m3Err_malformedFunctionSignature, maxNumArgs < 0); _throwif ("insane argument count", maxNumArgs > d_m3MaxSaneFunctionArgCount); -_ (AllocFuncType (& funcType, maxNumArgs)); + const unsigned umaxNumArgs = (unsigned)maxNumArgs; + +_ (AllocFuncType (& funcType, umaxNumArgs)); bool parsingArgs = false; while (* sig) @@ -65,19 +67,19 @@ _ (AllocFuncType (& funcType, maxNumArgs)); _throwif ("unknown argument type char", c_m3Type_unknown == type); if (type == c_m3Type_none) - continue; + continue; if (not parsingArgs) { _throwif ("malformed function signature; too many return types", funcType->numRets >= 1); - funcType->types [funcType->numRets++] = type; + d_FuncRetType(funcType, funcType->numRets++) = type; } else { - _throwif (m3Err_malformedFunctionSignature, funcType->numArgs >= maxNumArgs); // forgot trailing ')' ? + _throwif (m3Err_malformedFunctionSignature, funcType->numArgs >= umaxNumArgs); // forgot trailing ')' ? - funcType->types [funcType->numRets + funcType->numArgs++] = type; + d_FuncArgType(funcType, funcType->numArgs++) = type; } } @@ -148,7 +150,7 @@ M3Result FindAndLinkFunction (IM3Module io_module, ccstr_t i_functionName, ccstr_t i_signature, voidptr_t i_function, - voidptr_t i_userdata) + voidptr_t i_userdata) { M3Result result = m3Err_functionLookupFailed; diff --git a/src/m3_code.c b/src/m3_code.c index 4619948..f625caa 100644 --- a/src/m3_code.c +++ b/src/m3_code.c @@ -7,6 +7,16 @@ #include "m3_code.h" +#if d_m3RecordBacktraces +// Code mapping page ops + +M3CodeMappingPage * NewCodeMappingPage (u32 i_minCapacity); +void FreeCodeMappingPage (M3CodeMappingPage * i_page); +#endif // d_m3RecordBacktraces + +//--------------------------------------------------------------------------------------------------------------------------------- + + IM3CodePage NewCodePage (u32 i_minNumLines) { static u32 s_sequence = 0; @@ -23,6 +33,16 @@ IM3CodePage NewCodePage (u32 i_minNumLines) page->info.sequence = ++s_sequence; page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t); +#if d_m3RecordBacktraces + page->info.mapping = NewCodeMappingPage (page->info.numLines); + if (!page->info.mapping) + { + m3Free (page); + return NULL; + } + page->info.mapping->basePC = GetPageStartPC(page); +#endif // d_m3RecordBacktraces + m3log (runtime, "new page: %p; seq: %d; bytes: %d; lines: %d", GetPagePC (page), page->info.sequence, pageSize, page->info.numLines); } @@ -39,6 +59,9 @@ void FreeCodePages (IM3CodePage * io_list) m3log (code, "free page: %d; %p; util: %3.1f%%", page->info.sequence, page, 100. * page->info.lineIndex / page->info.numLines); IM3CodePage next = page->info.next; +#if d_m3RecordBacktraces + FreeCodeMappingPage (page->info.mapping); +#endif // d_m3RecordBacktraces m3Free (page); page = next; } @@ -79,6 +102,20 @@ void EmitWord64 (IM3CodePage i_page, const u64 i_word) } +#if d_m3RecordBacktraces +void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset) +{ + M3CodeMappingPage * page = i_page->info.mapping; + d_m3Assert (page->size < page->capacity); + + M3CodeMapEntry * entry = & page->entries[page->size++]; + pc_t pc = GetPagePC (i_page); + + entry->pcOffset = pc - page->basePC; + entry->moduleOffset = i_moduleOffset; +} +#endif // d_m3RecordBacktraces + pc_t GetPageStartPC (IM3CodePage i_page) { return & i_page->code [0]; @@ -143,3 +180,76 @@ IM3CodePage GetEndCodePage (IM3CodePage i_list) return end; } + +#if d_m3RecordBacktraces +bool ContainsPC (IM3CodePage i_page, pc_t i_pc) +{ + return GetPageStartPC (i_page) <= i_pc && i_pc < GetPagePC (i_page); +} + + +bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset) +{ + M3CodeMappingPage * mapping = i_page->info.mapping; + + u32 pcOffset = i_pc - mapping->basePC; + + u32 left = 0; + u32 right = mapping->size; + + while (left < right) + { + u32 mid = left + (right - left) / 2; + + if (mapping->entries[mid].pcOffset < pcOffset) + { + left = mid + 1; + } + else if (mapping->entries[mid].pcOffset > pcOffset) + { + right = mid; + } + else + { + *o_moduleOffset = mapping->entries[mid].moduleOffset; + return true; + } + } + + // Getting here means left is now one more than the element we want. + if (left > 0) + { + left--; + *o_moduleOffset = mapping->entries[left].moduleOffset; + return true; + } + else return false; +} +#endif // d_m3RecordBacktraces + +//--------------------------------------------------------------------------------------------------------------------------------- + + +#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/src/m3_code.h b/src/m3_code.h index 32116c1..44bd87e 100644 --- a/src/m3_code.h +++ b/src/m3_code.h @@ -32,6 +32,9 @@ pc_t GetPagePC (IM3CodePage i_page); void EmitWord_impl (IM3CodePage i_page, void* i_word); void EmitWord32 (IM3CodePage i_page, u32 i_word); void EmitWord64 (IM3CodePage i_page, u64 i_word); +# if d_m3RecordBacktraces +void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset); +# endif // d_m3RecordBacktraces void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); IM3CodePage PopCodePage (IM3CodePage * io_list); @@ -39,12 +42,39 @@ IM3CodePage PopCodePage (IM3CodePage * io_list); IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid +# if d_m3RecordBacktraces +bool ContainsPC (IM3CodePage i_page, pc_t i_pc); +bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset); +# endif // d_m3RecordBacktraces + # ifdef DEBUG void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); # endif #define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) +//--------------------------------------------------------------------------------------------------------------------------------- + +# if d_m3RecordBacktraces + +typedef struct M3CodeMapEntry +{ + u32 pcOffset; + u32 moduleOffset; +} +M3CodeMapEntry; + +typedef struct M3CodeMappingPage +{ + pc_t basePC; + u32 size; + u32 capacity; + M3CodeMapEntry entries []; +} +M3CodeMappingPage; + +# endif // d_m3RecordBacktraces + d_m3EndExternC #endif // m3_code_h diff --git a/src/m3_compile.c b/src/m3_compile.c index 0066d94..03dd6ff 100644 --- a/src/m3_compile.c +++ b/src/m3_compile.c @@ -938,7 +938,7 @@ M3Result Compile_Const_f32 (IM3Compilation o, m3opcode_t i_opcode) union { u32 u; f32 f; } value = { 0 }; -_ (Read_f32 (& value.f, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (const f32 = %f)", get_indention_string (o), value.f); +_ (Read_f32 (& value.f, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (const f32 = %" PRIf32 ")", get_indention_string (o), value.f); _ (PushConst (o, value.u, c_m3Type_f32)); _catch: return result; @@ -951,7 +951,7 @@ M3Result Compile_Const_f64 (IM3Compilation o, m3opcode_t i_opcode) union { u64 u; f64 f; } value = { 0 }; -_ (Read_f64 (& value.f, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (const f64 = %lf)", get_indention_string (o), value.f); +_ (Read_f64 (& value.f, & o->wasm, o->wasmEnd)); m3log (compile, d_indent " (const f64 = %" PRIf64 ")", get_indention_string (o), value.f); _ (PushConst (o, value.u, c_m3Type_f64)); _catch: return result; @@ -1394,7 +1394,7 @@ _ (ReadLEB_u32 (& functionIndex, & o->wasm, o->wasmEnd)); if (function) { m3log (compile, d_indent " (func= '%s'; args= %d)", - get_indention_string (o), GetFunctionName (function), function->funcType->numArgs); + get_indention_string (o), m3_GetFunctionName (function), function->funcType->numArgs); if (function->module) { // OPTZ: could avoid arg copy when args are already sequential and at top @@ -1422,7 +1422,7 @@ _ (EmitOp (o, op)); } else { - result = ErrorCompile (m3Err_functionImportMissing, o, "'%s.%s'", GetFunctionImportModuleName (function), GetFunctionName (function)); + result = ErrorCompile (m3Err_functionImportMissing, o, "'%s.%s'", GetFunctionImportModuleName (function), m3_GetFunctionName (function)); } } else result = m3Err_functionLookupFailed; @@ -1509,13 +1509,10 @@ _ (ReadLebSigned (& type, 33, & o->wasm, o->wasmEnd)); _ (NormalizeType (&valueType, type)); m3log (compile, d_indent " (type: %s)", get_indention_string (o), c_waTypes [valueType]); *o_blockType = o->module->environment->retFuncTypes[valueType]; } - else if (type < o->module->numFuncTypes) - { - *o_blockType = o->module->funcTypes[type]; m3log (compile, d_indent " (type: %s)", get_indention_string (o), SPrintFuncTypeSignature (*o_blockType)); - } else { - return "func type out of bounds"; + _throwif("func type out of bounds", type >= o->module->numFuncTypes); + *o_blockType = o->module->funcTypes[type]; m3log (compile, d_indent " (type: %s)", get_indention_string (o), SPrintFuncTypeSignature (*o_blockType)); } _catch: return result; } @@ -2171,6 +2168,7 @@ M3Result Compile_BlockStatements (IM3Compilation o) while (o->wasm < o->wasmEnd) { emit_stack_dump (o); + o->lastOpcodeStart = o->wasm; m3opcode_t opcode = * (o->wasm++); log_opcode (o, opcode); #ifndef d_m3CompileExtendedOpcode @@ -2179,12 +2177,14 @@ M3Result Compile_BlockStatements (IM3Compilation o) } #endif - M3Compiler compiler = GetOpInfo(opcode)->compiler; + IM3OpInfo opinfo = GetOpInfo(opcode); + _throwif(m3Err_unknownOpcode, opinfo == NULL); - if (not compiler) - compiler = Compile_Operator; - - result = (* compiler) (o, opcode); + if (opinfo->compiler) { + result = (* opinfo->compiler) (o, opcode); + } else { + result = Compile_Operator(o, opcode); + } o->previousOpcode = opcode; // m3logif (stack, dump_type_stack (o)) @@ -2197,7 +2197,7 @@ M3Result Compile_BlockStatements (IM3Compilation o) if (opcode == c_waOp_end or opcode == c_waOp_else) break; } - +_catch: return result; } @@ -2341,7 +2341,7 @@ M3Result Compile_Function (IM3Function io_function) IM3FuncType ft = io_function->funcType; M3Result result = m3Err_none; m3log (compile, "compiling: '%s'; wasm-size: %d; numArgs: %d; return: %s", - io_function->name, (u32) (io_function->wasmEnd - io_function->wasm), GetFunctionNumArgs (io_function), c_waTypes [GetSingleRetType(ft)]); + m3_GetFunctionName(io_function), (u32) (io_function->wasmEnd - io_function->wasm), GetFunctionNumArgs (io_function), c_waTypes [GetSingleRetType(ft)]); IM3Runtime runtime = io_function->module->runtime; IM3Compilation o = & runtime->compilation; diff --git a/src/m3_compile.h b/src/m3_compile.h index 40ed07f..b6fd4f5 100644 --- a/src/m3_compile.h +++ b/src/m3_compile.h @@ -35,12 +35,15 @@ typedef struct M3FuncType u32 numRets; u32 numArgs; - u8 types[]; // returns, then args + u8 types[]; // returns, then args } M3FuncType; 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 @@ -83,6 +86,7 @@ typedef struct bytes_t wasm; bytes_t wasmEnd; + bytes_t lastOpcodeStart; M3CompilationScope block; diff --git a/src/m3_config.h b/src/m3_config.h index 20aa48d..f2a80c1 100644 --- a/src/m3_config.h +++ b/src/m3_config.h @@ -32,8 +32,8 @@ # define d_m3MaxConstantTableSize 120 # endif -# ifndef d_m3LogOutput -# define d_m3LogOutput 1 +# ifndef d_m3MaxDuplicateFunctionImpl +# define d_m3MaxDuplicateFunctionImpl 3 # endif # ifndef d_m3VerboseLogs @@ -57,6 +57,9 @@ # define d_m3ProfilerSlotMask 0xFFFF # endif +# ifndef d_m3RecordBacktraces +# define d_m3RecordBacktraces 0 +# endif // profiling and tracing ------------------------------------------------------ @@ -69,7 +72,9 @@ # endif # ifndef d_m3EnableStrace -# define d_m3EnableStrace 0 // trace exported function calls +# define d_m3EnableStrace 0 // 1 - trace exported function calls + // 2 - trace all calls (structured) - requires DEBUG + // 3 - all calls + loops + memory operations # endif @@ -99,18 +104,10 @@ # define d_m3LogCodePages 0 // dump metacode pages when released # endif -# ifndef d_m3LogExec -# define d_m3LogExec 0 // low-level interpreter specific logs -# endif - # ifndef d_m3LogRuntime # define d_m3LogRuntime 0 // higher-level runtime information # endif -# ifndef d_m3LogStackTrace -# define d_m3LogStackTrace 0 // dump the call stack when traps occur -# endif - # ifndef d_m3LogNativeStack # define d_m3LogNativeStack 0 // track the memory usage of the C-stack # endif diff --git a/src/m3_config_platforms.h b/src/m3_config_platforms.h index d212ce2..05c8e2d 100644 --- a/src/m3_config_platforms.h +++ b/src/m3_config_platforms.h @@ -353,6 +353,8 @@ typedef int8_t i8; # if defined (M3_COMPILER_MSVC) # define vectorcall // For MSVC, better not to specify any call convention +# elif defined(__MINGW32__) +# define vectorcall # elif defined(WIN32) # define vectorcall __vectorcall # elif defined (ESP8266) @@ -404,9 +406,6 @@ 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_m3LogOutput -# define d_m3LogOutput 0 -# endif # ifndef d_m3VerboseLogs # define d_m3VerboseLogs 0 # endif diff --git a/src/m3_core.c b/src/m3_core.c index 32ad972..1b6bf84 100644 --- a/src/m3_core.c +++ b/src/m3_core.c @@ -9,9 +9,10 @@ #include "wasm3.h" #include "m3_core.h" +#include "m3_env.h" void m3_Abort(const char* message) { -#if d_m3LogOutput +#ifdef DEBUG fprintf(stderr, "Error: %s\n", message); #endif abort(); @@ -119,7 +120,7 @@ M3Result m3_Malloc (void ** o_ptr, size_t i_size) void m3_Free (void ** io_ptr) { -// if (i_ptr) printf("== free %p\n", i_ptr); +// if (io_ptr) printf("== free %p\n", io_ptr); free (* io_ptr); * io_ptr = NULL; } @@ -141,7 +142,7 @@ M3Result m3_Realloc (void ** io_ptr, size_t i_newSize, size_t i_oldSize) } else result = m3Err_mallocFailed; -// printf("== realloc %p -> %p => %d\n", i_ptr, ptr, (u32) i_newSize); +// printf("== realloc %p -> %p => %d\n", io_ptr, io_ptr, (u32) i_newSize); } return result; @@ -489,3 +490,101 @@ M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) return result; } + +#if d_m3RecordBacktraces +u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc) +{ + // walk the code pages + IM3CodePage curr = i_runtime->pagesOpen; + bool pageFound = false; + + while (curr) + { + if (ContainsPC (curr, i_pc)) + { + pageFound = true; + break; + } + curr = curr->info.next; + } + + if (!pageFound) + { + curr = i_runtime->pagesFull; + while (curr) + { + if (ContainsPC (curr, i_pc)) + { + pageFound = true; + break; + } + curr = curr->info.next; + } + } + + if (pageFound) + { + u32 result = 0; + + bool pcFound = MapPCToOffset (curr, i_pc, & result); + d_m3Assert (pcFound); + + return result; + } + else return 0; +} + + +void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) +{ + // don't try to push any more frames if we've already had an alloc failure + if (UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) + return; + + M3BacktraceFrame * newFrame; + m3Alloc ((void **) & newFrame, M3BacktraceFrame, 1); + + if (!newFrame) + { + io_runtime->backtrace.lastFrame = M3_BACKTRACE_TRUNCATED; + return; + } + + newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc); + + if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame) + io_runtime->backtrace.frames = newFrame; + else + io_runtime->backtrace.lastFrame->next = newFrame; + io_runtime->backtrace.lastFrame = newFrame; +} + + +void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function) +{ + // If we've had an alloc failure then the last frame doesn't refer to the + // frame we want to fill in the function info for. + if (UNLIKELY (io_runtime->backtrace.lastFrame == M3_BACKTRACE_TRUNCATED)) + return; + + if (!io_runtime->backtrace.lastFrame) + return; + + io_runtime->backtrace.lastFrame->function = i_function; +} + + +void ClearBacktrace (IM3Runtime io_runtime) +{ + M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames; + while (currentFrame) + { + M3BacktraceFrame * nextFrame = currentFrame->next; + m3Free (currentFrame); + currentFrame = nextFrame; + } + + io_runtime->backtrace.frames = NULL; + io_runtime->backtrace.lastFrame = NULL; +} +#endif // d_m3RecordBacktraces diff --git a/src/m3_core.h b/src/m3_core.h index 56439ec..10b4d01 100644 --- a/src/m3_core.h +++ b/src/m3_core.h @@ -43,6 +43,9 @@ typedef uint8_t u8; typedef int8_t i8; #endif // d_m3ShortTypesDefined +#define PRIf32 "f" +#define PRIf64 "lf" + typedef const void * m3ret_t; typedef const void * voidptr_t; typedef const char * cstr_t; @@ -65,7 +68,7 @@ typedef m3slot_t * m3stack_t; typedef const void * const cvptr_t; -# if d_m3LogOutput && defined (DEBUG) +# if defined (DEBUG) # define d_m3Log(CATEGORY, FMT, ...) printf (" %8s | " FMT, #CATEGORY, ##__VA_ARGS__); @@ -111,12 +114,6 @@ const void * const cvptr_t; # define m3log_runtime(...) {} # endif -# if d_m3LogExec -# define m3log_exec(CATEGORY, FMT, ...) d_m3Log(CATEGORY, FMT, ##__VA_ARGS__) -# else -# define m3log_exec(...) {} -# endif - # define m3log(CATEGORY, FMT, ...) m3log_##CATEGORY (CATEGORY, FMT "\n", ##__VA_ARGS__) # else # define d_m3Log(CATEGORY, FMT, ...) {} @@ -142,15 +139,20 @@ typedef struct M3MemoryHeader } M3MemoryHeader; +struct M3CodeMappingPage; typedef struct M3CodePageHeader { - struct M3CodePage * next; + struct M3CodePage * next; - u32 lineIndex; - u32 numLines; - u32 sequence; // this is just used for debugging; could be removed - u32 usageCount; + u32 lineIndex; + u32 numLines; + u32 sequence; // this is just used for debugging; could be removed + u32 usageCount; + +# if d_m3RecordBacktraces + struct M3CodeMappingPage * mapping; +# endif // d_m3RecordBacktraces } M3CodePageHeader; @@ -240,6 +242,12 @@ size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); +# if d_m3RecordBacktraces +void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc); +void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function); +void ClearBacktrace (IM3Runtime io_runtime); +# endif + d_m3EndExternC #endif // m3_core_h diff --git a/src/m3_emit.c b/src/m3_emit.c index 4b7a52c..46d7824 100644 --- a/src/m3_emit.c +++ b/src/m3_emit.c @@ -62,6 +62,9 @@ M3Result EmitOp (IM3Compilation o, IM3Operation i_operation) if (not result) { if (d_m3LogEmit) log_emit (o, i_operation); +# if d_m3RecordBacktraces + EmitMappingEntry (o->page, o->lastOpcodeStart - o->module->wasmStart); +# endif // d_m3RecordBacktraces EmitWord (o->page, i_operation); } } diff --git a/src/m3_env.c b/src/m3_env.c index 2755566..6940152 100644 --- a/src/m3_env.c +++ b/src/m3_env.c @@ -41,16 +41,19 @@ void Function_Release (IM3Function i_function) { m3Free (i_function->constants); - // name can be an alias of fieldUtf8 - if (i_function->name != i_function->import.fieldUtf8) + for (int i = 0; i < i_function->numNames; i++) { - m3Free (i_function->name); + // name can be an alias of fieldUtf8 + if (i_function->names[i] != i_function->import.fieldUtf8) + { + m3Free (i_function->names[i]); + } } FreeImportInfo (& i_function->import); - if (i_function->ownsWasmCode) - m3Free (i_function->wasm); + //if (i_function->ownsWasmCode) + // m3Free (i_function->wasm); // Function_FreeCompiledCode (func); @@ -88,12 +91,37 @@ void Function_FreeCompiledCode (IM3Function i_function) -cstr_t GetFunctionName (IM3Function i_function) +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) - return i_function->import.fieldUtf8; + { + *o_numNames = 1; + return &i_function->import.fieldUtf8; + } else - return (i_function->name) ? i_function->name : ""; + { + *o_numNames = i_function->numNames; + return i_function->names; + } } @@ -154,6 +182,8 @@ IM3Environment m3_NewEnvironment () // 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; @@ -176,7 +206,15 @@ void Environment_Release (IM3Environment i_environment) IM3FuncType next = ftype->next; m3Free (ftype); ftype = next; - } m3log (runtime, "freeing %d pages from environment", CountCodePages (i_environment->pagesReleased)); + } + 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); } @@ -256,6 +294,9 @@ void Environment_ReleaseCodePages (IM3Environment i_environment, IM3CodePage i while (end) { end->info.lineIndex = 0; // reset page +#if d_m3RecordBacktraces + end->info.mapping->size = 0; +#endif // d_m3RecordBacktraces IM3CodePage next = end->info.next; if (not next) @@ -302,8 +343,6 @@ void * m3_GetUserData (IM3Runtime i_runtime) return i_runtime ? i_runtime->userdata : NULL; } -typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info); - void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info) { void * r = NULL; @@ -376,7 +415,11 @@ M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type M3Result result = m3Err_none; // create a temporary runtime context +#if defined(d_m3PreferStaticAlloc) + static M3Runtime runtime; +#else M3Runtime runtime; +#endif M3_INIT (runtime); runtime.environment = i_module->runtime->environment; @@ -393,6 +436,7 @@ M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type o->module = i_module; o->wasm = * io_bytes; o->wasmEnd = i_end; + o->lastOpcodeStart = o->wasm; o->block.depth = -1; // so that root compilation depth = 0 @@ -409,9 +453,8 @@ M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type if (not result) { m3ret_t r = Call (m3code, stack, NULL, d_m3OpDefaultArgs); - result = runtime.runtimeError; - if (r == 0 and not result) + if (r == 0) { if (SizeOfType (i_type) == sizeof (u32)) { @@ -602,26 +645,19 @@ _ (ReadLEB_u32 (& numElements, & bytes, end)); u32 endElement = numElements + offset; - if (endElement > offset) // TODO: check this, endElement depends on offset - { -_ (m3ReallocArray (& io_module->table0, IM3Function, endElement, io_module->table0Size)); - - io_module->table0Size = endElement; + _throwif ("table overflow", offset >= endElement); // TODO: check this, endElement depends on offset +_ (m3ReallocArray (& io_module->table0, IM3Function, endElement, io_module->table0Size)); - for (u32 e = 0; e < numElements; ++e) - { - u32 functionIndex; -_ (ReadLEB_u32 (& functionIndex, & bytes, end)); + io_module->table0Size = endElement; - if (functionIndex < io_module->numFunctions) - { - IM3Function function = & io_module->functions [functionIndex]; d_m3Assert (function); //printf ("table: %s\n", function->name); - io_module->table0 [e + offset] = function; - } - else _throw ("function index out of range"); - } + for (u32 e = 0; e < numElements; ++e) + { + u32 functionIndex; +_ (ReadLEB_u32 (& functionIndex, & bytes, end)); + _throwif ("function index out of range", functionIndex >= io_module->numFunctions); + IM3Function function = & io_module->functions [functionIndex]; d_m3Assert (function); //printf ("table: %s\n", m3_GetFunctionName(function)); + io_module->table0 [e + offset] = function; } - else _throw ("table overflow"); } else _throw ("element table index must be zero for MVP"); } @@ -629,11 +665,11 @@ _ (ReadLEB_u32 (& functionIndex, & bytes, end)); _catch: return result; } -M3Result InitStartFunc (IM3Module io_module) +M3Result m3_RunStart (IM3Module io_module) { M3Result result = m3Err_none; - if (io_module->startFunction >= 0) + if (io_module and io_module->startFunction >= 0) { IM3Function function = & io_module->functions [io_module->startFunction]; @@ -651,7 +687,7 @@ _ (Compile_Function (function)); _ ((M3Result) Call (function->compiled, (m3stack_t) runtime->stack, runtime->memory.mallocated, d_m3OpDefaultArgs)); - io_module->startFunction = -1; + io_module->startFunction = -1; } _catch: return result; @@ -693,9 +729,14 @@ void * v_FindFunction (IM3Module i_module, const char * const i_name) { IM3Function f = & i_module->functions [i]; - if (f->name) + bool isImported = f->import.moduleUtf8 or f->import.fieldUtf8; + + if (isImported) + continue; + + for (int j = 0; j < f->numNames; j++) { - if (strcmp (f->name, i_name) == 0) + if (f->names [j] and strcmp (f->names [j], i_name) == 0) return f; } } @@ -727,7 +768,7 @@ M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, cons // Check if start function needs to be called if (function and function->module->startFunction) { - result = InitStartFunc (function->module); + result = m3_RunStart (function->module); if (result) return result; } @@ -737,148 +778,236 @@ M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, cons return result; } +uint32_t m3_GetArgCount (IM3Function i_function) +{ + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft) { + return ft->numArgs; + } + } + return 0; +} -M3Result m3_Call (IM3Function i_function) +uint32_t m3_GetRetCount (IM3Function i_function) { - return m3_CallWithArgs (i_function, 0, NULL); + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft) { + return ft->numRets; + } + } + return 0; } -M3Result m3_CallWithArgs (IM3Function i_function, uint32_t i_argc, const char * const * i_argv) +M3ValueType m3_GetArgType (IM3Function i_function, uint32_t index) { - M3Result result = m3Err_none; + if (i_function) { + IM3FuncType ft = i_function->funcType; + if (ft and index < ft->numArgs) { + return (M3ValueType)d_FuncArgType(ft, index); + } + } + return c_m3Type_none; +} - if (i_function->compiled) - { - IM3Module module = i_function->module; +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 c_m3Type_none; +} - IM3Runtime runtime = module->runtime; - runtime->argc = i_argc; - runtime->argv = i_argv; - if (i_function->name and strcmp (i_function->name, "_start") == 0) // WASI - i_argc = 0; +M3Result m3_CallV (IM3Function i_function, ...) +{ + va_list ap; + va_start(ap, i_function); + M3Result r = m3_CallVL(i_function, ap); + va_end(ap); + return r; +} - IM3FuncType ftype = i_function->funcType; m3log (runtime, "calling %s", SPrintFuncTypeSignature (ftype)); +M3Result m3_CallVL (IM3Function i_function, va_list i_args) +{ + IM3Runtime runtime = i_function->module->runtime; + IM3FuncType ftype = i_function->funcType; - if (i_argc != ftype->numArgs) - _throw (m3Err_argumentCountMismatch); + if (!i_function->compiled) { + return m3Err_missingCompiledCode; + } - // args are always 64-bit aligned - u64 * stack = (u64 *) runtime->stack; +# if d_m3RecordBacktraces + ClearBacktrace (runtime); +# endif - // The format is currently not user-friendly by default, - // as this is used in spec tests - for (u32 i = 0; i < ftype->numArgs; ++i) - { - u64 * s = & stack [i]; - ccstr_t str = i_argv[i]; - - switch (ftype->types[ftype->numRets + i]) { -#ifdef USE_HUMAN_FRIENDLY_ARGS - case c_m3Type_i32: *(i32*)(s) = atol(str); break; - case c_m3Type_i64: *(i64*)(s) = atoll(str); break; - case c_m3Type_f32: *(f32*)(s) = atof(str); break; - case c_m3Type_f64: *(f64*)(s) = atof(str); break; -#else - case c_m3Type_i32: - case c_m3Type_f32: *(u32*)(s) = strtoul(str, NULL, 10); break; - case c_m3Type_i64: - case c_m3Type_f64: *(u64*)(s) = strtoull(str, NULL, 10); break; -#endif - default: _throw("unknown argument type"); - } + u8* s = (u8*) runtime->stack; + 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; + 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; + default: return "unknown argument type"; } + } + m3StackCheckInit(); + M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); - m3StackCheckInit(); -_ ((M3Result) Call (i_function->compiled, (m3stack_t) stack, runtime->memory.mallocated, d_m3OpDefaultArgs)); - -#if d_m3LogOutput - switch (GetSingleRetType(ftype)) { - case c_m3Type_none: fprintf (stderr, "Result: \n"); break; -#ifdef USE_HUMAN_FRIENDLY_ARGS - case c_m3Type_i32: fprintf (stderr, "Result: %" PRIi32 "\n", *(i32*)(stack)); break; - case c_m3Type_i64: fprintf (stderr, "Result: %" PRIi64 "\n", *(i64*)(stack)); break; - case c_m3Type_f32: fprintf (stderr, "Result: %f\n", *(f32*)(stack)); break; - case c_m3Type_f64: fprintf (stderr, "Result: %lf\n", *(f64*)(stack)); break; -#else - case c_m3Type_i32: - case c_m3Type_f32: - fprintf (stderr, "Result: %u\n", *(u32*)(stack)); break; - case c_m3Type_i64: - case c_m3Type_f64: - fprintf (stderr, "Result: %" PRIu64 "\n", *(u64*)(stack)); break; -#endif // USE_HUMAN_FRIENDLY_ARGS - default: _throw("unknown return type"); - } + runtime->lastCalled = r ? NULL : i_function; #if d_m3LogNativeStack - int stackUsed = m3StackGetMax(); - fprintf (stderr, "Native stack used: %d\n", stackUsed); -#endif // d_m3LogNativeStack + int stackUsed = m3StackGetMax(); + fprintf (stderr, "Native stack used: %d\n", stackUsed); +#endif + + return r; +} -#endif // d_m3LogOutput +M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[]) +{ + IM3Runtime runtime = i_function->module->runtime; + IM3FuncType ftype = i_function->funcType; - //u64 value = * (u64 *) (stack); - //m3log (runtime, "return64: %" PRIu64 " return32: %u", value, (u32) value); + if (i_argc != ftype->numArgs) { + return m3Err_argumentCountMismatch; + } + if (!i_function->compiled) { + return m3Err_missingCompiledCode; } - else _throw (m3Err_missingCompiledCode); - _catch: return result; +# if d_m3RecordBacktraces + ClearBacktrace (runtime); +# endif + + u8* s = (u8*) runtime->stack; + 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; + 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; + default: return "unknown argument type"; + } + } + + m3StackCheckInit(); + M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); + + runtime->lastCalled = r ? NULL : i_function; + +#if d_m3LogNativeStack + int stackUsed = m3StackGetMax(); + fprintf (stderr, "Native stack used: %d\n", stackUsed); +#endif + + return r; } -#if 0 -M3Result m3_CallMain (IM3Function i_function, uint32_t i_argc, const char * const * i_argv) +M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]) { - M3Result result = m3Err_none; - - if (i_function->compiled) - { - IM3Module module = i_function->module; + IM3FuncType ftype = i_function->funcType; + IM3Runtime runtime = i_function->module->runtime; - IM3Runtime runtime = module->runtime; + if (i_argc != ftype->numArgs) { + return m3Err_argumentCountMismatch; + } + if (!i_function->compiled) { + return m3Err_missingCompiledCode; + } - u8 * linearMemory = runtime->memory.wasmPages; +# if d_m3RecordBacktraces + ClearBacktrace (runtime); +# endif - m3stack_t stack = (m3stack_t) runtime->stack; + u8* s = (u8*) runtime->stack; + 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; + 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; + default: return "unknown argument type"; + } + } - if (i_argc) - { - IM3Memory memory = & runtime->memory; - // FIX: memory allocation in general + m3StackCheckInit(); + M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); - i32 offset = AllocatePrivateHeap (memory, sizeof (i32) * i_argc); + runtime->lastCalled = r ? NULL : i_function; - i32 * pointers = (i32 *) (memory->wasmPages + offset); +#if d_m3LogNativeStack + int stackUsed = m3StackGetMax(); + fprintf (stderr, "Native stack used: %d\n", stackUsed); +#endif - for (u32 i = 0; i < i_argc; ++i) - { - size_t argLength = strlen (i_argv [i]) + 1; + return r; +} - if (argLength < 4000) - { - i32 o = AllocatePrivateHeap (memory, (i32) argLength); - memcpy (memory->wasmPages + o, i_argv [i], argLength); +M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]) +{ + IM3FuncType ftype = i_function->funcType; + IM3Runtime runtime = i_function->module->runtime; - * pointers++ = o; - } - else _throw ("insane argument string length"); - } + if (i_retc != ftype->numRets) { + return m3Err_argumentCountMismatch; + } + if (i_function != runtime->lastCalled) { + return "function not called"; + } - stack [0] = i_argc; - stack [1] = offset; + 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; + 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; + default: return "unknown return type"; } + } + return m3Err_none; +} -_ ((M3Result)Call (i_function->compiled, stack, linearMemory, d_m3OpDefaultArgs)); +M3Result m3_GetResultsV (IM3Function i_function, ...) +{ + va_list ap; + va_start(ap, i_function); + M3Result r = m3_GetResultsVL(i_function, ap); + va_end(ap); + return r; +} + +M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets) +{ + IM3Runtime runtime = i_function->module->runtime; + IM3FuncType ftype = i_function->funcType; - //u64 value = * (u64 *) (stack); - //m3log (runtime, "return64: % " PRIu64 " return32: %" PRIu32, value, (u32) value); + if (i_function != runtime->lastCalled) { + return "function not called"; } - else _throw (m3Err_missingCompiledCode); - _catch: return result; + u8* s = (u8*) runtime->stack; + for (u32 i = 0; i < ftype->numRets; ++i) + { + 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; + 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; + default: return "unknown argument type"; + } + } + return m3Err_none; } -#endif - void ReleaseCodePageNoTrack (IM3Runtime i_runtime, IM3CodePage i_codePage) { @@ -970,18 +1099,23 @@ M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, #endif -void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* info) +void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info) { - *info = i_runtime->error; - - m3_ResetErrorInfo (i_runtime); + if (i_runtime) + { + *o_info = i_runtime->error; + m3_ResetErrorInfo (i_runtime); + } } void m3_ResetErrorInfo (IM3Runtime i_runtime) { - M3_INIT(i_runtime->error); - i_runtime->error.message = ""; + if (i_runtime) + { + M3_INIT(i_runtime->error); + i_runtime->error.message = ""; + } } uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, uint32_t i_memoryIndex) @@ -1002,3 +1136,13 @@ uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, return memory; } + +M3BacktraceInfo * m3_GetBacktrace (IM3Runtime i_runtime) +{ +# if d_m3RecordBacktraces + return & i_runtime->backtrace; +# else + return NULL; +# endif +} + diff --git a/src/m3_env.h b/src/m3_env.h index 4f3b21e..1155e05 100644 --- a/src/m3_env.h +++ b/src/m3_env.h @@ -10,7 +10,6 @@ #include "wasm3.h" #include "m3_code.h" -#include "m3_exec.h" #include "m3_compile.h" d_m3BeginExternC @@ -29,32 +28,36 @@ typedef struct M3Function bytes_t wasm; bytes_t wasmEnd; - cstr_t name; + 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 +#if (d_m3EnableCodePageRefCounting) + IM3CodePage * codePageRefs; // array of all pages used u32 numCodePageRefs; -# endif +#endif -# if defined(DEBUG) +#if defined(DEBUG) u32 hits; -# endif +#endif +#if d_m3EnableStrace >= 2 + u16 index; +#endif u16 maxStackSlots; u16 numArgSlots; - u16 numLocals; // not including args + u16 numLocals; // not including args u16 numLocalBytes; void * constants; u16 numConstantBytes; - bool ownsWasmCode; + //bool ownsWasmCode; } M3Function; @@ -62,7 +65,7 @@ void Function_Release (IM3Function i_function); void Function_FreeCompiledCode (IM3Function i_function); cstr_t GetFunctionImportModuleName (IM3Function i_function); -cstr_t GetFunctionName (IM3Function i_function); +cstr_t * GetFunctionNames (IM3Function i_function, u16 * o_numNames); u32 GetFunctionNumArgs (IM3Function i_function); u32 GetFunctionNumReturns (IM3Function i_function); @@ -140,13 +143,16 @@ typedef struct M3Module struct M3Runtime * runtime; struct M3Environment * environment; + bytes_t wasmStart; + bytes_t wasmEnd; + cstr_t name; u32 numFuncTypes; IM3FuncType * funcTypes; // array of pointers to list of FuncTypes u32 numImports; - IM3Function * imports; // notice: "I" prefix. imports are pointers to functions in another module. + //IM3Function * imports; b // notice: "I" prefix. imports are pointers to functions in another module. u32 numFunctions; M3Function * functions; @@ -156,7 +162,7 @@ typedef struct M3Module u32 numDataSegments; M3DataSegment * dataSegments; - u32 importedGlobals; + //u32 importedGlobals; u32 numGlobals; M3Global * globals; @@ -170,7 +176,7 @@ typedef struct M3Module M3MemoryInfo memoryInfo; bool memoryImported; - bool hasWasmCodeCopy; + //bool hasWasmCodeCopy; struct M3Module * next; } @@ -189,7 +195,7 @@ typedef struct M3Environment IM3FuncType funcTypes; // linked list - IM3FuncType retFuncTypes[5]; + IM3FuncType retFuncTypes[5]; // the number of elements must match the basic types as per M3ValueType M3CodePage * pagesReleased; } @@ -219,22 +225,25 @@ typedef struct M3Runtime void * stack; u32 stackSize; u32 numStackSlots; - - i32 exit_code; - u32 argc; - ccstr_t * argv; + IM3Function lastCalled; // last function that successfully executed void * userdata; M3Memory memory; u32 memoryLimit; - M3Result runtimeError; +#if d_m3EnableStrace >= 2 + u32 callDepth; +#endif M3ErrorInfo error; #if d_m3VerboseLogs char error_message[256]; // the actual buffer. M3ErrorInfo can point to this #endif + +#if d_m3RecordBacktraces + M3BacktraceInfo backtrace; +#endif } M3Runtime; @@ -252,8 +261,6 @@ IM3CodePage AcquireCodePage (IM3Runtime io_runtime); IM3CodePage AcquireCodePageWithCapacity (IM3Runtime io_runtime, u32 i_lineCount); void ReleaseCodePage (IM3Runtime io_runtime, IM3CodePage i_codePage); -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, ...); - d_m3EndExternC #endif // m3_env_h diff --git a/src/m3_exec.c b/src/m3_exec.c index 65c08ed..7ca770c 100644 --- a/src/m3_exec.c +++ b/src/m3_exec.c @@ -10,451 +10,6 @@ #include "m3_compile.h" -void ReportError2 (IM3Function i_function, m3ret_t i_result) -{ - i_function->module->runtime->runtimeError = (M3Result)i_result; -} - -d_m3OpDef (GetGlobal_s32) -{ - u32 * global = immediate (u32 *); - slot (u32) = * global; // printf ("get global: %p %" PRIi64 "\n", global, *global); - - nextOp (); -} - - -d_m3OpDef (GetGlobal_s64) -{ - u64 * global = immediate (u64 *); - slot (u64) = * global; // printf ("get global: %p %" PRIi64 "\n", global, *global); - - nextOp (); -} - - -d_m3OpDef (SetGlobal_i32) -{ - u32 * global = immediate (u32 *); - * global = (u32) _r0; // printf ("set global: %p %" PRIi64 "\n", global, _r0); - - nextOp (); -} - - -d_m3OpDef (SetGlobal_i64) -{ - u64 * global = immediate (u64 *); - * global = (u64) _r0; // printf ("set global: %p %" PRIi64 "\n", global, _r0); - - nextOp (); -} - - -d_m3OpDef (Call) -{ - pc_t callPC = immediate (pc_t); - i32 stackOffset = immediate (i32); - IM3Memory memory = m3MemInfo (_mem); - - m3stack_t sp = _sp + stackOffset; - - m3ret_t r = Call (callPC, sp, _mem, d_m3OpDefaultArgs); - - if (r == 0) - { - _mem = memory->mallocated; - nextOp (); - } - else return r; -} - - -d_m3OpDef (CallIndirect) -{ - u32 tableIndex = slot (u32); - IM3Module module = immediate (IM3Module); - IM3FuncType type = immediate (IM3FuncType); - i32 stackOffset = immediate (i32); - IM3Memory memory = m3MemInfo (_mem); - - m3stack_t sp = _sp + stackOffset; - - m3ret_t r = m3Err_none; - - if (tableIndex < module->table0Size) - { - IM3Function function = module->table0 [tableIndex]; - - if (function) - { - if (type == function->funcType) - { - if (UNLIKELY(not function->compiled)) - r = Compile_Function (function); - - if (not r) - { - r = Call (function->compiled, sp, _mem, d_m3OpDefaultArgs); - - if (not r) - { - _mem = memory->mallocated; - r = nextOpDirect (); - } - } - } - else r = m3Err_trapIndirectCallTypeMismatch; - } - else r = m3Err_trapTableElementIsNull; - } - else r = m3Err_trapTableIndexOutOfRange; - - return r; -} - - -d_m3OpDef (CallRawFunction) -{ - M3RawCall call = (M3RawCall) (* _pc++); - IM3Function function = immediate (IM3Function); - void * userdata = immediate (void *); - u64* const sp = ((u64*)_sp); - -#if d_m3EnableStrace - IM3FuncType ftype = function->funcType; - - FILE* out = stderr; - char outbuff[1024]; - char* outp = outbuff; - char* oute = outbuff+1024; - - outp += snprintf(outp, oute-outp, "%s.%s(", function->import.moduleUtf8, function->import.fieldUtf8); - - const int nArgs = ftype->numArgs; - const int nRets = ftype->numRets; - for (int i=0; itypes[nRets + i]; - switch (type) { - case c_m3Type_i32: outp += snprintf(outp, oute-outp, "%i", *(i32*)(sp+i)); break; - case c_m3Type_i64: outp += snprintf(outp, oute-outp, "%lli", *(i64*)(sp+i)); break; - case c_m3Type_f32: outp += snprintf(outp, oute-outp, "%f", *(f32*)(sp+i)); break; - case c_m3Type_f64: outp += snprintf(outp, oute-outp, "%lf", *(f64*)(sp+i)); break; - default: outp += snprintf(outp, oute-outp, "", type); break; - } - outp += snprintf(outp, oute-outp, (i < nArgs-1) ? ", " : ")"); - } -#endif - - m3ret_t possible_trap = call (m3MemRuntime(_mem), sp, m3MemData(_mem), userdata); - -#if d_m3EnableStrace - if (possible_trap) { - fprintf(out, "%s -> %s\n", outbuff, possible_trap); - } else { - switch (GetSingleRetType(ftype)) { - case c_m3Type_none: fprintf(out, "%s\n", outbuff); break; - case c_m3Type_i32: fprintf(out, "%s = %i\n", outbuff, *(i32*)sp); break; - case c_m3Type_i64: fprintf(out, "%s = %lli\n", outbuff, *(i64*)sp); break; - case c_m3Type_f32: fprintf(out, "%s = %f\n", outbuff, *(f32*)sp); break; - case c_m3Type_f64: fprintf(out, "%s = %lf\n", outbuff, *(f64*)sp); break; - } - } -#endif - - return possible_trap; -} - - -d_m3OpDef (MemCurrent) -{ - IM3Memory memory = m3MemInfo (_mem); - - _r0 = memory->numPages; - - nextOp (); -} - - -d_m3OpDef (MemGrow) -{ - IM3Runtime runtime = m3MemRuntime(_mem); - IM3Memory memory = & runtime->memory; - - u32 numPagesToGrow = (u32) _r0; - _r0 = memory->numPages; - - if (numPagesToGrow) - { - u32 requiredPages = memory->numPages + numPagesToGrow; - - M3Result r = ResizeMemory (runtime, requiredPages); - if (r) - _r0 = -1; - - _mem = memory->mallocated; - } - - nextOp (); -} - - -// 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 -// for the stub approach. Stubbing makes it easier to dynamically free the compilation. You can also -// do both. -d_m3OpDef (Compile) -{ - rewrite_op (op_Call); - - IM3Function function = immediate (IM3Function); - - 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); - - if (not result) - { - // patch up compiled pc and call rewriten op_Call - * ((void**) --_pc) = (void*) (function->compiled); - --_pc; - result = nextOpDirect (); - } - else ReportError2 (function, result); - - return result; -} - - - -d_m3OpDef (Entry) -{ - d_m3ClearRegisters - - IM3Function function = immediate (IM3Function); - -#if d_m3SkipStackCheck - if (true) -#else - if ((void *) ((m3slot_t *) _sp + function->maxStackSlots) < _mem->maxStack) -#endif - { - m3log (exec, " enter %p > %s %s", _pc - 2, function->name ? function->name : ".unnamed", SPrintFunctionArgList (function, _sp)); - -#if defined(DEBUG) - function->hits++; -#endif - u8 * stack = (u8 *) ((m3slot_t *) _sp + function->numArgSlots); - - memset (stack, 0x0, function->numLocalBytes); - stack += function->numLocalBytes; - - if (function->constants) - { - memcpy (stack, function->constants, function->numConstantBytes); - } - - m3ret_t r = nextOpDirect (); - -# if d_m3LogExec - char str [100] = { '!', 0 }; - - if (not r) - SPrintArg (str, 99, _sp, GetSingleRetType(function->funcType)); - - m3log (exec, " exit < %s %s %s %s", function->name, function->funcType->numRets ? "->" : "", str, r ? (cstr_t)r : ""); -# elif d_m3LogStackTrace - if (r) - printf (" ** %s %p\n", function->name, _sp); -# endif - - return r; - } - else return m3Err_trapStackOverflow; -} - - -d_m3OpDef (Loop) -{ - // regs are unused coming into a loop anyway - // this reduces code size & stack usage - d_m3ClearRegisters - - m3ret_t r; - - IM3Memory memory = m3MemInfo (_mem); - - do - { - r = nextOpDirect (); // printf ("loop: %p\n", r); - // linear memory pointer needs refreshed here because the block it's looping over - // can potentially invoke the grow operation. - _mem = memory->mallocated; - } - while (r == _pc); - - return r; -} - - -d_m3OpDef (Branch) -{ - return jumpOp (* _pc); -} - - -d_m3OpDef (If_r) -{ - i32 condition = (i32) _r0; - - pc_t elsePC = immediate (pc_t); - - if (condition) - nextOp (); - else - return jumpOp (elsePC); -} - - -d_m3OpDef (If_s) -{ - i32 condition = slot (i32); - - pc_t elsePC = immediate (pc_t); - - if (condition) - nextOp (); - else - return jumpOp (elsePC); -} - - -d_m3OpDef (BranchTable) -{ - i32 branchIndex = slot (i32); // branch index is always in a slot - i32 numTargets = immediate (i32); - - pc_t * branches = (pc_t *) _pc; - - if (branchIndex < 0 or branchIndex > numTargets) - branchIndex = numTargets; // the default index - - return jumpOp (branches [branchIndex]); -} - - -#define d_m3SetRegisterSetSlot(TYPE, REG) \ -d_m3OpDef (SetRegister_##TYPE) \ -{ \ - REG = slot (TYPE); \ - nextOp (); \ -} \ - \ -d_m3OpDef (SetSlot_##TYPE) \ -{ \ - slot (TYPE) = (TYPE) REG; \ - nextOp (); \ -} \ - \ -d_m3OpDef (PreserveSetSlot_##TYPE) \ -{ \ - TYPE * stack = slot_ptr (TYPE); \ - TYPE * preserve = slot_ptr (TYPE); \ - \ - * preserve = * stack; \ - * stack = (TYPE) REG; \ - \ - nextOp (); \ -} - -d_m3SetRegisterSetSlot (i32, _r0) -d_m3SetRegisterSetSlot (i64, _r0) -#if d_m3HasFloat -d_m3SetRegisterSetSlot (f32, _fp0) -d_m3SetRegisterSetSlot (f64, _fp0) -#endif - -d_m3OpDef (CopySlot_32) -{ - u32 * dst = slot_ptr (u32); - u32 * src = slot_ptr (u32); - - * dst = * src; - - nextOp (); -} - - -d_m3OpDef (PreserveCopySlot_32) -{ - u32 * dest = slot_ptr (u32); - u32 * src = slot_ptr (u32); - u32 * preserve = slot_ptr (u32); - - * preserve = * dest; - * dest = * src; - - nextOp (); -} - - -d_m3OpDef (CopySlot_64) -{ - u64 * dst = slot_ptr (u64); - u64 * src = slot_ptr (u64); - - * dst = * src; // printf ("copy: %p <- %" PRIi64 " <- %p\n", dst, * dst, src); - - nextOp (); -} - - -d_m3OpDef (PreserveCopySlot_64) -{ - u64 * dest = slot_ptr (u64); - u64 * src = slot_ptr (u64); - u64 * preserve = slot_ptr (u64); - - * preserve = * dest; - * dest = * src; - - nextOp (); -} - - -#if d_m3EnableOpTracing -//-------------------------------------------------------------------------------------------------------- -d_m3OpDef (DumpStack) -{ - u32 opcodeIndex = immediate (u32); - u32 stackHeight = immediate (u32); - IM3Function function = immediate (IM3Function); - - cstr_t funcName = (function) ? function->name : ""; - - printf (" %4d ", opcodeIndex); - printf (" %-25s r0: 0x%016" PRIx64 " i:%" PRIi64 " u:%" PRIu64 "\n", funcName, _r0, _r0, _r0); - printf (" fp0: %lf\n", _fp0); - - m3stack_t sp = _sp; - - for (u32 i = 0; i < stackHeight; ++i) - { - cstr_t kind = ""; - - printf ("%p %5s %2d: 0x%" PRIx64 " i:%" PRIi64 "\n", sp, kind, i, (u64) *(sp), (i64) *(sp)); - - ++sp; - } - printf ("---------------------------------------------------------------------------------------------------------\n"); - - return nextOpDirect(); -} -#endif - - # if d_m3EnableOpProfiling //-------------------------------------------------------------------------------------------------------- static M3ProfilerSlot s_opProfilerCounts [d_m3ProfilerSlotMask + 1] = {}; diff --git a/src/m3_exec.h b/src/m3_exec.h index 0e3e2be..8081e7f 100644 --- a/src/m3_exec.h +++ b/src/m3_exec.h @@ -23,8 +23,11 @@ //------------------------------------------------------------------------------------------------------ -#include "m3_exec_defs.h" #include "m3_math_utils.h" +#include "m3_compile.h" +#include "m3_env.h" +#include "m3_info.h" +#include "m3_exec_defs.h" #include @@ -32,20 +35,12 @@ d_m3BeginExternC # define rewrite_op(OP) * ((void **) (_pc-1)) = (void*)(OP) -# define d_m3RetSig static inline m3ret_t vectorcall -# define d_m3Op(NAME) op_section d_m3RetSig op_##NAME (d_m3OpSig) - -# define d_m3OpDef(NAME) op_section m3ret_t vectorcall op_##NAME (d_m3OpSig) -# define d_m3OpDecl(NAME) m3ret_t vectorcall op_##NAME (d_m3OpSig); - # define immediate(TYPE) * ((TYPE *) _pc++) # define skip_immediate(TYPE) (_pc++) # define slot(TYPE) * (TYPE *) (_sp + immediate (i32)) # define slot_ptr(TYPE) (TYPE *) (_sp + immediate (i32)) -#define nextOpDirect() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) -#define jumpOpDirect(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) # if d_m3EnableOpProfiling d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName); @@ -54,17 +49,53 @@ d_m3BeginExternC d_m3RetSig debugOp (d_m3OpSig, cstr_t i_operationName); # define nextOp() return debugOp (d_m3OpAllArgs, __FUNCTION__) # else -# define nextOp() return nextOpDirect() +# define nextOp() nextOpDirect() # endif -#define jumpOp(PC) jumpOpDirect((pc_t)PC) +#define jumpOp(PC) jumpOpDirect(PC) + +#if d_m3RecordBacktraces + #define pushBacktraceFrame() (PushBacktraceFrame (_mem->runtime, _pc - 1)) + #define fillBacktraceFrame(FUNCTION) (FillBacktraceFunctionInfo (_mem->runtime, function)) + + #define newTrap(err) return (pushBacktraceFrame (), err) + #define forwardTrap(err) return err +#else + #define pushBacktraceFrame() do {} while (0) + #define fillBacktraceFrame(FUNCTION) do {} while (0) + + #define newTrap(err) return err + #define forwardTrap(err) return err +#endif + + +#if d_m3EnableStrace == 1 + // Flat trace + #define d_m3TracePrepare + #define d_m3TracePrint(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) +#elif d_m3EnableStrace >= 2 + // Structured trace + #define d_m3TracePrepare const IM3Runtime trace_rt = m3MemRuntime(_mem); + #define d_m3TracePrint(fmt, ...) fprintf(stderr, "%*s" fmt "\n", (trace_rt->callDepth)*2, "", ##__VA_ARGS__) +#else + #define d_m3TracePrepare + #define d_m3TracePrint(fmt, ...) +#endif + +#if d_m3EnableStrace >= 3 + #define d_m3TraceLoad(TYPE,offset,val) d_m3TracePrint("load." #TYPE " 0x%x = %" PRI##TYPE, offset, val) + #define d_m3TraceStore(TYPE,offset,val) d_m3TracePrint("store." #TYPE " 0x%x , %" PRI##TYPE, offset, val) +#else + #define d_m3TraceLoad(TYPE,offset,val) + #define d_m3TraceStore(TYPE,offset,val) +#endif d_m3RetSig Call (d_m3OpSig) { m3ret_t possible_trap = m3_Yield (); if (UNLIKELY(possible_trap)) return possible_trap; - return nextOpDirect(); + nextOpDirect(); } // TODO: OK, this needs some explanation here ;0 @@ -442,9 +473,504 @@ d_m3ReinterpretOp (_fp0, f32, _r0, i32) d_m3ReinterpretOp (_fp0, f64, _r0, i64) #endif -d_m3OpDecl (Loop) -d_m3OpDecl (If_r) -d_m3OpDecl (If_s) + +d_m3Op (GetGlobal_s32) +{ + u32 * global = immediate (u32 *); + slot (u32) = * global; // printf ("get global: %p %" PRIi64 "\n", global, *global); + + nextOp (); +} + + +d_m3Op (GetGlobal_s64) +{ + u64 * global = immediate (u64 *); + slot (u64) = * global; // printf ("get global: %p %" PRIi64 "\n", global, *global); + + nextOp (); +} + + +d_m3Op (SetGlobal_i32) +{ + u32 * global = immediate (u32 *); + * global = (u32) _r0; // printf ("set global: %p %" PRIi64 "\n", global, _r0); + + nextOp (); +} + + +d_m3Op (SetGlobal_i64) +{ + u64 * global = immediate (u64 *); + * global = (u64) _r0; // printf ("set global: %p %" PRIi64 "\n", global, _r0); + + nextOp (); +} + + +d_m3Op (Call) +{ + pc_t callPC = immediate (pc_t); + i32 stackOffset = immediate (i32); + IM3Memory memory = m3MemInfo (_mem); + + m3stack_t sp = _sp + stackOffset; + + m3ret_t r = Call (callPC, sp, _mem, d_m3OpDefaultArgs); + _mem = memory->mallocated; + + if (LIKELY(not r)) + nextOp (); + else + { + pushBacktraceFrame (); + forwardTrap (r); + } +} + + +d_m3Op (CallIndirect) +{ + u32 tableIndex = slot (u32); + IM3Module module = immediate (IM3Module); + IM3FuncType type = immediate (IM3FuncType); + i32 stackOffset = immediate (i32); + IM3Memory memory = m3MemInfo (_mem); + + m3stack_t sp = _sp + stackOffset; + + m3ret_t r = m3Err_none; + + if (LIKELY(tableIndex < module->table0Size)) + { + IM3Function function = module->table0 [tableIndex]; + + if (LIKELY(function)) + { + if (LIKELY(type == function->funcType)) + { + if (UNLIKELY(not function->compiled)) + r = Compile_Function (function); + + if (LIKELY(not r)) + { + r = Call (function->compiled, sp, _mem, d_m3OpDefaultArgs); + _mem = memory->mallocated; + + if (LIKELY(not r)) + nextOpDirect (); + else + { + pushBacktraceFrame (); + forwardTrap (r); + } + } + } + else r = m3Err_trapIndirectCallTypeMismatch; + } + else r = m3Err_trapTableElementIsNull; + } + else r = m3Err_trapTableIndexOutOfRange; + + if (UNLIKELY(r)) + newTrap (r); + else forwardTrap (r); +} + + +d_m3Op (CallRawFunction) +{ + d_m3TracePrepare + + M3ImportContext ctx; + + M3RawCall call = (M3RawCall) (* _pc++); + ctx.function = immediate (IM3Function); + ctx.userdata = immediate (void *); + u64* const sp = ((u64*)_sp); + IM3Memory memory = m3MemInfo (_mem); + + IM3Runtime runtime = m3MemRuntime(_mem); + +#if d_m3EnableStrace + IM3FuncType ftype = ctx.function->funcType; + + FILE* out = stderr; + char outbuff[1024]; + char* outp = outbuff; + char* oute = outbuff+1024; + + outp += snprintf(outp, oute-outp, "%s!%s(", ctx.function->import.moduleUtf8, ctx.function->import.fieldUtf8); + + const int nArgs = ftype->numArgs; + const int nRets = ftype->numRets; + 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; + default: outp += snprintf(outp, oute-outp, "", type); break; + } + outp += snprintf(outp, oute-outp, (i < nArgs-1) ? ", " : ")"); + } +# if d_m3EnableStrace >= 2 + outp += snprintf(outp, oute-outp, " { }"); +# endif +#endif + + // m3_Call uses runtime->stack to set-up initial exported function stack. + // Reconfigure the stack to enable recursive invocations of m3_Call. + // I.e. exported/table function can be called from an impoted function. + void* stack_backup = runtime->stack; + runtime->stack = sp; + m3ret_t possible_trap = call (runtime, &ctx, sp, m3MemData(_mem)); + runtime->stack = stack_backup; + +#if d_m3EnableStrace + if (UNLIKELY(possible_trap)) { + d_m3TracePrint("%s -> %s", outbuff, possible_trap); + } else { + switch (GetSingleRetType(ftype)) { + case c_m3Type_none: d_m3TracePrint("%s", outbuff); break; + case c_m3Type_i32: d_m3TracePrint("%s = %" PRIi32, outbuff, *(i32*)sp); break; + case c_m3Type_i64: d_m3TracePrint("%s = %" PRIi64, outbuff, *(i64*)sp); break; + case c_m3Type_f32: d_m3TracePrint("%s = %" PRIf32, outbuff, *(f32*)sp); break; + case c_m3Type_f64: d_m3TracePrint("%s = %" PRIf64, outbuff, *(f64*)sp); break; + } + } +#endif + + if (UNLIKELY(possible_trap)) { + _mem = memory->mallocated; + pushBacktraceFrame (); + } + forwardTrap (possible_trap); +} + + +d_m3Op (MemCurrent) +{ + IM3Memory memory = m3MemInfo (_mem); + + _r0 = memory->numPages; + + nextOp (); +} + + +d_m3Op (MemGrow) +{ + IM3Runtime runtime = m3MemRuntime(_mem); + IM3Memory memory = & runtime->memory; + + u32 numPagesToGrow = (u32) _r0; + _r0 = memory->numPages; + + if (numPagesToGrow) + { + u32 requiredPages = memory->numPages + numPagesToGrow; + + M3Result r = ResizeMemory (runtime, requiredPages); + if (r) + _r0 = -1; + + _mem = memory->mallocated; + } + + nextOp (); +} + + +// 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 +// for the stub approach. Stubbing makes it easier to dynamically free the compilation. You can also +// do both. +d_m3Op (Compile) +{ + rewrite_op (op_Call); + + IM3Function function = immediate (IM3Function); + + 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); + + if (not result) + { + // patch up compiled pc and call rewriten op_Call + * ((void**) --_pc) = (void*) (function->compiled); + --_pc; + nextOpDirect (); + } + + newTrap (result); +} + + + +d_m3Op (Entry) +{ + d_m3ClearRegisters + + d_m3TracePrepare + + IM3Function function = immediate (IM3Function); + IM3Memory memory = m3MemInfo (_mem); + +#if d_m3SkipStackCheck + if (true) +#else + if (LIKELY((void *)((m3slot_t *) _sp + function->maxStackSlots) < _mem->maxStack)) +#endif + { +#if defined(DEBUG) + function->hits++; +#endif + u8 * stack = (u8 *) ((m3slot_t *) _sp + function->numArgSlots); + + memset (stack, 0x0, function->numLocalBytes); + stack += function->numLocalBytes; + + if (function->constants) + { + memcpy (stack, function->constants, function->numConstantBytes); + } + +#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)); + } + trace_rt->callDepth++; +#endif + + m3ret_t r = nextOpImpl (); + +#if d_m3EnableStrace >= 2 + trace_rt->callDepth--; + + if (r) { + d_m3TracePrint("} !trap = %s", (char*)r); + } else { + int rettype = GetSingleRetType(function->funcType); + if (rettype != c_m3Type_none) { + char str [128] = { 0 }; + SPrintArg (str, 127, _sp, rettype); + d_m3TracePrint("} = %s", str); + } else { + d_m3TracePrint("}"); + } + } +#endif + + if (UNLIKELY(r)) { + _mem = memory->mallocated; + fillBacktraceFrame (); + } + forwardTrap (r); + } + else newTrap (m3Err_trapStackOverflow); +} + + +d_m3Op (Loop) +{ + d_m3TracePrepare + + // regs are unused coming into a loop anyway + // this reduces code size & stack usage + d_m3ClearRegisters + + m3ret_t r; + + IM3Memory memory = m3MemInfo (_mem); + + do + { +#if d_m3EnableStrace >= 3 + d_m3TracePrint("iter {"); + trace_rt->callDepth++; +#endif + r = nextOpImpl (); // printf ("loop: %p\n", r); + +#if d_m3EnableStrace >= 3 + trace_rt->callDepth--; + d_m3TracePrint("}"); +#endif + // linear memory pointer needs refreshed here because the block it's looping over + // can potentially invoke the grow operation. + _mem = memory->mallocated; + } + while (r == _pc); + + forwardTrap (r); +} + + +d_m3Op (Branch) +{ + jumpOp (* _pc); +} + + +d_m3Op (If_r) +{ + i32 condition = (i32) _r0; + + pc_t elsePC = immediate (pc_t); + + if (condition) + nextOp (); + else + jumpOp (elsePC); +} + + +d_m3Op (If_s) +{ + i32 condition = slot (i32); + + pc_t elsePC = immediate (pc_t); + + if (condition) + nextOp (); + else + jumpOp (elsePC); +} + + +d_m3Op (BranchTable) +{ + u32 branchIndex = slot (u32); // branch index is always in a slot + u32 numTargets = immediate (u32); + + pc_t * branches = (pc_t *) _pc; + + if (branchIndex > numTargets) + branchIndex = numTargets; // the default index + + jumpOp (branches [branchIndex]); +} + + +#define d_m3SetRegisterSetSlot(TYPE, REG) \ +d_m3Op (SetRegister_##TYPE) \ +{ \ + REG = slot (TYPE); \ + nextOp (); \ +} \ + \ +d_m3Op (SetSlot_##TYPE) \ +{ \ + slot (TYPE) = (TYPE) REG; \ + nextOp (); \ +} \ + \ +d_m3Op (PreserveSetSlot_##TYPE) \ +{ \ + TYPE * stack = slot_ptr (TYPE); \ + TYPE * preserve = slot_ptr (TYPE); \ + \ + * preserve = * stack; \ + * stack = (TYPE) REG; \ + \ + nextOp (); \ +} + +d_m3SetRegisterSetSlot (i32, _r0) +d_m3SetRegisterSetSlot (i64, _r0) +#if d_m3HasFloat +d_m3SetRegisterSetSlot (f32, _fp0) +d_m3SetRegisterSetSlot (f64, _fp0) +#endif + +d_m3Op (CopySlot_32) +{ + u32 * dst = slot_ptr (u32); + u32 * src = slot_ptr (u32); + + * dst = * src; + + nextOp (); +} + + +d_m3Op (PreserveCopySlot_32) +{ + u32 * dest = slot_ptr (u32); + u32 * src = slot_ptr (u32); + u32 * preserve = slot_ptr (u32); + + * preserve = * dest; + * dest = * src; + + nextOp (); +} + + +d_m3Op (CopySlot_64) +{ + u64 * dst = slot_ptr (u64); + u64 * src = slot_ptr (u64); + + * dst = * src; // printf ("copy: %p <- %" PRIi64 " <- %p\n", dst, * dst, src); + + nextOp (); +} + + +d_m3Op (PreserveCopySlot_64) +{ + u64 * dest = slot_ptr (u64); + u64 * src = slot_ptr (u64); + u64 * preserve = slot_ptr (u64); + + * preserve = * dest; + * dest = * src; + + nextOp (); +} + + +#if d_m3EnableOpTracing +//-------------------------------------------------------------------------------------------------------- +d_m3Op (DumpStack) +{ + u32 opcodeIndex = immediate (u32); + u32 stackHeight = immediate (u32); + IM3Function function = immediate (IM3Function); + + cstr_t funcName = (function) ? m3_GetFunctionName(function) : ""; + + printf (" %4d ", opcodeIndex); + printf (" %-25s r0: 0x%016" PRIx64 " i:%" PRIi64 " u:%" PRIu64 "\n", funcName, _r0, _r0, _r0); +#if d_m3HasFloat + printf (" fp0: %" PRIf64 "\n", _fp0); +#endif + m3stack_t sp = _sp; + + for (u32 i = 0; i < stackHeight; ++i) + { + cstr_t kind = ""; + + printf ("%p %5s %2d: 0x%" PRIx64 " i:%" PRIi64 "\n", sp, kind, i, (u64) *(sp), (i64) *(sp)); + + ++sp; + } + printf ("---------------------------------------------------------------------------------------------------------\n"); + + nextOpDirect(); +} +#endif #define d_m3Select_i(TYPE, REG) \ @@ -549,13 +1075,10 @@ d_m3Select_f (f64, _fp0, s, slot (i32)) d_m3Op (Return) { m3StackCheck(); - return NULL; + return m3Err_none; } -d_m3OpDecl (Branch) - - d_m3Op (BranchIf_r) { i32 condition = (i32) _r0; @@ -563,7 +1086,7 @@ d_m3Op (BranchIf_r) if (condition) { - return jumpOp (branch); + jumpOp (branch); } else nextOp (); } @@ -576,7 +1099,7 @@ d_m3Op (BranchIf_s) if (condition) { - return jumpOp (branch); + jumpOp (branch); } else nextOp (); } @@ -593,7 +1116,7 @@ d_m3Op (TYPE##_BranchIf_##LABEL##s) \ if (condition) \ { \ _r0 = value; \ - return jumpOp (branch); \ + jumpOp (branch); \ } \ else nextOp (); \ } @@ -605,8 +1128,6 @@ d_m3BranchIf (i32, s, slot (i32)) d_m3BranchIf (i64, s, slot (i32)) -d_m3OpDecl (BranchTable) - d_m3Op (ContinueLoop) { @@ -632,18 +1153,6 @@ d_m3Op (ContinueLoopIf) } - -d_m3OpDecl (Compile) -d_m3OpDecl (Call) -d_m3OpDecl (CallIndirect) -d_m3OpDecl (CallRawFunction) -d_m3OpDecl (CallRawFunctionEx) -d_m3OpDecl (Entry) - -d_m3OpDecl (MemCurrent) -d_m3OpDecl (MemGrow) - - d_m3Op (Const32) { u32 value = * (u32 *)_pc++; @@ -661,30 +1170,24 @@ d_m3Op (Const64) } d_m3Op (Unsupported) -{ m3log (exec, "*** unsupported ***"); - return "unsupported instruction executed"; +{ + newTrap ("unsupported instruction executed"); } d_m3Op (Unreachable) -{ m3log (exec, "*** trapping ***"); +{ m3StackCheck(); - return m3Err_trapUnreachable; + newTrap (m3Err_trapUnreachable); } d_m3Op (End) { m3StackCheck(); - return 0; + return m3Err_none; } -d_m3OpDecl (GetGlobal_s32) -d_m3OpDecl (GetGlobal_s64) -d_m3OpDecl (SetGlobal_i32) -d_m3OpDecl (SetGlobal_i64) - - d_m3Op (SetGlobal_s32) { u32 * global = immediate (u32 *); @@ -722,23 +1225,6 @@ d_m3Op (SetGlobal_f64) #endif -d_m3OpDecl (CopySlot_32) -d_m3OpDecl (PreserveCopySlot_32) - -d_m3OpDecl (CopySlot_64) -d_m3OpDecl (PreserveCopySlot_64) - -#define d_m3SetRegisterSetSlotDecl(TYPE) \ - d_m3OpDecl (SetRegister_##TYPE) \ - d_m3OpDecl (SetSlot_##TYPE) \ - d_m3OpDecl (PreserveSetSlot_##TYPE) - -d_m3SetRegisterSetSlotDecl (i32) -d_m3SetRegisterSetSlotDecl (i64) -d_m3SetRegisterSetSlotDecl (f32) -d_m3SetRegisterSetSlotDecl (f64) - - #if d_m3SkipMemoryBoundsCheck # define m3MemCheck(x) true #else @@ -746,19 +1232,19 @@ d_m3SetRegisterSetSlotDecl (f64) #endif #ifdef DEBUG - #define d_outOfBounds return ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ + #define d_outOfBounds newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ _mem->runtime, "memory size: %zu; access offset: %zu", \ - _mem->length, operand) + _mem->length, operand)) #else - #define d_outOfBounds return m3Err_trapOutOfBoundsMemoryAccess + #define d_outOfBounds newTrap (m3Err_trapOutOfBoundsMemoryAccess) #endif // memcpy here is to support non-aligned access on some platforms. -// TODO: check if this is optimized-out on x86/x64, and performance impact #define d_m3Load(REG,DEST_TYPE,SRC_TYPE) \ d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_r) \ { \ + d_m3TracePrepare \ u32 offset = immediate (u32); \ u64 operand = (u32) _r0; \ operand += offset; \ @@ -771,11 +1257,13 @@ d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_r) \ memcpy(&value, src8, sizeof(value)); \ M3_BSWAP_##SRC_TYPE(value); \ REG = (DEST_TYPE)value; \ + d_m3TraceLoad(DEST_TYPE, operand, REG); \ nextOp (); \ } else d_outOfBounds; \ } \ d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_s) \ { \ + d_m3TracePrepare \ u64 operand = slot (u32); \ u32 offset = immediate (u32); \ operand += offset; \ @@ -788,6 +1276,7 @@ d_m3Op(DEST_TYPE##_Load_##SRC_TYPE##_s) \ memcpy(&value, src8, sizeof(value)); \ M3_BSWAP_##SRC_TYPE(value); \ REG = (DEST_TYPE)value; \ + d_m3TraceLoad(DEST_TYPE, operand, REG); \ nextOp (); \ } else d_outOfBounds; \ } @@ -820,6 +1309,7 @@ d_m3Load_i (i64, i64); #define d_m3Store(REG, SRC_TYPE, DEST_TYPE) \ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_rs) \ { \ + d_m3TracePrepare \ u64 operand = slot (u32); \ u32 offset = immediate (u32); \ operand += offset; \ @@ -827,6 +1317,7 @@ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_rs) \ if (m3MemCheck( \ operand + sizeof (DEST_TYPE) <= _mem->length \ )) { \ + d_m3TraceStore(SRC_TYPE, operand, REG); \ u8* mem8 = m3MemData(_mem) + operand; \ DEST_TYPE val = (DEST_TYPE) REG; \ M3_BSWAP_##DEST_TYPE(val); \ @@ -836,6 +1327,7 @@ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_rs) \ } \ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_sr) \ { \ + d_m3TracePrepare \ const SRC_TYPE value = slot (SRC_TYPE); \ u64 operand = (u32) _r0; \ u32 offset = immediate (u32); \ @@ -844,6 +1336,7 @@ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_sr) \ if (m3MemCheck( \ operand + sizeof (DEST_TYPE) <= _mem->length \ )) { \ + d_m3TraceStore(SRC_TYPE, operand, value); \ u8* mem8 = m3MemData(_mem) + operand; \ DEST_TYPE val = (DEST_TYPE) value; \ M3_BSWAP_##DEST_TYPE(val); \ @@ -853,6 +1346,7 @@ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_sr) \ } \ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_ss) \ { \ + d_m3TracePrepare \ const SRC_TYPE value = slot (SRC_TYPE); \ u64 operand = slot (u32); \ u32 offset = immediate (u32); \ @@ -861,6 +1355,7 @@ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_ss) \ if (m3MemCheck( \ operand + sizeof (DEST_TYPE) <= _mem->length \ )) { \ + d_m3TraceStore(SRC_TYPE, operand, value); \ u8* mem8 = m3MemData(_mem) + operand; \ DEST_TYPE val = (DEST_TYPE) value; \ M3_BSWAP_##DEST_TYPE(val); \ @@ -873,6 +1368,7 @@ d_m3Op (SRC_TYPE##_Store_##DEST_TYPE##_ss) \ #define d_m3StoreFp(REG, TYPE) \ d_m3Op (TYPE##_Store_##TYPE##_rr) \ { \ + d_m3TracePrepare \ u64 operand = (u32) _r0; \ u32 offset = immediate (u32); \ operand += offset; \ @@ -880,6 +1376,7 @@ d_m3Op (TYPE##_Store_##TYPE##_rr) \ if (m3MemCheck( \ operand + sizeof (TYPE) <= _mem->length \ )) { \ + d_m3TraceStore(TYPE, operand, REG); \ u8* mem8 = m3MemData(_mem) + operand; \ TYPE val = (TYPE) REG; \ M3_BSWAP_##TYPE(val); \ @@ -946,14 +1443,10 @@ d_m3RetSig debugOp (d_m3OpSig, cstr_t i_opcode) } puts (name); - return nextOpDirect(); + nextOpDirect(); } # endif -# if d_m3EnableOpTracing -d_m3OpDecl (DumpStack) -# endif - # if d_m3EnableOpProfiling typedef struct M3ProfilerSlot @@ -969,7 +1462,7 @@ d_m3RetSig profileOp (d_m3OpSig, cstr_t i_operationName) { ProfileHit (i_operationName); - return nextOpDirect(); + nextOpDirect(); } # endif diff --git a/src/m3_exec_defs.h b/src/m3_exec_defs.h index 13f96d8..7bb4e81 100644 --- a/src/m3_exec_defs.h +++ b/src/m3_exec_defs.h @@ -12,13 +12,17 @@ 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. -# define d_m3ClearRegisters _r0 = 0; _fp0 = 0.; +# define d_m3OpDefaultArgs 0, 0.0 +# define d_m3ClearRegisters _r0 = 0; _fp0 = 0.0; #else @@ -30,12 +34,17 @@ d_m3BeginExternC #endif -# define m3MemData(mem) (u8*)(((M3MemoryHeader*)(mem))+1) -# define m3MemRuntime(mem) (((M3MemoryHeader*)(mem))->runtime) -# define m3MemInfo(mem) (&(((M3MemoryHeader*)(mem))->runtime->memory)) - 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 nextOpImpl() ((IM3Operation)(* _pc))(_pc + 1, d_m3OpArgs) +#define jumpOpImpl(PC) ((IM3Operation)(* PC))( PC + 1, d_m3OpArgs) + +#define nextOpDirect() return nextOpImpl() +#define jumpOpDirect(PC) return jumpOpImpl((pc_t)(PC)) + d_m3EndExternC #endif // m3_exec_defs_h diff --git a/src/m3_info.c b/src/m3_info.c index a5f6a2c..7ce7dc7 100644 --- a/src/m3_info.c +++ b/src/m3_info.c @@ -9,8 +9,9 @@ #include "m3_info.h" #include "m3_emit.h" #include "m3_compile.h" +#include "m3_exec.h" -#if d_m3LogOutput +#ifdef DEBUG typedef struct OpInfo { @@ -65,26 +66,22 @@ cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType) sprintf (string, "("); - u32 numArgs = i_funcType->numArgs; - u32 numRets = i_funcType->numRets; - u8 * types = i_funcType->types; - - for (u32 i = 0; i < numArgs; ++i) + for (u32 i = 0; i < i_funcType->numArgs; ++i) { if (i != 0) strcat (string, ", "); - strcat (string, GetTypeName (types [numRets + i])); + strcat (string, GetTypeName (d_FuncArgType(i_funcType, i))); } strcat (string, ") -> "); - for (u32 i = 0; i < numRets; ++i) + for (u32 i = 0; i < i_funcType->numRets; ++i) { if (i != 0) strcat (string, ", "); - strcat (string, GetTypeName (types [i])); + strcat (string, GetTypeName (d_FuncRetType(i_funcType, i))); } return string; @@ -103,9 +100,9 @@ size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp, u8 i_type) len = snprintf (o_string, i_n, "%" PRIi64, * (i64 *) i_sp); #if d_m3HasFloat else if (i_type == c_m3Type_f32) - len = snprintf (o_string, i_n, "%f", * (f32 *) i_sp); + len = snprintf (o_string, i_n, "%" PRIf32, * (f32 *) i_sp); else if (i_type == c_m3Type_f64) - len = snprintf (o_string, i_n, "%lf", * (f64 *) i_sp); + len = snprintf (o_string, i_n, "%" PRIf64, * (f64 *) i_sp); #endif len = M3_MAX (0, len); @@ -131,11 +128,10 @@ cstr_t SPrintFunctionArgList (IM3Function i_function, m3stack_t i_sp) if (funcType) { u32 numArgs = funcType->numArgs; - u8 * argTypes = funcType->types + funcType->numRets; for (u32 i = 0; i < numArgs; ++i) { - u8 type = argTypes [i]; + u8 type = d_FuncArgType(funcType, i); ret = snprintf (s, e-s, "%s: ", c_waTypes [type]); s += M3_MAX (0, ret); @@ -205,7 +201,8 @@ d_m3Decoder (Entry) { IM3Function function = fetch (IM3Function); - sprintf (o_string, "%s", function->name); + // only prints out the first registered name for the function + sprintf (o_string, "%s", m3_GetFunctionName(function)); } @@ -452,5 +449,5 @@ void log_emit (IM3Compilation o, IM3Operation i_operation) # endif } -#endif //d_m3LogOutput +#endif // DEBUG diff --git a/src/m3_info.h b/src/m3_info.h index 48a133f..573f156 100644 --- a/src/m3_info.h +++ b/src/m3_info.h @@ -12,7 +12,7 @@ d_m3BeginExternC -#if d_m3LogOutput +#ifdef DEBUG void dump_type_stack (IM3Compilation o); void log_opcode (IM3Compilation o, u8 i_opcode); @@ -22,7 +22,7 @@ void log_emit (IM3Compilation o, IM3Operation i_operat cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); -#else // d_m3LogOutput +#else // DEBUG #define dump_type_stack(...) {} #define log_opcode(...) {} @@ -30,7 +30,7 @@ cstr_t SPrintFuncTypeSignature (IM3FuncType i_funcType); #define emit_stack_dump(...) {} #define log_emit(...) {} -#endif // d_m3LogOutput +#endif // DEBUG d_m3EndExternC diff --git a/src/m3_math_utils.h b/src/m3_math_utils.h index f9dab3e..8c1b091 100644 --- a/src/m3_math_utils.h +++ b/src/m3_math_utils.h @@ -153,27 +153,27 @@ u64 rotr64(u64 n, unsigned c) { * Integer Div, Rem */ -#define OP_DIV_U(RES, A, B) \ - if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ +#define OP_DIV_U(RES, A, B) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ RES = A / B; -#define OP_REM_U(RES, A, B) \ - if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ +#define OP_REM_U(RES, A, B) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ RES = A % B; // 2's complement detection #if (INT_MIN != -INT_MAX) - #define OP_DIV_S(RES, A, B, TYPE_MIN) \ - if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ - if (UNLIKELY(B == -1 and A == TYPE_MIN)) { \ - return m3Err_trapIntegerOverflow; \ - } \ + #define OP_DIV_S(RES, A, B, TYPE_MIN) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + if (UNLIKELY(B == -1 and A == TYPE_MIN)) { \ + newTrap (m3Err_trapIntegerOverflow); \ + } \ RES = A / B; - #define OP_REM_S(RES, A, B, TYPE_MIN) \ - if (UNLIKELY(B == 0)) return m3Err_trapDivisionByZero; \ - if (UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ + #define OP_REM_S(RES, A, B, TYPE_MIN) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + if (UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ else RES = A % B; #else @@ -189,10 +189,10 @@ u64 rotr64(u64 n, unsigned c) { #define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ if (UNLIKELY(isnan(A))) { \ - return m3Err_trapIntegerConversion; \ + newTrap (m3Err_trapIntegerConversion); \ } \ if (UNLIKELY(A <= RMIN or A >= RMAX)) { \ - return m3Err_trapIntegerOverflow; \ + newTrap (m3Err_trapIntegerOverflow); \ } \ RES = (TYPE)A; diff --git a/src/m3_module.c b/src/m3_module.c index 3322069..849856f 100644 --- a/src/m3_module.c +++ b/src/m3_module.c @@ -29,7 +29,7 @@ void m3_FreeModule (IM3Module i_module) Module_FreeFunctions (i_module); m3Free (i_module->functions); - m3Free (i_module->imports); + //m3Free (i_module->imports); m3Free (i_module->funcTypes); m3Free (i_module->dataSegments); m3Free (i_module->table0); @@ -66,28 +66,31 @@ _ (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)); - if (i_typeIndex < io_module->numFuncTypes) - { - IM3FuncType ft = io_module->funcTypes [i_typeIndex]; + _throwif("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes); - IM3Function func = Module_GetFunction (io_module, index); - func->funcType = ft; + IM3FuncType ft = io_module->funcTypes [i_typeIndex]; - if (i_importInfo) - { - func->import = * i_importInfo; - func->name = i_importInfo->fieldUtf8; - } + IM3Function func = Module_GetFunction (io_module, index); + func->funcType = ft; +#if d_m3EnableStrace >= 2 + func->index = index; +#endif - // m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); + if (i_importInfo and func->numNames == 0) + { + func->import = * i_importInfo; + func->numNames = 1; + func->names[0] = i_importInfo->fieldUtf8; } - else result = "type sig index out of bounds"; - _catch: return result; + // m3log (module, " added function: %3d; sig: %d", index, i_typeIndex); + +} _catch: + return result; } @@ -100,3 +103,18 @@ IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) return func; } + + +const char* m3_GetModuleName (IM3Module i_module) +{ + if (!i_module || !i_module->name) + return ""; + + return i_module->name; +} + +IM3Runtime m3_GetModuleRuntime (IM3Module i_module) +{ + return i_module ? i_module->runtime : NULL; +} + diff --git a/src/m3_optimize.c b/src/m3_optimize.c deleted file mode 100644 index 7730b5a..0000000 --- a/src/m3_optimize.c +++ /dev/null @@ -1,12 +0,0 @@ -// -// m3_optimize.c -// -// Created by Steven Massey on 4/27/19. -// Copyright © 2019 Steven Massey. All rights reserved. -// - -#include "m3_compile.h" -#include "m3_exec.h" - - -// not currently used diff --git a/src/m3_parse.c b/src/m3_parse.c index 0766a13..369bc45 100644 --- a/src/m3_parse.c +++ b/src/m3_parse.c @@ -227,9 +227,12 @@ _ (ReadLEB_u32 (& index, & i_bytes, i_end)); if (exportKind == d_externalKind_function) { - if (not io_module->functions [index].name) + _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions); + u16 numNames = io_module->functions [index].numNames; + if (numNames < d_m3MaxDuplicateFunctionImpl) { - io_module->functions [index].name = utf8; + io_module->functions [index].numNames++; + io_module->functions [index].names[numNames] = utf8; utf8 = NULL; // ownership transfered to M3Function } } @@ -263,8 +266,13 @@ M3Result Parse_InitExpr (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_ M3Result result = m3Err_none; // this doesn't generate code pages. just walks the wasm bytecode to find the end - IM3Runtime rt; - M3Compilation compilation = { rt= NULL, io_module, * io_bytes, i_end }; + +#if defined(d_m3PreferStaticAlloc) + static M3Compilation compilation; +#else + M3Compilation compilation; +#endif + compilation = (M3Compilation){ NULL, io_module, * io_bytes, i_end }; result = Compile_BlockStatements (& compilation); @@ -340,7 +348,7 @@ _ (NormalizeType (& normalType, wasmType)); func->module = io_module; func->wasm = start; func->wasmEnd = i_bytes; - func->ownsWasmCode = io_module->hasWasmCodeCopy; + //func->ownsWasmCode = io_module->hasWasmCodeCopy; func->numLocals = numLocals; } else _throw (m3Err_wasmSectionOverrun); @@ -472,9 +480,10 @@ _ (Read_utf8 (& name, & i_bytes, i_end)); if (index < io_module->numFunctions) { - if (not io_module->functions [index].name) + if (io_module->functions [index].numNames == 0) { - io_module->functions [index].name = name; m3log (parse, " naming function%5d: %s", index, name); + io_module->functions [index].numNames = 1; + io_module->functions [index].names[0] = name; m3log (parse, " naming function%5d: %s", index, name); name = NULL; // transfer ownership } // else m3log (parse, "prenamed: %s", io_module->functions [index].name); @@ -543,12 +552,15 @@ _ (m3Alloc (& module, M3Module, 1)); module->name = ".unnamed"; m3log (parse, "load module: %d bytes", i_numBytes); module->startFunction = -1; - module->hasWasmCodeCopy = false; + //module->hasWasmCodeCopy = false; module->environment = i_environment; const u8 * pos = i_bytes; const u8 * end = pos + i_numBytes; + module->wasmStart = pos; + module->wasmEnd = end; + u32 magic, version; _ (Read_u32 (& magic, & pos, end)); _ (Read_u32 (& version, & pos, end)); @@ -570,7 +582,7 @@ _ (ReadLEB_u7 (& section, & pos, end)); { u32 sectionLength; _ (ReadLEB_u32 (& sectionLength, & pos, end)); - _throwif(m3Err_wasmMalformed, pos + sectionLength > end); + _throwif(m3Err_wasmMalformed, pos + sectionLength > end); _ (ParseModuleSection (module, section, pos, sectionLength)); pos += sectionLength; diff --git a/src/wasm3.h b/src/wasm3.h index fa4428c..c6face8 100644 --- a/src/wasm3.h +++ b/src/wasm3.h @@ -10,12 +10,13 @@ #define M3_VERSION_MAJOR 0 #define M3_VERSION_MINOR 4 -#define M3_VERSION_REV 8 -#define M3_VERSION "0.4.8" +#define M3_VERSION_REV 9 +#define M3_VERSION "0.4.9" #include #include #include +#include #if defined(__cplusplus) extern "C" { @@ -28,7 +29,6 @@ struct M3Runtime; typedef struct M3Runtime * IM3Runtime; struct M3Module; typedef struct M3Module * IM3Module; struct M3Function; typedef struct M3Function * IM3Function; - typedef struct M3ErrorInfo { M3Result result; @@ -41,11 +41,28 @@ typedef struct M3ErrorInfo uint32_t line; const char * message; +} M3ErrorInfo; + +typedef struct M3BacktraceFrame +{ + uint32_t moduleOffset; + IM3Function function; + + struct M3BacktraceFrame * next; } -M3ErrorInfo; +M3BacktraceFrame, * IM3BacktraceFrame; +typedef struct M3BacktraceInfo +{ + IM3BacktraceFrame frames; + IM3BacktraceFrame lastFrame; // can be M3_BACKTRACE_TRUNCATED +} +M3BacktraceInfo, * IM3BacktraceInfo; + +// Constants +#define M3_BACKTRACE_TRUNCATED (void*)(SIZE_MAX) -enum // EWaTypes +typedef enum M3ValueType { c_m3Type_none = 0, c_m3Type_i32 = 1, @@ -54,21 +71,23 @@ enum // EWaTypes c_m3Type_f64 = 4, c_m3Type_unknown -}; +} M3ValueType; typedef struct M3ImportInfo { const char * moduleUtf8; const char * fieldUtf8; - -// unsigned char type; } -M3ImportInfo; - -typedef M3ImportInfo * IM3ImportInfo; +M3ImportInfo, * IM3ImportInfo; +typedef struct M3ImportContext +{ + void * userdata; + IM3Function function; +} +M3ImportContext, * IM3ImportContext; // ------------------------------------------------------------------------------------------------------------------------------- // error codes @@ -116,7 +135,6 @@ d_m3ErrorConst (functionStackOverflow, "compiling function overran its 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 (optimizerFailed, "optimizer failed") // not a fatal error. a result, // runtime errors d_m3ErrorConst (missingCompiledCode, "function is missing compiled m3 code") @@ -184,13 +202,16 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") 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 - typedef const void * (* M3RawCall) (IM3Runtime runtime, uint64_t * _sp, void * _mem, void * userdata); + // Calling m3_RunStart is optional + M3Result m3_RunStart (IM3Module i_module); + + typedef const void * (* M3RawCall) (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem); M3Result m3_LinkRawFunction (IM3Module io_module, const char * const i_moduleName, const char * const i_functionName, const char * const i_signature, - M3RawCall i_function); + M3RawCall i_function); M3Result m3_LinkRawFunctionEx (IM3Module io_module, const char * const i_moduleName, @@ -199,6 +220,9 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") M3RawCall i_function, const void * i_userdata); + const char* m3_GetModuleName (IM3Module i_module); + IM3Runtime m3_GetModuleRuntime (IM3Module i_module); + //------------------------------------------------------------------------------------------------------------------------------- // functions //------------------------------------------------------------------------------------------------------------------------------- @@ -208,14 +232,28 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") IM3Runtime i_runtime, const char * const i_functionName); - M3Result m3_Call (IM3Function i_function); - M3Result m3_CallWithArgs (IM3Function i_function, uint32_t i_argc, const char * const * i_argv); + uint32_t m3_GetArgCount (IM3Function i_function); + uint32_t m3_GetRetCount (IM3Function i_function); + M3ValueType m3_GetArgType (IM3Function i_function, uint32_t i_index); + M3ValueType m3_GetRetType (IM3Function i_function, uint32_t i_index); + + M3Result m3_CallV (IM3Function i_function, ...); + M3Result m3_CallVL (IM3Function i_function, va_list i_args); + M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[]); + M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]); + + M3Result m3_GetResultsV (IM3Function i_function, ...); + 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* info); + void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info); void m3_ResetErrorInfo (IM3Runtime i_runtime); + const char* m3_GetFunctionName (IM3Function i_function); + IM3Module m3_GetFunctionModule (IM3Function i_function); + //------------------------------------------------------------------------------------------------------------------------------- // debug info //------------------------------------------------------------------------------------------------------------------------------- @@ -224,6 +262,9 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") void m3_PrintM3Info (void); void m3_PrintProfilerInfo (void); + // 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); + #if defined(__cplusplus) } #endif