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