1 //
2 //  m3_env.c
3 //
4 //  Created by Steven Massey on 4/19/19.
5 //  Copyright © 2019 Steven Massey. All rights reserved.
6 //
7 
8 #include <stdarg.h>
9 #include <limits.h>
10 
11 #include "m3_env.h"
12 #include "m3_compile.h"
13 #include "m3_exec.h"
14 #include "m3_exception.h"
15 #include "m3_info.h"
16 
17 
m3_NewEnvironment()18 IM3Environment  m3_NewEnvironment  ()
19 {
20     M3Result result = m3Err_none;
21 
22     IM3Environment env = m3_AllocStruct (M3Environment);
23 
24     if (env)
25     {
26         _try
27         {
28             // create FuncTypes for all simple block return ValueTypes
29             for (u8 t = c_m3Type_none; t <= c_m3Type_f64; t++)
30             {
31                 IM3FuncType ftype;
32 _               (AllocFuncType (& ftype, 1));
33 
34                 ftype->numArgs = 0;
35                 ftype->numRets = (t == c_m3Type_none) ? 0 : 1;
36                 ftype->types [0] = t;
37 
38                 Environment_AddFuncType (env, & ftype);
39 
40                 d_m3Assert (t < 5);
41                 env->retFuncTypes [t] = ftype;
42             }
43         }
44 
45         _catch:
46         if (result)
47         {
48             m3_FreeEnvironment (env);
49             env = NULL;
50         }
51     }
52 
53     return env;
54 }
55 
56 
Environment_Release(IM3Environment i_environment)57 void  Environment_Release  (IM3Environment i_environment)
58 {
59     IM3FuncType ftype = i_environment->funcTypes;
60 
61     while (ftype)
62     {
63         IM3FuncType next = ftype->next;
64         m3_Free (ftype);
65         ftype = next;
66     }
67 
68     m3log (runtime, "freeing %d pages from environment", CountCodePages (i_environment->pagesReleased));
69     FreeCodePages (& i_environment->pagesReleased);
70 }
71 
72 
m3_FreeEnvironment(IM3Environment i_environment)73 void  m3_FreeEnvironment  (IM3Environment i_environment)
74 {
75     if (i_environment)
76     {
77         Environment_Release (i_environment);
78         m3_Free (i_environment);
79     }
80 }
81 
82 
83 // returns the same io_funcType or replaces it with an equivalent that's already in the type linked list
Environment_AddFuncType(IM3Environment i_environment,IM3FuncType * io_funcType)84 void  Environment_AddFuncType  (IM3Environment i_environment, IM3FuncType * io_funcType)
85 {
86     IM3FuncType addType = * io_funcType;
87     IM3FuncType newType = i_environment->funcTypes;
88 
89     while (newType)
90     {
91         if (AreFuncTypesEqual (newType, addType))
92         {
93             m3_Free (addType);
94             break;
95         }
96 
97         newType = newType->next;
98     }
99 
100     if (newType == NULL)
101     {
102         newType = addType;
103         newType->next = i_environment->funcTypes;
104         i_environment->funcTypes = newType;
105     }
106 
107     * io_funcType = newType;
108 }
109 
110 
RemoveCodePageOfCapacity(M3CodePage ** io_list,u32 i_minimumLineCount)111 IM3CodePage RemoveCodePageOfCapacity (M3CodePage ** io_list, u32 i_minimumLineCount)
112 {
113     IM3CodePage prev = NULL;
114     IM3CodePage page = * io_list;
115 
116     while (page)
117     {
118         if (NumFreeLines (page) >= i_minimumLineCount)
119         {                                                           d_m3Assert (page->info.usageCount == 0);
120             IM3CodePage next = page->info.next;
121             if (prev)
122                 prev->info.next = next; // mid-list
123             else
124                 * io_list = next;       // front of list
125 
126             break;
127         }
128 
129         prev = page;
130         page = page->info.next;
131     }
132 
133     return page;
134 }
135 
136 
Environment_AcquireCodePage(IM3Environment i_environment,u32 i_minimumLineCount)137 IM3CodePage  Environment_AcquireCodePage (IM3Environment i_environment, u32 i_minimumLineCount)
138 {
139     return RemoveCodePageOfCapacity (& i_environment->pagesReleased, i_minimumLineCount);
140 }
141 
142 
Environment_ReleaseCodePages(IM3Environment i_environment,IM3CodePage i_codePageList)143 void  Environment_ReleaseCodePages  (IM3Environment i_environment, IM3CodePage i_codePageList)
144 {
145     IM3CodePage end = i_codePageList;
146 
147     while (end)
148     {
149         end->info.lineIndex = 0; // reset page
150 #if d_m3RecordBacktraces
151         end->info.mapping->size = 0;
152 #endif // d_m3RecordBacktraces
153 
154         IM3CodePage next = end->info.next;
155         if (not next)
156             break;
157 
158         end = next;
159     }
160 
161     if (end)
162     {
163         // push list to front
164         end->info.next = i_environment->pagesReleased;
165         i_environment->pagesReleased = i_codePageList;
166     }
167 }
168 
169 
m3_NewRuntime(IM3Environment i_environment,u32 i_stackSizeInBytes,void * i_userdata)170 IM3Runtime  m3_NewRuntime  (IM3Environment i_environment, u32 i_stackSizeInBytes, void * i_userdata)
171 {
172     IM3Runtime runtime = m3_AllocStruct (M3Runtime);
173 
174     if (runtime)
175     {
176         m3_ResetErrorInfo(runtime);
177 
178         runtime->environment = i_environment;
179         runtime->userdata = i_userdata;
180 
181         runtime->stack = m3_Malloc (i_stackSizeInBytes + 4*sizeof (m3slot_t)); // TODO: more precise stack checks
182 
183         if (runtime->stack)
184         {
185             runtime->numStackSlots = i_stackSizeInBytes / sizeof (m3slot_t);         m3log (runtime, "new stack: %p", runtime->stack);
186         }
187         else m3_Free (runtime);
188     }
189 
190     return runtime;
191 }
192 
m3_GetUserData(IM3Runtime i_runtime)193 void *  m3_GetUserData  (IM3Runtime i_runtime)
194 {
195     return i_runtime ? i_runtime->userdata : NULL;
196 }
197 
198 
ForEachModule(IM3Runtime i_runtime,ModuleVisitor i_visitor,void * i_info)199 void *  ForEachModule  (IM3Runtime i_runtime, ModuleVisitor i_visitor, void * i_info)
200 {
201     void * r = NULL;
202 
203     IM3Module module = i_runtime->modules;
204 
205     while (module)
206     {
207         IM3Module next = module->next;
208         r = i_visitor (module, i_info);
209         if (r)
210             break;
211 
212         module = next;
213     }
214 
215     return r;
216 }
217 
218 
_FreeModule(IM3Module i_module,void * i_info)219 void *  _FreeModule  (IM3Module i_module, void * i_info)
220 {
221     m3_FreeModule (i_module);
222     return NULL;
223 }
224 
225 
Runtime_Release(IM3Runtime i_runtime)226 void  Runtime_Release  (IM3Runtime i_runtime)
227 {
228     ForEachModule (i_runtime, _FreeModule, NULL);                   d_m3Assert (i_runtime->numActiveCodePages == 0);
229 
230     Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesOpen);
231     Environment_ReleaseCodePages (i_runtime->environment, i_runtime->pagesFull);
232 
233     m3_Free (i_runtime->stack);
234     m3_Free (i_runtime->memory.mallocated);
235 }
236 
237 
m3_FreeRuntime(IM3Runtime i_runtime)238 void  m3_FreeRuntime  (IM3Runtime i_runtime)
239 {
240     if (i_runtime)
241     {
242         m3_PrintProfilerInfo ();
243 
244         Runtime_Release (i_runtime);
245         m3_Free (i_runtime);
246     }
247 }
248 
EvaluateExpression(IM3Module i_module,void * o_expressed,u8 i_type,bytes_t * io_bytes,cbytes_t i_end)249 M3Result  EvaluateExpression  (IM3Module i_module, void * o_expressed, u8 i_type, bytes_t * io_bytes, cbytes_t i_end)
250 {
251     M3Result result = m3Err_none;
252 
253     // OPTZ: use a simplified interpreter for expressions
254 
255     // create a temporary runtime context
256 #if defined(d_m3PreferStaticAlloc)
257     static M3Runtime runtime;
258 #else
259     M3Runtime runtime;
260 #endif
261     M3_INIT (runtime);
262 
263     runtime.environment = i_module->runtime->environment;
264     runtime.numStackSlots = i_module->runtime->numStackSlots;
265     runtime.stack = i_module->runtime->stack;
266 
267     m3stack_t stack = (m3stack_t)runtime.stack;
268 
269     IM3Runtime savedRuntime = i_module->runtime;
270     i_module->runtime = & runtime;
271 
272     IM3Compilation o = & runtime.compilation;
273     o->runtime = & runtime;
274     o->module =  i_module;
275     o->wasm =    * io_bytes;
276     o->wasmEnd = i_end;
277     o->lastOpcodeStart = o->wasm;
278 
279     o->block.depth = -1;  // so that root compilation depth = 0
280 
281     //  OPTZ: this code page could be erased after use.  maybe have 'empty' list in addition to full and open?
282     o->page = AcquireCodePage (& runtime);  // AcquireUnusedCodePage (...)
283 
284     if (o->page)
285     {
286         IM3FuncType ftype = runtime.environment->retFuncTypes[i_type];
287 
288         pc_t m3code = GetPagePC (o->page);
289         result = CompileBlock (o, ftype, c_waOp_block);
290 
291         if (not result && o->maxStackSlots >= runtime.numStackSlots) {
292             result = m3Err_trapStackOverflow;
293         }
294 
295         if (not result)
296         {
297             m3ret_t r = Call (m3code, stack, NULL, d_m3OpDefaultArgs);
298 
299             if (r == 0)
300             {                                                                               m3log (runtime, "expression result: %s", SPrintValue (stack, i_type));
301                 if (SizeOfType (i_type) == sizeof (u32))
302                 {
303                     * (u32 *) o_expressed = * ((u32 *) stack);
304                 }
305                 else
306                 {
307                     * (u64 *) o_expressed = * ((u64 *) stack);
308                 }
309             }
310         }
311 
312         // TODO: EraseCodePage (...) see OPTZ above
313         ReleaseCodePage (& runtime, o->page);
314     }
315     else result = m3Err_mallocFailedCodePage;
316 
317     runtime.stack = NULL;        // prevent free(stack) in ReleaseRuntime
318     Runtime_Release (& runtime);
319     i_module->runtime = savedRuntime;
320 
321     * io_bytes = o->wasm;
322 
323     return result;
324 }
325 
326 
InitMemory(IM3Runtime io_runtime,IM3Module i_module)327 M3Result  InitMemory  (IM3Runtime io_runtime, IM3Module i_module)
328 {
329     M3Result result = m3Err_none;                                     //d_m3Assert (not io_runtime->memory.wasmPages);
330 
331     if (not i_module->memoryImported)
332     {
333         u32 maxPages = i_module->memoryInfo.maxPages;
334         io_runtime->memory.maxPages = maxPages ? maxPages : 65536;
335 
336         result = ResizeMemory (io_runtime, i_module->memoryInfo.initPages);
337     }
338 
339     return result;
340 }
341 
342 
ResizeMemory(IM3Runtime io_runtime,u32 i_numPages)343 M3Result  ResizeMemory  (IM3Runtime io_runtime, u32 i_numPages)
344 {
345     M3Result result = m3Err_none;
346 
347     u32 numPagesToAlloc = i_numPages;
348 
349     M3Memory * memory = & io_runtime->memory;
350 
351 #if 0 // Temporary fix for memory allocation
352     if (memory->mallocated) {
353         memory->numPages = i_numPages;
354         memory->mallocated->end = memory->wasmPages + (memory->numPages * c_m3MemPageSize);
355         return result;
356     }
357 
358     i_numPagesToAlloc = 256;
359 #endif
360 
361     if (numPagesToAlloc <= memory->maxPages)
362     {
363         size_t numPageBytes = numPagesToAlloc * d_m3MemPageSize;
364 
365 #if d_m3MaxLinearMemoryPages > 0
366         _throwif("linear memory limitation exceeded", numPagesToAlloc > d_m3MaxLinearMemoryPages);
367 #endif
368 
369         // Limit the amount of memory that gets actually allocated
370         if (io_runtime->memoryLimit) {
371             numPageBytes = M3_MIN (numPageBytes, io_runtime->memoryLimit);
372         }
373 
374         size_t numBytes = numPageBytes + sizeof (M3MemoryHeader);
375 
376         size_t numPreviousBytes = memory->numPages * d_m3MemPageSize;
377         if (numPreviousBytes)
378             numPreviousBytes += sizeof (M3MemoryHeader);
379 
380         void* newMem = m3_Realloc (memory->mallocated, numBytes, numPreviousBytes);
381         _throwifnull(newMem);
382 
383         memory->mallocated = (M3MemoryHeader*)newMem;
384 
385 # if d_m3LogRuntime
386         M3MemoryHeader * oldMallocated = memory->mallocated;
387 # endif
388 
389         memory->numPages = numPagesToAlloc;
390 
391         memory->mallocated->length =  numPageBytes;
392         memory->mallocated->runtime = io_runtime;
393 
394         memory->mallocated->maxStack = (m3slot_t *) io_runtime->stack + io_runtime->numStackSlots;
395 
396         m3log (runtime, "resized old: %p; mem: %p; length: %zu; pages: %d", oldMallocated, memory->mallocated, memory->mallocated->length, memory->numPages);
397     }
398     else result = m3Err_wasmMemoryOverflow;
399 
400     _catch: return result;
401 }
402 
403 
InitGlobals(IM3Module io_module)404 M3Result  InitGlobals  (IM3Module io_module)
405 {
406     M3Result result = m3Err_none;
407 
408     if (io_module->numGlobals)
409     {
410         // placing the globals in their structs isn't good for cache locality, but i don't really know what the global
411         // access patterns typcially look like yet.
412 
413         //          io_module->globalMemory = m3Alloc (m3reg_t, io_module->numGlobals);
414 
415         //          if (io_module->globalMemory)
416         {
417             for (u32 i = 0; i < io_module->numGlobals; ++i)
418             {
419                 M3Global * g = & io_module->globals [i];                        m3log (runtime, "initializing global: %d", i);
420 
421                 if (g->initExpr)
422                 {
423                     bytes_t start = g->initExpr;
424                     result = EvaluateExpression (io_module, & g->intValue, g->type, & start, g->initExpr + g->initExprSize);
425 
426                     if (not result)
427                     {
428                         // io_module->globalMemory [i] = initValue;
429                     }
430                     else break;
431                 }
432                 else
433                 {                                                               m3log (runtime, "importing global");
434 
435                 }
436             }
437         }
438         //          else result = ErrorModule (m3Err_mallocFailed, io_module, "could allocate globals for module: '%s", io_module->name);
439     }
440 
441     return result;
442 }
443 
444 
InitDataSegments(M3Memory * io_memory,IM3Module io_module)445 M3Result  InitDataSegments  (M3Memory * io_memory, IM3Module io_module)
446 {
447     M3Result result = m3Err_none;
448 
449     _throwif ("unallocated linear memory", !(io_memory->mallocated));
450 
451     for (u32 i = 0; i < io_module->numDataSegments; ++i)
452     {
453         M3DataSegment * segment = & io_module->dataSegments [i];
454 
455         i32 segmentOffset;
456         bytes_t start = segment->initExpr;
457 _       (EvaluateExpression (io_module, & segmentOffset, c_m3Type_i32, & start, segment->initExpr + segment->initExprSize));
458 
459         m3log (runtime, "loading data segment: %d; size: %d; offset: %d", i, segment->size, segmentOffset);
460 
461         if (segmentOffset >= 0 && (size_t)(segmentOffset) + segment->size <= io_memory->mallocated->length)
462         {
463             u8 * dest = m3MemData (io_memory->mallocated) + segmentOffset;
464             memcpy (dest, segment->data, segment->size);
465         } else {
466             _throw ("data segment out of bounds");
467         }
468     }
469 
470     _catch: return result;
471 }
472 
473 
InitElements(IM3Module io_module)474 M3Result  InitElements  (IM3Module io_module)
475 {
476     M3Result result = m3Err_none;
477 
478     bytes_t bytes = io_module->elementSection;
479     cbytes_t end = io_module->elementSectionEnd;
480 
481     for (u32 i = 0; i < io_module->numElementSegments; ++i)
482     {
483         u32 index;
484 _       (ReadLEB_u32 (& index, & bytes, end));
485 
486         if (index == 0)
487         {
488             i32 offset;
489 _           (EvaluateExpression (io_module, & offset, c_m3Type_i32, & bytes, end));
490             _throwif ("table underflow", offset < 0);
491 
492             u32 numElements;
493 _           (ReadLEB_u32 (& numElements, & bytes, end));
494 
495             size_t endElement = (size_t) numElements + offset;
496             _throwif ("table overflow", endElement > d_m3MaxSaneTableSize);
497 
498             // is there any requirement that elements must be in increasing sequence?
499             // make sure the table isn't shrunk.
500             if (endElement > io_module->table0Size)
501             {
502                 io_module->table0 = m3_ReallocArray (IM3Function, io_module->table0, endElement, io_module->table0Size);
503                 io_module->table0Size = (u32) endElement;
504             }
505             _throwifnull(io_module->table0);
506 
507             for (u32 e = 0; e < numElements; ++e)
508             {
509                 u32 functionIndex;
510 _               (ReadLEB_u32 (& functionIndex, & bytes, end));
511                 _throwif ("function index out of range", functionIndex >= io_module->numFunctions);
512                 IM3Function function = & io_module->functions [functionIndex];      d_m3Assert (function); //printf ("table: %s\n", m3_GetFunctionName(function));
513                 io_module->table0 [e + offset] = function;
514             }
515         }
516         else _throw ("element table index must be zero for MVP");
517     }
518 
519     _catch: return result;
520 }
521 
m3_CompileModule(IM3Module io_module)522 M3Result  m3_CompileModule  (IM3Module io_module)
523 {
524     M3Result result = m3Err_none;
525 
526     for (u32 i = 0; i < io_module->numFunctions; ++i)
527     {
528         IM3Function f = & io_module->functions [i];
529         if (f->wasm and not f->compiled)
530         {
531 _           (CompileFunction (f));
532         }
533     }
534 
535     _catch: return result;
536 }
537 
m3_RunStart(IM3Module io_module)538 M3Result  m3_RunStart  (IM3Module io_module)
539 {
540 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
541     // Execution disabled for fuzzing builds
542     return m3Err_none;
543 #endif
544 
545     M3Result result = m3Err_none;
546 
547     if (io_module and io_module->startFunction >= 0)
548     {
549         IM3Function function = & io_module->functions [io_module->startFunction];
550 
551         if (not function->compiled)
552         {
553 _           (CompileFunction (function));
554         }
555 
556         IM3FuncType ftype = function->funcType;
557         if (ftype->numArgs != 0 || ftype->numRets != 0)
558             _throw (m3Err_argumentCountMismatch);
559 
560         IM3Module module = function->module;
561         IM3Runtime runtime = module->runtime;
562 
563 _       ((M3Result) Call (function->compiled, (m3stack_t) runtime->stack, runtime->memory.mallocated, d_m3OpDefaultArgs));
564 
565         io_module->startFunction = -1;
566     }
567 
568     _catch: return result;
569 }
570 
571 // TODO: deal with main + side-modules loading efforcement
m3_LoadModule(IM3Runtime io_runtime,IM3Module io_module)572 M3Result  m3_LoadModule  (IM3Runtime io_runtime, IM3Module io_module)
573 {
574     M3Result result = m3Err_none;
575 
576     if (UNLIKELY(io_module->runtime)) {
577         return m3Err_moduleAlreadyLinked;
578     }
579 
580     io_module->runtime = io_runtime;
581     M3Memory * memory = & io_runtime->memory;
582 
583 _   (InitMemory (io_runtime, io_module));
584 _   (InitGlobals (io_module));
585 _   (InitDataSegments (memory, io_module));
586 _   (InitElements (io_module));
587 
588     // Start func might use imported functions, which are not liked here yet,
589     // so it will be called before a function call is attempted (in m3_FindFunction)
590 
591 #ifdef DEBUG
592     Module_GenerateNames(io_module);
593 #endif
594 
595     io_module->next = io_runtime->modules;
596     io_runtime->modules = io_module;
597     return result; // ok
598 
599 _catch:
600     io_module->runtime = NULL;
601     return result;
602 }
603 
m3_FindGlobal(IM3Module io_module,const char * const i_globalName)604 IM3Global  m3_FindGlobal  (IM3Module               io_module,
605                            const char * const      i_globalName)
606 {
607     // Search exports
608     for (u32 i = 0; i < io_module->numGlobals; ++i)
609     {
610         IM3Global g = & io_module->globals [i];
611         if (g->name and strcmp (g->name, i_globalName) == 0)
612         {
613             return g;
614         }
615     }
616 
617     // Search imports
618     for (u32 i = 0; i < io_module->numGlobals; ++i)
619     {
620         IM3Global g = & io_module->globals [i];
621 
622         if (g->import.moduleUtf8 and g->import.fieldUtf8)
623         {
624             if (strcmp (g->import.fieldUtf8, i_globalName) == 0)
625             {
626                 return g;
627             }
628         }
629     }
630     return NULL;
631 }
632 
m3_GetGlobal(IM3Global i_global,IM3TaggedValue o_value)633 M3Result  m3_GetGlobal  (IM3Global                 i_global,
634                          IM3TaggedValue            o_value)
635 {
636     if (not i_global) return m3Err_globalLookupFailed;
637 
638     switch (i_global->type) {
639     case c_m3Type_i32: o_value->value.i32 = i_global->intValue; break;
640     case c_m3Type_i64: o_value->value.i64 = i_global->intValue; break;
641 # if d_m3HasFloat
642     case c_m3Type_f32: o_value->value.f32 = i_global->f32Value; break;
643     case c_m3Type_f64: o_value->value.f64 = i_global->f64Value; break;
644 # endif
645     default: return m3Err_invalidTypeId;
646     }
647 
648     o_value->type = (M3ValueType)(i_global->type);
649     return m3Err_none;
650 }
651 
m3_SetGlobal(IM3Global i_global,const IM3TaggedValue i_value)652 M3Result  m3_SetGlobal  (IM3Global                 i_global,
653                          const IM3TaggedValue      i_value)
654 {
655     if (not i_global) return m3Err_globalLookupFailed;
656     // TODO: if (not g->isMutable) return m3Err_globalNotMutable;
657 
658     if (i_global->type != i_value->type) return m3Err_globalTypeMismatch;
659 
660     switch (i_value->type) {
661     case c_m3Type_i32: i_global->intValue = i_value->value.i32; break;
662     case c_m3Type_i64: i_global->intValue = i_value->value.i64; break;
663 # if d_m3HasFloat
664     case c_m3Type_f32: i_global->f32Value = i_value->value.f32; break;
665     case c_m3Type_f64: i_global->f64Value = i_value->value.f64; break;
666 # endif
667     default: return m3Err_invalidTypeId;
668     }
669 
670     return m3Err_none;
671 }
672 
m3_GetGlobalType(IM3Global i_global)673 M3ValueType  m3_GetGlobalType  (IM3Global          i_global)
674 {
675     return (i_global) ? (M3ValueType)(i_global->type) : c_m3Type_none;
676 }
677 
678 
v_FindFunction(IM3Module i_module,const char * const i_name)679 void *  v_FindFunction  (IM3Module i_module, const char * const i_name)
680 {
681     for (u32 i = 0; i < i_module->numFunctions; ++i)
682     {
683         IM3Function f = & i_module->functions [i];
684 
685         bool isImported = f->import.moduleUtf8 or f->import.fieldUtf8;
686 
687         if (isImported)
688             continue;
689 
690         for (int j = 0; j < f->numNames; j++)
691         {
692             if (f->names [j] and strcmp (f->names [j], i_name) == 0)
693                 return f;
694         }
695     }
696 
697     return NULL;
698 }
699 
700 
m3_FindFunction(IM3Function * o_function,IM3Runtime i_runtime,const char * const i_functionName)701 M3Result  m3_FindFunction  (IM3Function * o_function, IM3Runtime i_runtime, const char * const i_functionName)
702 {
703     M3Result result = m3Err_none;                               d_m3Assert (o_function and i_runtime and i_functionName);
704 
705     IM3Function function = NULL;
706 
707     if (not i_runtime->modules) {
708         _throw ("no modules loaded");
709     }
710 
711     function = (IM3Function) ForEachModule (i_runtime, (ModuleVisitor) v_FindFunction, (void *) i_functionName);
712 
713     if (function)
714     {
715         if (not function->compiled)
716         {
717 _           (CompileFunction (function))
718         }
719 
720         // Check if start function needs to be called
721         if (function->module->startFunction)
722         {
723 _           (m3_RunStart (function->module))
724         }
725     }
726     else _throw (ErrorModule (m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName));
727 
728     _catch:
729     if (result)
730         function = NULL;
731 
732     * o_function = function;
733 
734     return result;
735 }
736 
737 
m3_GetArgCount(IM3Function i_function)738 uint32_t  m3_GetArgCount  (IM3Function i_function)
739 {
740     if (i_function) {
741         IM3FuncType ft = i_function->funcType;
742         if (ft) {
743             return ft->numArgs;
744         }
745     }
746     return 0;
747 }
748 
m3_GetRetCount(IM3Function i_function)749 uint32_t  m3_GetRetCount  (IM3Function i_function)
750 {
751     if (i_function) {
752         IM3FuncType ft = i_function->funcType;
753         if (ft) {
754             return ft->numRets;
755         }
756     }
757     return 0;
758 }
759 
760 
m3_GetArgType(IM3Function i_function,uint32_t index)761 M3ValueType  m3_GetArgType  (IM3Function i_function, uint32_t index)
762 {
763     if (i_function) {
764         IM3FuncType ft = i_function->funcType;
765         if (ft and index < ft->numArgs) {
766             return (M3ValueType)d_FuncArgType(ft, index);
767         }
768     }
769     return c_m3Type_none;
770 }
771 
m3_GetRetType(IM3Function i_function,uint32_t index)772 M3ValueType  m3_GetRetType  (IM3Function i_function, uint32_t index)
773 {
774     if (i_function) {
775         IM3FuncType ft = i_function->funcType;
776         if (ft and index < ft->numRets) {
777             return (M3ValueType) d_FuncRetType (ft, index);
778         }
779     }
780     return c_m3Type_none;
781 }
782 
783 
GetStackPointerForArgs(IM3Function i_function)784 u8 *  GetStackPointerForArgs  (IM3Function i_function)
785 {
786     u64 * stack = (u64 *) i_function->module->runtime->stack;
787     IM3FuncType ftype = i_function->funcType;
788 
789     stack += ftype->numRets;
790 
791     return (u8 *) stack;
792 }
793 
794 
m3_CallV(IM3Function i_function,...)795 M3Result  m3_CallV  (IM3Function i_function, ...)
796 {
797     va_list ap;
798     va_start(ap, i_function);
799     M3Result r = m3_CallVL(i_function, ap);
800     va_end(ap);
801     return r;
802 }
803 
804 static
ReportNativeStackUsage()805 void  ReportNativeStackUsage  ()
806 {
807 #   if d_m3LogNativeStack
808         int stackUsed =  m3StackGetMax();
809         fprintf (stderr, "Native stack used: %d\n", stackUsed);
810 #   endif
811 }
812 
813 
m3_CallVL(IM3Function i_function,va_list i_args)814 M3Result  m3_CallVL  (IM3Function i_function, va_list i_args)
815 {
816     IM3Runtime runtime = i_function->module->runtime;
817     IM3FuncType ftype = i_function->funcType;
818 
819     if (!i_function->compiled) {
820         return m3Err_missingCompiledCode;
821     }
822 
823 # if d_m3RecordBacktraces
824     ClearBacktrace (runtime);
825 # endif
826 
827     u8* s = GetStackPointerForArgs (i_function);
828 
829     for (u32 i = 0; i < ftype->numArgs; ++i)
830     {
831         switch (d_FuncArgType(ftype, i)) {
832         case c_m3Type_i32:  *(i32*)(s) = va_arg(i_args, i32);  s += 8; break;
833         case c_m3Type_i64:  *(i64*)(s) = va_arg(i_args, i64);  s += 8; break;
834 # if d_m3HasFloat
835         case c_m3Type_f32:  *(f32*)(s) = va_arg(i_args, f64);  s += 8; break; // f32 is passed as f64
836         case c_m3Type_f64:  *(f64*)(s) = va_arg(i_args, f64);  s += 8; break;
837 # endif
838         default: return "unknown argument type";
839         }
840     }
841     m3StackCheckInit();
842     M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs);
843     ReportNativeStackUsage ();
844 
845     runtime->lastCalled = r ? NULL : i_function;
846 
847     return r;
848 }
849 
m3_Call(IM3Function i_function,uint32_t i_argc,const void * i_argptrs[])850 M3Result  m3_Call  (IM3Function i_function, uint32_t i_argc, const void * i_argptrs[])
851 {
852     IM3Runtime runtime = i_function->module->runtime;
853     IM3FuncType ftype = i_function->funcType;
854 
855     if (i_argc != ftype->numArgs) {
856         return m3Err_argumentCountMismatch;
857     }
858     if (!i_function->compiled) {
859         return m3Err_missingCompiledCode;
860     }
861 
862 # if d_m3RecordBacktraces
863     ClearBacktrace (runtime);
864 # endif
865 
866     u8* s = GetStackPointerForArgs (i_function);
867 
868     for (u32 i = 0; i < ftype->numArgs; ++i)
869     {
870         switch (d_FuncArgType(ftype, i)) {
871         case c_m3Type_i32:  *(i32*)(s) = *(i32*)i_argptrs[i];  s += 8; break;
872         case c_m3Type_i64:  *(i64*)(s) = *(i64*)i_argptrs[i];  s += 8; break;
873 # if d_m3HasFloat
874         case c_m3Type_f32:  *(f32*)(s) = *(f32*)i_argptrs[i];  s += 8; break;
875         case c_m3Type_f64:  *(f64*)(s) = *(f64*)i_argptrs[i];  s += 8; break;
876 # endif
877         default: return "unknown argument type";
878         }
879     }
880 
881     m3StackCheckInit();
882     M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs);
883     ReportNativeStackUsage ();
884 
885     runtime->lastCalled = r ? NULL : i_function;
886 
887 
888     return r;
889 }
890 
m3_CallArgv(IM3Function i_function,uint32_t i_argc,const char * i_argv[])891 M3Result  m3_CallArgv  (IM3Function i_function, uint32_t i_argc, const char * i_argv[])
892 {
893     IM3FuncType ftype = i_function->funcType;
894     IM3Runtime runtime = i_function->module->runtime;
895 
896     if (i_argc != ftype->numArgs) {
897         return m3Err_argumentCountMismatch;
898     }
899     if (!i_function->compiled) {
900         return m3Err_missingCompiledCode;
901     }
902 
903 # if d_m3RecordBacktraces
904     ClearBacktrace (runtime);
905 # endif
906 
907     u8* s = GetStackPointerForArgs (i_function);
908 
909     for (u32 i = 0; i < ftype->numArgs; ++i)
910     {
911         switch (d_FuncArgType(ftype, i)) {
912         case c_m3Type_i32:  *(i32*)(s) = strtoul(i_argv[i], NULL, 10);  s += 8; break;
913         case c_m3Type_i64:  *(i64*)(s) = strtoull(i_argv[i], NULL, 10); s += 8; break;
914 # if d_m3HasFloat
915         case c_m3Type_f32:  *(f32*)(s) = strtod(i_argv[i], NULL);       s += 8; break;  // strtof would be less portable
916         case c_m3Type_f64:  *(f64*)(s) = strtod(i_argv[i], NULL);       s += 8; break;
917 # endif
918         default: return "unknown argument type";
919         }
920     }
921 
922     m3StackCheckInit();
923     M3Result r = (M3Result) Call (i_function->compiled, (m3stack_t)(runtime->stack), runtime->memory.mallocated, d_m3OpDefaultArgs);
924     ReportNativeStackUsage ();
925 
926     runtime->lastCalled = r ? NULL : i_function;
927 
928     return r;
929 }
930 
931 
932 //u8 * AlignStackPointerTo64Bits (const u8 * i_stack)
933 //{
934 //    uintptr_t ptr = (uintptr_t) i_stack;
935 //    return (u8 *) ((ptr + 7) & ~7);
936 //}
937 
938 
m3_GetResults(IM3Function i_function,uint32_t i_retc,const void * o_retptrs[])939 M3Result  m3_GetResults  (IM3Function i_function, uint32_t i_retc, const void * o_retptrs[])
940 {
941     IM3FuncType ftype = i_function->funcType;
942     IM3Runtime runtime = i_function->module->runtime;
943 
944     if (i_retc != ftype->numRets) {
945         return m3Err_argumentCountMismatch;
946     }
947     if (i_function != runtime->lastCalled) {
948         return "function not called";
949     }
950 
951     u8* s = (u8*) runtime->stack;
952 
953     for (u32 i = 0; i < ftype->numRets; ++i)
954     {
955         switch (d_FuncRetType(ftype, i)) {
956         case c_m3Type_i32:  *(i32*)o_retptrs[i] = *(i32*)(s); s += 8; break;
957         case c_m3Type_i64:  *(i64*)o_retptrs[i] = *(i64*)(s); s += 8; break;
958 # if d_m3HasFloat
959         case c_m3Type_f32:  *(f32*)o_retptrs[i] = *(f32*)(s); s += 8; break;
960         case c_m3Type_f64:  *(f64*)o_retptrs[i] = *(f64*)(s); s += 8; break;
961 # endif
962         default: return "unknown return type";
963         }
964     }
965     return m3Err_none;
966 }
967 
m3_GetResultsV(IM3Function i_function,...)968 M3Result  m3_GetResultsV  (IM3Function i_function, ...)
969 {
970     va_list ap;
971     va_start(ap, i_function);
972     M3Result r = m3_GetResultsVL(i_function, ap);
973     va_end(ap);
974     return r;
975 }
976 
m3_GetResultsVL(IM3Function i_function,va_list o_rets)977 M3Result  m3_GetResultsVL  (IM3Function i_function, va_list o_rets)
978 {
979     IM3Runtime runtime = i_function->module->runtime;
980     IM3FuncType ftype = i_function->funcType;
981 
982     if (i_function != runtime->lastCalled) {
983         return "function not called";
984     }
985 
986     u8* s = (u8*) runtime->stack;
987     for (u32 i = 0; i < ftype->numRets; ++i)
988     {
989         switch (d_FuncRetType(ftype, i)) {
990         case c_m3Type_i32:  *va_arg(o_rets, i32*) = *(i32*)(s);  s += 8; break;
991         case c_m3Type_i64:  *va_arg(o_rets, i64*) = *(i64*)(s);  s += 8; break;
992 # if d_m3HasFloat
993         case c_m3Type_f32:  *va_arg(o_rets, f32*) = *(f32*)(s);  s += 8; break;
994         case c_m3Type_f64:  *va_arg(o_rets, f64*) = *(f64*)(s);  s += 8; break;
995 # endif
996         default: return "unknown argument type";
997         }
998     }
999     return m3Err_none;
1000 }
1001 
ReleaseCodePageNoTrack(IM3Runtime i_runtime,IM3CodePage i_codePage)1002 void  ReleaseCodePageNoTrack (IM3Runtime i_runtime, IM3CodePage i_codePage)
1003 {
1004     if (i_codePage)
1005     {
1006         IM3CodePage * list;
1007 
1008         bool pageFull = (NumFreeLines (i_codePage) < d_m3CodePageFreeLinesThreshold);
1009         if (pageFull)
1010             list = & i_runtime->pagesFull;
1011         else
1012             list = & i_runtime->pagesOpen;
1013 
1014         PushCodePage (list, i_codePage);                        m3log (emit, "release page: %d to queue: '%s'", i_codePage->info.sequence, pageFull ? "full" : "open")
1015     }
1016 }
1017 
1018 
AcquireCodePageWithCapacity(IM3Runtime i_runtime,u32 i_minLineCount)1019 IM3CodePage  AcquireCodePageWithCapacity  (IM3Runtime i_runtime, u32 i_minLineCount)
1020 {
1021     IM3CodePage page = RemoveCodePageOfCapacity (& i_runtime->pagesOpen, i_minLineCount);
1022 
1023     if (not page)
1024     {
1025         page = Environment_AcquireCodePage (i_runtime->environment, i_minLineCount);
1026 
1027         if (not page)
1028             page = NewCodePage (i_minLineCount);
1029 
1030         if (page)
1031             i_runtime->numCodePages++;
1032     }
1033 
1034     if (page)
1035     {                                                            m3log (emit, "acquire page: %d", page->info.sequence);
1036         i_runtime->numActiveCodePages++;
1037     }
1038 
1039     return page;
1040 }
1041 
1042 
AcquireCodePage(IM3Runtime i_runtime)1043 IM3CodePage  AcquireCodePage  (IM3Runtime i_runtime)
1044 {
1045     return AcquireCodePageWithCapacity (i_runtime, d_m3CodePageFreeLinesThreshold);
1046 }
1047 
1048 
ReleaseCodePage(IM3Runtime i_runtime,IM3CodePage i_codePage)1049 void  ReleaseCodePage  (IM3Runtime i_runtime, IM3CodePage i_codePage)
1050 {
1051     if (i_codePage)
1052     {
1053         ReleaseCodePageNoTrack (i_runtime, i_codePage);
1054         i_runtime->numActiveCodePages--;
1055 
1056 #       if defined (DEBUG)
1057             u32 numOpen = CountCodePages (i_runtime->pagesOpen);
1058             u32 numFull = CountCodePages (i_runtime->pagesFull);
1059 
1060             m3log (runtime, "runtime: %p; open-pages: %d; full-pages: %d; active: %d; total: %d", i_runtime, numOpen, numFull, i_runtime->numActiveCodePages, i_runtime->numCodePages);
1061 
1062             d_m3Assert (numOpen + numFull + i_runtime->numActiveCodePages == i_runtime->numCodePages);
1063 
1064 #           if d_m3LogCodePages
1065                 dump_code_page (i_codePage, /* startPC: */ NULL);
1066 #           endif
1067 #       endif
1068     }
1069 }
1070 
1071 
1072 #if d_m3VerboseErrorMessages
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,...)1073 M3Result  m3Error  (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function,
1074                     const char * const i_file, u32 i_lineNum, const char * const i_errorMessage, ...)
1075 {
1076     if (i_runtime)
1077     {
1078         i_runtime->error = (M3ErrorInfo){ .result = i_result, .runtime = i_runtime, .module = i_module,
1079                                           .function = i_function, .file = i_file, .line = i_lineNum };
1080         i_runtime->error.message = i_runtime->error_message;
1081 
1082         va_list args;
1083         va_start (args, i_errorMessage);
1084         vsnprintf (i_runtime->error_message, sizeof(i_runtime->error_message), i_errorMessage, args);
1085         va_end (args);
1086     }
1087 
1088     return i_result;
1089 }
1090 #endif
1091 
1092 
m3_GetErrorInfo(IM3Runtime i_runtime,M3ErrorInfo * o_info)1093 void  m3_GetErrorInfo  (IM3Runtime i_runtime, M3ErrorInfo* o_info)
1094 {
1095     if (i_runtime)
1096     {
1097         *o_info = i_runtime->error;
1098         m3_ResetErrorInfo (i_runtime);
1099     }
1100 }
1101 
1102 
m3_ResetErrorInfo(IM3Runtime i_runtime)1103 void m3_ResetErrorInfo (IM3Runtime i_runtime)
1104 {
1105     if (i_runtime)
1106     {
1107         M3_INIT(i_runtime->error);
1108         i_runtime->error.message = "";
1109     }
1110 }
1111 
m3_GetMemory(IM3Runtime i_runtime,uint32_t * o_memorySizeInBytes,uint32_t i_memoryIndex)1112 uint8_t *  m3_GetMemory  (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, uint32_t i_memoryIndex)
1113 {
1114     uint8_t * memory = NULL;                                                    d_m3Assert (i_memoryIndex == 0);
1115 
1116     if (i_runtime)
1117     {
1118         u32 size = (u32) i_runtime->memory.mallocated->length;
1119 
1120         if (o_memorySizeInBytes)
1121             * o_memorySizeInBytes = size;
1122 
1123         if (size)
1124             memory = m3MemData (i_runtime->memory.mallocated);
1125     }
1126 
1127     return memory;
1128 }
1129 
1130 
m3_GetMemorySize(IM3Runtime i_runtime)1131 uint32_t  m3_GetMemorySize  (IM3Runtime i_runtime)
1132 {
1133     return i_runtime->memory.mallocated->length;
1134 }
1135 
1136 
m3_GetBacktrace(IM3Runtime i_runtime)1137 M3BacktraceInfo *  m3_GetBacktrace  (IM3Runtime i_runtime)
1138 {
1139 # if d_m3RecordBacktraces
1140     return & i_runtime->backtrace;
1141 # else
1142     return NULL;
1143 # endif
1144 }
1145 
1146