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