// // m3_env.c // // Created by Steven Massey on 4/19/19. // Copyright © 2019 Steven Massey. All rights reserved. // #include #include #include "m3_env.h" #include "m3_compile.h" #include "m3_exception.h" #include "m3_info.h" IM3Environment m3_NewEnvironment () { IM3Environment env = m3_AllocStruct (M3Environment); if (env) { _try { // create FuncTypes for all simple block return ValueTypes for (u8 t = c_m3Type_none; t <= c_m3Type_f64; t++) { IM3FuncType ftype; _ (AllocFuncType (& ftype, 1)); ftype->numArgs = 0; ftype->numRets = (t == c_m3Type_none) ? 0 : 1; ftype->types [0] = t; Environment_AddFuncType (env, & ftype); d_m3Assert (t < 5); env->retFuncTypes [t] = ftype; } } _catch: if (result) { m3_FreeEnvironment (env); env = NULL; } } return env; } ////////////////////////////////////////////////////////////////////////////////////////////////////////// //NOTE(martin): patched to allow controlling runtime memory externally void m3_RuntimeSetMemoryCallbacks(IM3Runtime runtime, m3_resize_proc resizeCallback, m3_free_proc freeCallback, void* userData) { runtime->resizeCallback = resizeCallback; runtime->freeCallback = freeCallback; runtime->memoryUserData = userData; } ////////////////////////////////////////////////////////////////////////////////////////////////////////// void Environment_Release (IM3Environment i_environment) { IM3FuncType ftype = i_environment->funcTypes; while (ftype) { IM3FuncType next = ftype->next; m3_Free (ftype); ftype = next; } m3log (runtime, "freeing %d pages from environment", CountCodePages (i_environment->pagesReleased)); FreeCodePages (& i_environment->pagesReleased); } void m3_FreeEnvironment (IM3Environment i_environment) { if (i_environment) { Environment_Release (i_environment); m3_Free (i_environment); } } void m3_SetCustomSectionHandler (IM3Environment i_environment, M3SectionHandler i_handler) { if (i_environment) i_environment->customSectionHandler = i_handler; } // returns the same io_funcType or replaces it with an equivalent that's already in the type linked list void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType) { IM3FuncType addType = * io_funcType; IM3FuncType newType = i_environment->funcTypes; while (newType) { if (AreFuncTypesEqual (newType, addType)) { m3_Free (addType); break; } newType = newType->next; } if (newType == NULL) { newType = addType; newType->next = i_environment->funcTypes; i_environment->funcTypes = newType; } * io_funcType = newType; } IM3CodePage RemoveCodePageOfCapacity (M3CodePage ** io_list, u32 i_minimumLineCount) { IM3CodePage prev = NULL; IM3CodePage page = * io_list; while (page) { if (NumFreeLines (page) >= i_minimumLineCount) { d_m3Assert (page->info.usageCount == 0); IM3CodePage next = page->info.next; if (prev) prev->info.next = next; // mid-list else * io_list = next; // front of list break; } prev = page; page = page->info.next; } return page; } IM3CodePage Environment_AcquireCodePage (IM3Environment i_environment, u32 i_minimumLineCount) { return RemoveCodePageOfCapacity (& i_environment->pagesReleased, i_minimumLineCount); } void Environment_ReleaseCodePages (IM3Environment i_environment, IM3CodePage i_codePageList) { IM3CodePage end = i_codePageList; 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) break; end = next; } if (end) { // push list to front end->info.next = i_environment->pagesReleased; i_environment->pagesReleased = i_codePageList; } } IM3Runtime m3_NewRuntime (IM3Environment i_environment, u32 i_stackSizeInBytes, void * i_userdata) { IM3Runtime runtime = m3_AllocStruct (M3Runtime); if (runtime) { m3_ResetErrorInfo(runtime); runtime->environment = i_environment; runtime->userdata = i_userdata; runtime->stack = m3_Malloc ("Wasm Stack", i_stackSizeInBytes + 4*sizeof (m3slot_t)); // TODO: more precise stack checks if (runtime->stack) { runtime->numStackSlots = i_stackSizeInBytes / sizeof (m3slot_t); m3log (runtime, "new stack: %p", runtime->stack); } else m3_Free (runtime); } return runtime; } void * m3_GetUserData (IM3Runtime i_runtime) { return i_runtime ? i_runtime->userdata : NULL; } void * ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info) { void * r = NULL; IM3Module module = i_runtime->modules; while (module) { IM3Module next = module->next; r = i_visitor (module, i_info); if (r) break; module = next; } return r; } void * _FreeModule (IM3Module i_module, void * i_info) { m3_FreeModule (i_module); return NULL; } void Runtime_Release (IM3Runtime i_runtime) { ForEachModule (i_runtime, _FreeModule, NULL); d_m3Assert (i_runtime->numActiveCodePages == 0); Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesOpen); Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesFull); m3_Free (i_runtime->stack); ////////////////////////////////////////////////////////////////////////////////////////////////////////// //NOTE(martin): patched to allow controlling runtime memory externally #if 0 m3_Free (i_runtime->memory.mallocated); #else if(i_runtime->freeCallback) { i_runtime->freeCallback(i_runtime->memory.mallocated, i_runtime->memoryUserData); } else { m3_Free(i_runtime->memory.mallocated); } #endif ////////////////////////////////////////////////////////////////////////////////////////////////////////// } void m3_FreeRuntime (IM3Runtime i_runtime) { if (i_runtime) { m3_PrintProfilerInfo (); Runtime_Release (i_runtime); m3_Free (i_runtime); } } M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type, bytes_t * io_bytes, cbytes_t i_end) { M3Result result = m3Err_none; // OPTZ: use a simplified interpreter for expressions // create a temporary runtime context #if defined(d_m3PreferStaticAlloc) static M3Runtime runtime; #else M3Runtime runtime; #endif M3_INIT (runtime); runtime.environment = i_module->runtime->environment; runtime.numStackSlots = i_module->runtime->numStackSlots; runtime.stack = i_module->runtime->stack; m3stack_t stack = (m3stack_t)runtime.stack; IM3Runtime savedRuntime = i_module->runtime; i_module->runtime = & runtime; IM3Compilation o = & runtime.compilation; o->runtime = & runtime; 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 // OPTZ: this code page could be erased after use. maybe have 'empty' list in addition to full and open? o->page = AcquireCodePage (& runtime); // AcquireUnusedCodePage (...) if (o->page) { IM3FuncType ftype = runtime.environment->retFuncTypes[i_type]; pc_t m3code = GetPagePC (o->page); result = CompileBlock (o, ftype, c_waOp_block); if (not result && o->maxStackSlots >= runtime.numStackSlots) { result = m3Err_trapStackOverflow; } if (not result) { m3ret_t r = RunCode (m3code, stack, NULL, d_m3OpDefaultArgs); if (r == 0) { m3log (runtime, "expression result: %s", SPrintValue (stack, i_type)); if (SizeOfType (i_type) == sizeof (u32)) { * (u32 *) o_expressed = * ((u32 *) stack); } else { * (u64 *) o_expressed = * ((u64 *) stack); } } } // TODO: EraseCodePage (...) see OPTZ above ReleaseCodePage (& runtime, o->page); } else result = m3Err_mallocFailedCodePage; runtime.stack = NULL; // prevent free(stack) in ReleaseRuntime Runtime_Release (& runtime); i_module->runtime = savedRuntime; * io_bytes = o->wasm; return result; } M3Result InitMemory (IM3Runtime io_runtime, IM3Module i_module) { M3Result result = m3Err_none; //d_m3Assert (not io_runtime->memory.wasmPages); if (not i_module->memoryImported) { u32 maxPages = i_module->memoryInfo.maxPages; io_runtime->memory.maxPages = maxPages ? maxPages : 65536; result = ResizeMemory (io_runtime, i_module->memoryInfo.initPages); } return result; } M3Result ResizeMemory (IM3Runtime io_runtime, u32 i_numPages) { M3Result result = m3Err_none; u32 numPagesToAlloc = i_numPages; M3Memory * memory = & io_runtime->memory; #if 0 // Temporary fix for memory allocation if (memory->mallocated) { memory->numPages = i_numPages; memory->mallocated->end = memory->wasmPages + (memory->numPages * c_m3MemPageSize); return result; } i_numPagesToAlloc = 256; #endif if (numPagesToAlloc <= memory->maxPages) { size_t numPageBytes = numPagesToAlloc * d_m3MemPageSize; #if d_m3MaxLinearMemoryPages > 0 _throwif("linear memory limitation exceeded", numPagesToAlloc > d_m3MaxLinearMemoryPages); #endif // Limit the amount of memory that gets actually allocated if (io_runtime->memoryLimit) { numPageBytes = M3_MIN (numPageBytes, io_runtime->memoryLimit); } size_t numBytes = numPageBytes + sizeof (M3MemoryHeader); size_t numPreviousBytes = memory->numPages * d_m3MemPageSize; if (numPreviousBytes) numPreviousBytes += sizeof (M3MemoryHeader); ////////////////////////////////////////////////////////////////////////////////////////////////////////// //NOTE(martin): patched to allow controlling runtime memory externally #if 0 void* newMem = m3_Realloc ("Wasm Linear Memory", memory->mallocated, numBytes, numPreviousBytes); #else void* newMem = 0; if(io_runtime->resizeCallback) { newMem = io_runtime->resizeCallback(memory->mallocated, numBytes, io_runtime->memoryUserData); #if d_m3LogHeapOps fprintf(stderr, PRIts ";heap:ReallocMem;%s;;%zu;%p;%zu;%p\n", m3_GetTimestamp(), "Wasm Linear Memory", numBytes, newMem, numPreviousBytes, memory->mallocated); #endif } else { newMem = m3_Realloc ("Wasm Linear Memory", memory->mallocated, numBytes, numPreviousBytes); } #endif ////////////////////////////////////////////////////////////////////////////////////////////////////////// _throwifnull(newMem); memory->mallocated = (M3MemoryHeader*)newMem; # if d_m3LogRuntime M3MemoryHeader * oldMallocated = memory->mallocated; # endif memory->numPages = numPagesToAlloc; memory->mallocated->length = numPageBytes; memory->mallocated->runtime = io_runtime; memory->mallocated->maxStack = (m3slot_t *) io_runtime->stack + io_runtime->numStackSlots; m3log (runtime, "resized old: %p; mem: %p; length: %zu; pages: %d", oldMallocated, memory->mallocated, memory->mallocated->length, memory->numPages); } else result = m3Err_wasmMemoryOverflow; _catch: return result; } M3Result InitGlobals (IM3Module io_module) { M3Result result = m3Err_none; if (io_module->numGlobals) { // placing the globals in their structs isn't good for cache locality, but i don't really know what the global // access patterns typically look like yet. // io_module->globalMemory = m3Alloc (m3reg_t, io_module->numGlobals); // if (io_module->globalMemory) { for (u32 i = 0; i < io_module->numGlobals; ++i) { M3Global * g = & io_module->globals [i]; m3log (runtime, "initializing global: %d", i); if (g->initExpr) { bytes_t start = g->initExpr; result = EvaluateExpression (io_module, & g->intValue, g->type, & start, g->initExpr + g->initExprSize); if (not result) { // io_module->globalMemory [i] = initValue; } else break; } else { m3log (runtime, "importing global"); } } } // else result = ErrorModule (m3Err_mallocFailed, io_module, "could allocate globals for module: '%s", io_module->name); } return result; } M3Result InitDataSegments (M3Memory * io_memory, IM3Module io_module) { M3Result result = m3Err_none; _throwif ("unallocated linear memory", !(io_memory->mallocated)); for (u32 i = 0; i < io_module->numDataSegments; ++i) { M3DataSegment * segment = & io_module->dataSegments [i]; i32 segmentOffset; bytes_t start = segment->initExpr; _ (EvaluateExpression (io_module, & segmentOffset, c_m3Type_i32, & start, segment->initExpr + segment->initExprSize)); m3log (runtime, "loading data segment: %d; size: %d; offset: %d", i, segment->size, segmentOffset); if (segmentOffset >= 0 && (size_t)(segmentOffset) + segment->size <= io_memory->mallocated->length) { u8 * dest = m3MemData (io_memory->mallocated) + segmentOffset; memcpy (dest, segment->data, segment->size); } else { _throw ("data segment out of bounds"); } } _catch: return result; } M3Result InitElements (IM3Module io_module) { M3Result result = m3Err_none; bytes_t bytes = io_module->elementSection; cbytes_t end = io_module->elementSectionEnd; for (u32 i = 0; i < io_module->numElementSegments; ++i) { u32 index; _ (ReadLEB_u32 (& index, & bytes, end)); if (index == 0) { i32 offset; _ (EvaluateExpression (io_module, & offset, c_m3Type_i32, & bytes, end)); _throwif ("table underflow", offset < 0); u32 numElements; _ (ReadLEB_u32 (& numElements, & bytes, end)); size_t endElement = (size_t) numElements + offset; _throwif ("table overflow", endElement > d_m3MaxSaneTableSize); // is there any requirement that elements must be in increasing sequence? // make sure the table isn't shrunk. if (endElement > io_module->table0Size) { io_module->table0 = m3_ReallocArray (IM3Function, io_module->table0, endElement, io_module->table0Size); io_module->table0Size = (u32) endElement; } _throwifnull(io_module->table0); for (u32 e = 0; e < numElements; ++e) { 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 ("element table index must be zero for MVP"); } _catch: return result; } M3Result m3_CompileModule (IM3Module io_module) { M3Result result = m3Err_none; for (u32 i = 0; i < io_module->numFunctions; ++i) { IM3Function f = & io_module->functions [i]; if (f->wasm and not f->compiled) { _ (CompileFunction (f)); } } _catch: return result; } M3Result m3_RunStart (IM3Module io_module) { #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // Execution disabled for fuzzing builds return m3Err_none; #endif M3Result result = m3Err_none; i32 startFunctionTmp = -1; if (io_module and io_module->startFunction >= 0) { IM3Function function = & io_module->functions [io_module->startFunction]; if (not function->compiled) { _ (CompileFunction (function)); } IM3FuncType ftype = function->funcType; if (ftype->numArgs != 0 || ftype->numRets != 0) _throw (m3Err_argumentCountMismatch); IM3Module module = function->module; IM3Runtime runtime = module->runtime; startFunctionTmp = io_module->startFunction; io_module->startFunction = -1; result = (M3Result) RunCode (function->compiled, (m3stack_t) runtime->stack, runtime->memory.mallocated, d_m3OpDefaultArgs); if (result) { io_module->startFunction = startFunctionTmp; EXCEPTION_PRINT(result); goto _catch; } } _catch: return result; } // TODO: deal with main + side-modules loading efforcement M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module) { M3Result result = m3Err_none; if (M3_UNLIKELY(io_module->runtime)) { return m3Err_moduleAlreadyLinked; } io_module->runtime = io_runtime; M3Memory * memory = & io_runtime->memory; _ (InitMemory (io_runtime, io_module)); _ (InitGlobals (io_module)); _ (InitDataSegments (memory, io_module)); _ (InitElements (io_module)); // Start func might use imported functions, which are not liked here yet, // so it will be called before a function call is attempted (in m3_FindFunction) #ifdef DEBUG Module_GenerateNames(io_module); #endif io_module->next = io_runtime->modules; io_runtime->modules = io_module; return result; // ok _catch: io_module->runtime = NULL; return result; } IM3Global m3_FindGlobal (IM3Module io_module, const char * const i_globalName) { // Search exports for (u32 i = 0; i < io_module->numGlobals; ++i) { IM3Global g = & io_module->globals [i]; if (g->name and strcmp (g->name, i_globalName) == 0) { return g; } } // Search imports for (u32 i = 0; i < io_module->numGlobals; ++i) { IM3Global g = & io_module->globals [i]; if (g->import.moduleUtf8 and g->import.fieldUtf8) { if (strcmp (g->import.fieldUtf8, i_globalName) == 0) { return g; } } } return NULL; } M3Result m3_GetGlobal (IM3Global i_global, IM3TaggedValue o_value) { if (not i_global) return m3Err_globalLookupFailed; switch (i_global->type) { case c_m3Type_i32: o_value->value.i32 = i_global->intValue; break; case c_m3Type_i64: o_value->value.i64 = i_global->intValue; break; # if d_m3HasFloat case c_m3Type_f32: o_value->value.f32 = i_global->f32Value; break; case c_m3Type_f64: o_value->value.f64 = i_global->f64Value; break; # endif default: return m3Err_invalidTypeId; } o_value->type = (M3ValueType)(i_global->type); return m3Err_none; } M3Result m3_SetGlobal (IM3Global i_global, const IM3TaggedValue i_value) { if (not i_global) return m3Err_globalLookupFailed; // TODO: if (not g->isMutable) return m3Err_globalNotMutable; if (i_global->type != i_value->type) return m3Err_globalTypeMismatch; switch (i_value->type) { case c_m3Type_i32: i_global->intValue = i_value->value.i32; break; case c_m3Type_i64: i_global->intValue = i_value->value.i64; break; # if d_m3HasFloat case c_m3Type_f32: i_global->f32Value = i_value->value.f32; break; case c_m3Type_f64: i_global->f64Value = i_value->value.f64; break; # endif default: return m3Err_invalidTypeId; } return m3Err_none; } M3ValueType m3_GetGlobalType (IM3Global i_global) { return (i_global) ? (M3ValueType)(i_global->type) : c_m3Type_none; } void * v_FindFunction (IM3Module i_module, const char * const i_name) { for (u32 i = 0; i < i_module->numFunctions; ++i) { IM3Function f = & i_module->functions [i]; bool isImported = f->import.moduleUtf8 or f->import.fieldUtf8; if (isImported) continue; for (int j = 0; j < f->numNames; j++) { if (f->names [j] and strcmp (f->names [j], i_name) == 0) return f; } } return NULL; } M3Result m3_FindFunction (IM3Function * o_function, IM3Runtime i_runtime, const char * const i_functionName) { M3Result result = m3Err_none; d_m3Assert (o_function and i_runtime and i_functionName); IM3Function function = NULL; if (not i_runtime->modules) { _throw ("no modules loaded"); } function = (IM3Function) ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName); if (function) { if (not function->compiled) { _ (CompileFunction (function)) } } else _throw (ErrorModule (m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName)); _catch: if (result) function = NULL; * o_function = function; return result; } static M3Result checkStartFunction(IM3Module i_module) { M3Result result = m3Err_none; d_m3Assert(i_module); // Check if start function needs to be called if (i_module->startFunction >= 0) { result = m3_RunStart (i_module); } return result; } uint32_t m3_GetArgCount (IM3Function i_function) { if (i_function) { IM3FuncType ft = i_function->funcType; if (ft) { return ft->numArgs; } } return 0; } uint32_t m3_GetRetCount (IM3Function i_function) { if (i_function) { IM3FuncType ft = i_function->funcType; if (ft) { return ft->numRets; } } return 0; } M3ValueType m3_GetArgType (IM3Function i_function, uint32_t index) { if (i_function) { IM3FuncType ft = i_function->funcType; if (ft and index < ft->numArgs) { return (M3ValueType)d_FuncArgType(ft, index); } } return c_m3Type_none; } 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; } u8 * GetStackPointerForArgs (IM3Function i_function) { u64 * stack = (u64 *) i_function->module->runtime->stack; IM3FuncType ftype = i_function->funcType; stack += ftype->numRets; return (u8 *) stack; } M3Result m3_CallV (IM3Function i_function, ...) { va_list ap; va_start(ap, i_function); M3Result r = m3_CallVL(i_function, ap); va_end(ap); return r; } static void ReportNativeStackUsage () { # if d_m3LogNativeStack int stackUsed = m3StackGetMax(); fprintf (stderr, "Native stack used: %d\n", stackUsed); # endif } M3Result m3_CallVL (IM3Function i_function, va_list i_args) { IM3Runtime runtime = i_function->module->runtime; IM3FuncType ftype = i_function->funcType; M3Result result = m3Err_none; if (!i_function->compiled) { return m3Err_missingCompiledCode; } # if d_m3RecordBacktraces ClearBacktrace (runtime); # endif u8* s = GetStackPointerForArgs (i_function); for (u32 i = 0; i < ftype->numArgs; ++i) { switch (d_FuncArgType(ftype, i)) { case c_m3Type_i32: *(i32*)(s) = va_arg(i_args, i32); s += 8; break; case c_m3Type_i64: *(i64*)(s) = va_arg(i_args, i64); s += 8; break; # if d_m3HasFloat case c_m3Type_f32: *(f32*)(s) = va_arg(i_args, f64); s += 8; break; // f32 is passed as f64 case c_m3Type_f64: *(f64*)(s) = va_arg(i_args, f64); s += 8; break; # endif default: return "unknown argument type"; } } m3StackCheckInit(); _ (checkStartFunction(i_function->module)) result = (M3Result) RunCode (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); ReportNativeStackUsage (); runtime->lastCalled = result ? NULL : i_function; _catch: return result; } 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; M3Result result = m3Err_none; if (i_argc != ftype->numArgs) { return m3Err_argumentCountMismatch; } if (!i_function->compiled) { return m3Err_missingCompiledCode; } # if d_m3RecordBacktraces ClearBacktrace (runtime); # endif u8* s = GetStackPointerForArgs (i_function); for (u32 i = 0; i < ftype->numArgs; ++i) { switch (d_FuncArgType(ftype, i)) { case c_m3Type_i32: *(i32*)(s) = *(i32*)i_argptrs[i]; s += 8; break; case c_m3Type_i64: *(i64*)(s) = *(i64*)i_argptrs[i]; s += 8; break; # if d_m3HasFloat case c_m3Type_f32: *(f32*)(s) = *(f32*)i_argptrs[i]; s += 8; break; case c_m3Type_f64: *(f64*)(s) = *(f64*)i_argptrs[i]; s += 8; break; # endif default: return "unknown argument type"; } } m3StackCheckInit(); _ (checkStartFunction(i_function->module)) result = (M3Result) RunCode (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); ReportNativeStackUsage (); runtime->lastCalled = result ? NULL : i_function; _catch: return result; } M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char * i_argv[]) { IM3FuncType ftype = i_function->funcType; IM3Runtime runtime = i_function->module->runtime; M3Result result = m3Err_none; if (i_argc != ftype->numArgs) { return m3Err_argumentCountMismatch; } if (!i_function->compiled) { return m3Err_missingCompiledCode; } # if d_m3RecordBacktraces ClearBacktrace (runtime); # endif u8* s = GetStackPointerForArgs (i_function); for (u32 i = 0; i < ftype->numArgs; ++i) { switch (d_FuncArgType(ftype, i)) { case c_m3Type_i32: *(i32*)(s) = strtoul(i_argv[i], NULL, 10); s += 8; break; case c_m3Type_i64: *(i64*)(s) = strtoull(i_argv[i], NULL, 10); s += 8; break; # if d_m3HasFloat case c_m3Type_f32: *(f32*)(s) = strtod(i_argv[i], NULL); s += 8; break; // strtof would be less portable case c_m3Type_f64: *(f64*)(s) = strtod(i_argv[i], NULL); s += 8; break; # endif default: return "unknown argument type"; } } m3StackCheckInit(); _ (checkStartFunction(i_function->module)) result = (M3Result) RunCode (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs); ReportNativeStackUsage (); runtime->lastCalled = result ? NULL : i_function; _catch: return result; } //u8 * AlignStackPointerTo64Bits (const u8 * i_stack) //{ // uintptr_t ptr = (uintptr_t) i_stack; // return (u8 *) ((ptr + 7) & ~7); //} M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[]) { IM3FuncType ftype = i_function->funcType; IM3Runtime runtime = i_function->module->runtime; if (i_retc != ftype->numRets) { return m3Err_argumentCountMismatch; } if (i_function != runtime->lastCalled) { return "function not called"; } u8* s = (u8*) runtime->stack; for (u32 i = 0; i < ftype->numRets; ++i) { switch (d_FuncRetType(ftype, i)) { case c_m3Type_i32: *(i32*)o_retptrs[i] = *(i32*)(s); s += 8; break; case c_m3Type_i64: *(i64*)o_retptrs[i] = *(i64*)(s); s += 8; break; # if d_m3HasFloat case c_m3Type_f32: *(f32*)o_retptrs[i] = *(f32*)(s); s += 8; break; case c_m3Type_f64: *(f64*)o_retptrs[i] = *(f64*)(s); s += 8; break; # endif default: return "unknown return type"; } } return m3Err_none; } 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; if (i_function != runtime->lastCalled) { return "function not called"; } 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; # if d_m3HasFloat case c_m3Type_f32: *va_arg(o_rets, f32*) = *(f32*)(s); s += 8; break; case c_m3Type_f64: *va_arg(o_rets, f64*) = *(f64*)(s); s += 8; break; # endif default: return "unknown argument type"; } } return m3Err_none; } void ReleaseCodePageNoTrack (IM3Runtime i_runtime, IM3CodePage i_codePage) { if (i_codePage) { IM3CodePage * list; bool pageFull = (NumFreeLines (i_codePage) < d_m3CodePageFreeLinesThreshold); if (pageFull) list = & i_runtime->pagesFull; else list = & i_runtime->pagesOpen; PushCodePage (list, i_codePage); m3log (emit, "release page: %d to queue: '%s'", i_codePage->info.sequence, pageFull ? "full" : "open") } } IM3CodePage AcquireCodePageWithCapacity (IM3Runtime i_runtime, u32 i_minLineCount) { IM3CodePage page = RemoveCodePageOfCapacity (& i_runtime->pagesOpen, i_minLineCount); if (not page) { page = Environment_AcquireCodePage (i_runtime->environment, i_minLineCount); if (not page) page = NewCodePage (i_runtime, i_minLineCount); if (page) i_runtime->numCodePages++; } if (page) { m3log (emit, "acquire page: %d", page->info.sequence); i_runtime->numActiveCodePages++; } return page; } IM3CodePage AcquireCodePage (IM3Runtime i_runtime) { return AcquireCodePageWithCapacity (i_runtime, d_m3CodePageFreeLinesThreshold); } void ReleaseCodePage (IM3Runtime i_runtime, IM3CodePage i_codePage) { if (i_codePage) { ReleaseCodePageNoTrack (i_runtime, i_codePage); i_runtime->numActiveCodePages--; # if defined (DEBUG) u32 numOpen = CountCodePages (i_runtime->pagesOpen); u32 numFull = CountCodePages (i_runtime->pagesFull); m3log (runtime, "runtime: %p; open-pages: %d; full-pages: %d; active: %d; total: %d", i_runtime, numOpen, numFull, i_runtime->numActiveCodePages, i_runtime->numCodePages); d_m3Assert (numOpen + numFull + i_runtime->numActiveCodePages == i_runtime->numCodePages); # if d_m3LogCodePages dump_code_page (i_codePage, /* startPC: */ NULL); # endif # endif } } #if d_m3VerboseErrorMessages M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function, const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...) { if (i_runtime) { i_runtime->error = (M3ErrorInfo){ .result = i_result, .runtime = i_runtime, .module = i_module, .function = i_function, .file = i_file, .line = i_lineNum }; i_runtime->error.message = i_runtime->error_message; va_list args; va_start (args, i_errorMessage); vsnprintf (i_runtime->error_message, sizeof(i_runtime->error_message), i_errorMessage, args); va_end (args); } return i_result; } #endif void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info) { if (i_runtime) { *o_info = i_runtime->error; m3_ResetErrorInfo (i_runtime); } } void m3_ResetErrorInfo (IM3Runtime i_runtime) { 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) { uint8_t * memory = NULL; d_m3Assert (i_memoryIndex == 0); if (i_runtime) { u32 size = (u32) i_runtime->memory.mallocated->length; if (o_memorySizeInBytes) * o_memorySizeInBytes = size; if (size) memory = m3MemData (i_runtime->memory.mallocated); } return memory; } uint32_t m3_GetMemorySize (IM3Runtime i_runtime) { return i_runtime->memory.mallocated->length; } M3BacktraceInfo * m3_GetBacktrace (IM3Runtime i_runtime) { # if d_m3RecordBacktraces return & i_runtime->backtrace; # else return NULL; # endif }