You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wasm3/source/m3_env.c

1010 lines
26 KiB
C

//
// m3_env.c
//
// Created by Steven Massey on 4/19/19.
// Copyright © 2019 Steven Massey. All rights reserved.
//
#include <stdarg.h>
#include "m3_env.h"
#include "m3_compile.h"
#include "m3_exec.h"
#include "m3_exception.h"
#include "m3_info.h"
M3Result AllocFuncType (IM3FuncType * o_functionType, u32 i_numArgs)
{
size_t funcTypeSize = sizeof (M3FuncType) - 3 /* sizeof (argTypes [3]) */ + i_numArgs;
return m3Alloc (o_functionType, u8, funcTypeSize);
}
bool AreFuncTypesEqual (const IM3FuncType i_typeA, const IM3FuncType i_typeB)
{
if (i_typeA->returnType == i_typeB->returnType)
{
if (i_typeA->numArgs == i_typeB->numArgs)
{
return (memcmp (i_typeA->argTypes, i_typeB->argTypes, i_typeA->numArgs) == 0);
}
}
return false;
}
void Runtime_ReleaseCodePages (IM3Runtime i_runtime)
{
}
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)
{
m3Free (i_function->name);
}
FreeImportInfo (& i_function->import);
if (i_function->ownsWasmCode)
m3Free (i_function->wasm);
// Function_FreeCompiledCode (func);
# if (d_m3EnableCodePageRefCounting)
{
m3Free (i_function->codePageRefs);
i_function->numCodePageRefs = 0;
}
# endif
}
void Function_FreeCompiledCode (IM3Function i_function)
{
# if (d_m3EnableCodePageRefCounting)
{
i_function->compiled = NULL;
while (i_function->numCodePageRefs--)
{
IM3CodePage page = i_function->codePageRefs [i_function->numCodePageRefs];
if (--(page->info.usageCount) == 0)
{
// printf ("free %p\n", page);
}
}
m3Free (i_function->codePageRefs);
Runtime_ReleaseCodePages (i_function->module->runtime);
}
# endif
}
cstr_t GetFunctionName (IM3Function i_function)
{
if (i_function->import.fieldUtf8)
return i_function->import.fieldUtf8;
else
return (i_function->name) ? i_function->name : "<unnamed>";
}
cstr_t GetFunctionImportModuleName (IM3Function i_function)
{
return (i_function->import.moduleUtf8) ? i_function->import.moduleUtf8 : "";
}
u32 GetFunctionNumArgs (IM3Function i_function)
{
u32 numArgs = 0;
if (i_function)
{
if (i_function->funcType)
numArgs = i_function->funcType->numArgs;
}
return numArgs;
}
u32 GetFunctionNumReturns (IM3Function i_function)
{
u32 numReturns = 0;
if (i_function->funcType)
numReturns = i_function->funcType->returnType ? 1 : 0;
return numReturns;
}
u8 GetFunctionReturnType (IM3Function i_function)
{
u8 returnType = c_m3Type_none;
if (i_function->funcType)
returnType = i_function->funcType->returnType;
return returnType;
}
u32 GetFunctionNumArgsAndLocals (IM3Function i_function)
{
if (i_function)
return i_function->numLocals + GetFunctionNumArgs (i_function);
else
return 0;
}
void FreeImportInfo (M3ImportInfo * i_info)
{
m3Free (i_info->moduleUtf8);
m3Free (i_info->fieldUtf8);
}
IM3Environment m3_NewEnvironment ()
{
IM3Environment env = NULL;
m3Alloc (& env, M3Environment, 1);
return env;
}
void Environment_Release (IM3Environment i_environment)
{
IM3FuncType ftype = i_environment->funcTypes;
while (ftype)
{
IM3FuncType next = ftype->next;
m3Free (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);
m3Free (i_environment);
}
}
void Environment_AddFuncType (IM3Environment i_environment, IM3FuncType * io_funcType)
{
IM3FuncType addType = * io_funcType;
IM3FuncType newType = i_environment->funcTypes;
while (newType)
{
if (AreFuncTypesEqual (newType, addType))
{
m3Free (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
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, M3StackInfo * i_nativeStackInfo)
{
IM3Runtime runtime = NULL;
m3Alloc (& runtime, M3Runtime, 1);
if (runtime)
{
m3_ResetErrorInfo(runtime);
runtime->environment = i_environment;
m3Alloc (& runtime->stack, u8, i_stackSizeInBytes);
if (runtime->stack)
{
runtime->numStackSlots = i_stackSizeInBytes / sizeof (m3slot_t); m3log (runtime, "new stack: %p", runtime->stack);
}
else m3Free (runtime);
}
return runtime;
}
typedef void * (* ModuleVisitor) (IM3Module i_module, void * i_info);
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 FreeCompilationPatches (IM3Compilation o)
{
IM3BranchPatch patches = o->releasedPatches;
while (patches)
{
IM3BranchPatch next = patches->next;
m3Free (patches);
patches = next;
}
}
void Runtime_Release (IM3Runtime i_runtime)
{
ForEachModule (i_runtime, _FreeModule, NULL); d_m3Assert (i_runtime->numActiveCodePages == 0);
Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesOpen);
Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesFull);
FreeCompilationPatches (& i_runtime->compilation);
m3Free (i_runtime->stack);
m3Free (i_runtime->memory.mallocated);
}
void m3_FreeRuntime (IM3Runtime i_runtime)
{
if (i_runtime)
{
m3_PrintProfilerInfo ();
Runtime_Release (i_runtime);
m3Free (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;
m3slot_t stack [d_m3MaxFunctionSlots]; // stack on the stack
// create a temporary runtime context
M3Runtime runtime;
M3_INIT (runtime);
runtime.environment = i_module->runtime->environment;
runtime.numStackSlots = d_m3MaxFunctionSlots;
runtime.stack = & 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->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)
{
pc_t m3code = GetPagePC (o->page);
result = CompileBlock (o, i_type, c_waOp_block);
if (not result)
{
m3ret_t r = Call (m3code, stack, NULL, d_m3OpDefaultArgs);
result = runtime.runtimeError;
if (r == 0 and not result)
{
if (SizeOfType (i_type) == sizeof (u32))
{
* (u32 *) o_expressed = * ((u32 *) stack);
}
else if (SizeOfType (i_type) == sizeof (u64))
{
* (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;
// Limit the amount of memory that gets 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);
_ (m3Reallocate (& memory->mallocated, numBytes, numPreviousBytes));
# 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 typcially 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;
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 (io_memory->mallocated)
{
u8 * dest = m3MemData (io_memory->mallocated) + segmentOffset;
if ((size_t) segmentOffset + segment->size <= io_memory->mallocated->length)
memcpy (dest, segment->data, segment->size);
else
_throw ("data segment overflowing linear memory");
}
else _throw ("unallocated linear memory");
}
_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));
u32 numElements;
_ (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;
for (u32 e = 0; e < numElements; ++e)
{
u32 functionIndex;
_ (ReadLEB_u32 (& functionIndex, & bytes, end));
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");
}
}
else _throw ("table overflow");
}
else _throw ("element table index must be zero for MVP");
}
_catch: return result;
}
M3Result InitStartFunc (IM3Module io_module)
{
M3Result result = m3Err_none;
if (io_module->startFunction >= 0)
{
IM3Function function = & io_module->functions [io_module->startFunction];
if (not function->compiled)
{
_ (Compile_Function (function));
}
_ (m3_Call(function));
}
_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 (not io_module->runtime)
{
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));
io_module->next = io_runtime->modules;
io_runtime->modules = io_module;
// Functions expect module to be linked to a runtime, so we call start here
_ (InitStartFunc (io_module));
}
else result = m3Err_moduleAlreadyLinked;
if (result)
io_module->runtime = NULL;
_catch: return result;
}
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];
if (f->name)
{
if (strcmp (f->name, 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;
if (!i_runtime->modules) {
return "no modules loaded";
}
IM3Function function = (IM3Function) ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName);
if (function)
{
if (not function->compiled)
{
result = Compile_Function (function);
if (result)
function = NULL;
}
}
else result = ErrorModule (m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName);
* o_function = function;
return result;
}
M3Result m3_Call (IM3Function i_function)
{
return m3_CallWithArgs (i_function, 0, NULL);
}
M3Result m3_CallWithArgs (IM3Function i_function, uint32_t i_argc, const char * const * i_argv)
{
M3Result result = m3Err_none;
if (i_function->compiled)
{
IM3Module module = i_function->module;
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;
IM3FuncType ftype = i_function->funcType; m3log (runtime, "calling %s", SPrintFuncTypeSignature (ftype));
if (i_argc != ftype->numArgs)
_throw (m3Err_argumentCountMismatch);
// args are always 64-bit aligned
u64 * stack = (u64 *) runtime->stack;
// 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->argTypes[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");
}
}
m3StackCheckInit();
_ ((M3Result) Call (i_function->compiled, (m3stack_t) stack, runtime->memory.mallocated, d_m3OpDefaultArgs));
#if d_m3LogOutput
switch (ftype->returnType) {
case c_m3Type_none: fprintf (stderr, "Result: <Empty Stack>\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: fprintf (stderr, "Result: %u\n", *(u32*)(stack)); break;
case c_m3Type_f32: {
union { u32 u; f32 f; } union32;
union32.f = * (f32 *)(stack);
fprintf (stderr, "Result: %u\n", union32.u );
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");
}
#if d_m3LogNativeStack
size_t stackUsed = m3StackGetMax();
fprintf (stderr, "Native stack used: %d\n", stackUsed);
#endif // d_m3LogNativeStack
#endif // d_m3LogOutput
//u64 value = * (u64 *) (stack);
//m3log (runtime, "return64: %" PRIu64 " return32: %u", value, (u32) value);
}
else _throw (m3Err_missingCompiledCode);
_catch: return result;
}
#if 0
M3Result m3_CallMain (IM3Function i_function, uint32_t i_argc, const char * const * i_argv)
{
M3Result result = m3Err_none;
if (i_function->compiled)
{
IM3Module module = i_function->module;
IM3Runtime runtime = module->runtime;
u8 * linearMemory = runtime->memory.wasmPages;
m3stack_t stack = (m3stack_t) runtime->stack;
if (i_argc)
{
IM3Memory memory = & runtime->memory;
// FIX: memory allocation in general
i32 offset = AllocatePrivateHeap (memory, sizeof (i32) * i_argc);
i32 * pointers = (i32 *) (memory->wasmPages + offset);
for (u32 i = 0; i < i_argc; ++i)
{
size_t argLength = strlen (i_argv [i]) + 1;
if (argLength < 4000)
{
i32 o = AllocatePrivateHeap (memory, (i32) argLength);
memcpy (memory->wasmPages + o, i_argv [i], argLength);
* pointers++ = o;
}
else _throw ("insane argument string length");
}
stack [0] = i_argc;
stack [1] = offset;
}
_ ((M3Result)Call (i_function->compiled, stack, linearMemory, d_m3OpDefaultArgs));
//u64 value = * (u64 *) (stack);
//m3log (runtime, "return64: % " PRIu64 " return32: %" PRIu32, value, (u32) value);
}
else _throw (m3Err_missingCompiledCode);
_catch: return result;
}
#endif
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_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_m3VerboseLogs
M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function,
const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...)
{
if (i_runtime)
{
i_runtime->error = (M3ErrorInfo){ i_result, i_runtime, i_module, i_function, i_file, i_lineNum };
i_runtime->error.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* info)
{
*info = i_runtime->error;
m3_ResetErrorInfo (i_runtime);
}
void m3_ResetErrorInfo (IM3Runtime i_runtime)
{
M3_INIT(i_runtime->error);
i_runtime->error.message = "";
}
void GetStackInfo (M3StackInfo * io_info)
{
io_info->startAddr = (void *) io_info;
bool stackGrowsDown = false;
stackGrowsDown = io_info->startAddr > (void *) & stackGrowsDown;
if (stackGrowsDown)
io_info->stackSize *= -1;
}
M3StackInfo m3_GetNativeStackInfo (i32 i_stackSize)
{
M3StackInfo info = { NULL, i_stackSize };
GetStackInfo (& info);
return info;
}
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;
}