Coverage Report

Created: 2024-10-29 06:06

/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