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