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 | |
17 | IM3Environment 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 | |
54 | void 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 | |
70 | void 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 | |
80 | void 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 |
87 | void 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 | |
114 | IM3CodePage 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 | |
140 | IM3CodePage Environment_AcquireCodePage (IM3Environment i_environment, u32 i_minimumLineCount) |
141 | { |
142 | return RemoveCodePageOfCapacity (& i_environment->pagesReleased, i_minimumLineCount); |
143 | } |
144 | |
145 | |
146 | void 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 | |
173 | IM3Runtime 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 | |
196 | void * m3_GetUserData (IM3Runtime i_runtime) |
197 | { |
198 | return i_runtime ? i_runtime->userdata : NULL; |
199 | } |
200 | |
201 | |
202 | void * 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 | |
222 | void * _FreeModule (IM3Module i_module, void * i_info) |
223 | { |
224 | m3_FreeModule (i_module); |
225 | return NULL; |
226 | } |
227 | |
228 | |
229 | void 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 | |
241 | void 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 | |
252 | M3Result 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 | |
330 | M3Result 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 | |
346 | M3Result 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 | |
407 | M3Result 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 | |
448 | M3Result 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 | |
477 | M3Result 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 | |
525 | M3Result 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 | |
541 | M3Result 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 |
584 | M3Result 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 | |
616 | IM3Global 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 | |
645 | M3Result 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 | |
664 | M3Result 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 | |
685 | M3ValueType m3_GetGlobalType (IM3Global i_global) |
686 | { |
687 | return (i_global) ? (M3ValueType)(i_global->type) : c_m3Type_none; |
688 | } |
689 | |
690 | |
691 | void * 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 | |
713 | M3Result 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 | |
743 | static |
744 | M3Result 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 | |
757 | uint32_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 | |
768 | uint32_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 | |
780 | M3ValueType 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 | |
791 | M3ValueType 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 | |
803 | u8 * 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 | |
814 | M3Result 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 | |
823 | static |
824 | void ReportNativeStackUsage () |
825 | { |
826 | # if d_m3LogNativeStack |
827 | int stackUsed = m3StackGetMax(); |
828 | fprintf (stderr, "Native stack used: %d\n" , stackUsed); |
829 | # endif |
830 | } |
831 | |
832 | |
833 | M3Result 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 | |
873 | M3Result 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 | |
917 | M3Result 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 | |
969 | M3Result 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 | |
998 | M3Result 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 | |
1007 | M3Result 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 | |
1032 | void 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 | |
1049 | IM3CodePage 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 | |
1073 | IM3CodePage AcquireCodePage (IM3Runtime i_runtime) |
1074 | { |
1075 | return AcquireCodePageWithCapacity (i_runtime, d_m3CodePageFreeLinesThreshold); |
1076 | } |
1077 | |
1078 | |
1079 | void 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 |
1103 | M3Result 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 | |
1123 | void 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 | |
1133 | void 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 | |
1142 | uint8_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 | |
1161 | uint32_t m3_GetMemorySize (IM3Runtime i_runtime) |
1162 | { |
1163 | return i_runtime->memory.mallocated->length; |
1164 | } |
1165 | |
1166 | |
1167 | M3BacktraceInfo * 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 | |