Coverage Report

Created: 2026-08-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_info.c
Line
Count
Source
1
//
2
//  m3_info.c
3
//
4
//  Created by Steven Massey on 4/27/19.
5
//  Copyright © 2019 Steven Massey. All rights reserved.
6
//
7
8
#include "m3_env.h"
9
#include "m3_info.h"
10
#include "m3_compile.h"
11
12
#if defined(DEBUG) || (d_m3EnableStrace >= 2)
13
14
size_t  SPrintArg  (char * o_string, size_t i_stringBufferSize, voidptr_t i_sp, u8 i_type)
15
{
16
    int len = 0;
17
18
    * o_string = 0;
19
20
    if      (i_type == c_m3Type_i32)
21
        len = snprintf (o_string, i_stringBufferSize, "%" PRIi32, * (i32 *) i_sp);
22
    else if (i_type == c_m3Type_i64)
23
        len = snprintf (o_string, i_stringBufferSize, "%" PRIi64, * (i64 *) i_sp);
24
#if d_m3HasFloat
25
    else if (i_type == c_m3Type_f32)
26
        len = snprintf (o_string, i_stringBufferSize, "%" PRIf32, * (f32 *) i_sp);
27
    else if (i_type == c_m3Type_f64)
28
        len = snprintf (o_string, i_stringBufferSize, "%" PRIf64, * (f64 *) i_sp);
29
#endif
30
31
    len = M3_MAX (0, len);
32
33
    return len;
34
}
35
36
37
cstr_t  SPrintFunctionArgList  (IM3Function i_function, m3stack_t i_sp)
38
{
39
    int ret;
40
    static char string [256];
41
42
    char * s = string;
43
    ccstr_t e = string + sizeof(string) - 1;
44
45
    ret = snprintf (s, e-s, "(");
46
    s += M3_MAX (0, ret);
47
48
    u64 * argSp = (u64 *) i_sp;
49
50
    IM3FuncType funcType = i_function->funcType;
51
    if (funcType)
52
    {
53
        u32 numArgs = funcType->numArgs;
54
55
        for (u32 i = 0; i < numArgs; ++i)
56
        {
57
            u8 type = d_FuncArgType(funcType, i);
58
59
            ret = snprintf (s, e-s, "%s: ", c_waTypes [BaseTypeOf(type)]);
60
            s += M3_MAX (0, ret);
61
62
            s += SPrintArg (s, e-s, argSp + i, type);
63
64
            if (i != numArgs - 1) {
65
                ret = snprintf (s, e-s, ", ");
66
                s += M3_MAX (0, ret);
67
            }
68
        }
69
    }
70
    else printf ("null signature");
71
72
    ret = snprintf (s, e-s, ")");
73
    s += M3_MAX (0, ret);
74
75
    return string;
76
}
77
78
#endif
79
80
#ifdef DEBUG
81
82
// a central function you can be breakpoint:
83
void ExceptionBreakpoint (cstr_t i_exception, cstr_t i_message)
84
{
85
    printf ("\nexception: '%s' @ %s\n", i_exception, i_message);
86
    return;
87
}
88
89
90
typedef struct OpInfo
91
{
92
    IM3OpInfo   info;
93
    m3opcode_t  opcode;
94
}
95
OpInfo;
96
97
void  m3_PrintM3Info  ()
98
{
99
    printf ("\n-- m3 configuration --------------------------------------------\n");
100
//  printf (" sizeof M3CodePage    : %zu bytes  (%d slots) \n", sizeof (M3CodePage), c_m3CodePageNumSlots);
101
    printf (" sizeof M3MemPage     : %u bytes              \n", d_m3DefaultMemPageSize);
102
    printf (" sizeof M3Compilation : %zu bytes             \n", sizeof (M3Compilation));
103
    printf (" sizeof M3Function    : %zu bytes             \n", sizeof (M3Function));
104
    printf ("----------------------------------------------------------------\n\n");
105
}
106
107
108
void *  v_PrintEnvModuleInfo  (IM3Module i_module, void * i_info)
109
{
110
    u32 * io_index = (u32 *) i_info;
111
    printf (" module [%u]  name: '%s'; funcs: %d  \n", (* io_index)++, i_module->name, i_module->numFunctions);
112
113
    return NULL;
114
}
115
116
117
void  m3_PrintRuntimeInfo  (IM3Runtime i_runtime)
118
{
119
    printf ("\n-- m3 runtime -------------------------------------------------\n");
120
121
    printf (" stack-size: %zu   \n\n", i_runtime->numStackSlots * sizeof (m3slot_t));
122
123
    u32 moduleIndex = 0;
124
    ForEachModule (i_runtime, v_PrintEnvModuleInfo, & moduleIndex);
125
126
    printf ("----------------------------------------------------------------\n\n");
127
}
128
129
130
cstr_t  GetTypeName  (m3type_t i_m3Type)
131
{
132
    u8 base = BaseTypeOf (i_m3Type);
133
134
    if (base <= c_m3Type_unknown)
135
        return c_waTypes [base];
136
    else
137
        return "?";
138
}
139
140
141
// TODO: these 'static char string []' aren't thread-friendly.  though these functions are
142
// mainly for simple diagnostics during development, it'd be nice if they were fully reliable.
143
144
cstr_t  SPrintFuncTypeSignature  (IM3FuncType i_funcType)
145
{
146
    static char string [256];
147
148
    sprintf (string, "(");
149
150
    for (u32 i = 0; i < i_funcType->numArgs; ++i)
151
    {
152
        if (i != 0)
153
            strcat (string, ", ");
154
155
        strcat (string, GetTypeName (d_FuncArgType(i_funcType, i)));
156
    }
157
158
    strcat (string, ") -> ");
159
160
    for (u32 i = 0; i < i_funcType->numRets; ++i)
161
    {
162
        if (i != 0)
163
            strcat (string, ", ");
164
165
        strcat (string, GetTypeName (d_FuncRetType(i_funcType, i)));
166
    }
167
168
    return string;
169
}
170
171
172
cstr_t  SPrintValue  (void * i_value, u8 i_type)
173
{
174
    static char string [100];
175
    SPrintArg (string, 100, (m3stack_t) i_value, i_type);
176
    return string;
177
}
178
179
static
180
OpInfo find_operation_info  (IM3Operation i_operation)
181
{
182
    OpInfo opInfo = { NULL, 0 };
183
184
    if (!i_operation) return opInfo;
185
186
    // Scans the table directly rather than through GetOpInfo (), which rejects
187
    // the reserved slots and the internal operations kept past the last opcode.
188
    // TODO: find also extended opcodes
189
    for (u32 i = 0; i < c_numOperations; ++i)
190
    {
191
        IM3OpInfo oi = & c_operations [i];
192
193
        for (u32 o = 0; o < 4; ++o)
194
        {
195
            if (oi->operations [o] == i_operation)
196
            {
197
                opInfo.info = oi;
198
                opInfo.opcode = i;
199
                return opInfo;
200
            }
201
        }
202
    }
203
204
    return opInfo;
205
}
206
207
208
#undef fetch
209
#define fetch(TYPE) (* (TYPE *) ((*o_pc)++))
210
211
#define d_m3Decoder(FUNC) void Decode_##FUNC (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc)
212
213
d_m3Decoder  (Call)
214
{
215
    void * function = fetch (void *);
216
    i32 stackOffset = fetch (i32);
217
218
    sprintf (o_string, "%p; stack-offset: %d", function, stackOffset);
219
}
220
221
222
d_m3Decoder (Entry)
223
{
224
    IM3Function function = fetch (IM3Function);
225
226
    // only prints out the first registered name for the function
227
    sprintf (o_string, "%s", m3_GetFunctionName(function));
228
}
229
230
231
d_m3Decoder (f64_Store)
232
{
233
    if (i_operation == i_opInfo->operations [0])
234
    {
235
        u32 operand = fetch (u32);
236
        u32 offset = fetch (u32);
237
238
        sprintf (o_string, "offset= slot:%d + immediate:%d", operand, offset);
239
    }
240
241
//    sprintf (o_string, "%s", function->name);
242
}
243
244
245
d_m3Decoder  (Branch)
246
{
247
    void * target = fetch (void *);
248
    sprintf (o_string, "%p", target);
249
}
250
251
d_m3Decoder  (BranchTable)
252
{
253
    u32 slot = fetch (u32);
254
255
    o_string += sprintf (o_string, "slot: %" PRIu32 "; targets: ", slot);
256
257
//    IM3Function function = fetch2 (IM3Function);
258
259
    i32 targets = fetch (i32);
260
261
    for (i32 i = 0; i < targets; ++i)
262
    {
263
        pc_t addr = fetch (pc_t);
264
        o_string += sprintf (o_string, "%" PRIi32 "=%p, ", i, addr);
265
    }
266
267
    pc_t addr = fetch (pc_t);
268
    sprintf (o_string, "def=%p ", addr);
269
}
270
271
272
d_m3Decoder  (Const)
273
{
274
    u64 value = fetch (u64); i32 offset = fetch (i32);
275
    sprintf (o_string, " slot [%d] = %" PRIu64, offset, value);
276
}
277
278
279
#undef fetch
280
281
void  DecodeOperation  (char * o_string, u8 i_opcode, IM3Operation i_operation, IM3OpInfo i_opInfo, pc_t * o_pc)
282
{
283
    #define d_m3Decode(OPCODE, FUNC) case OPCODE: Decode_##FUNC (o_string, i_opcode, i_operation, i_opInfo, o_pc); break;
284
285
    switch (i_opcode)
286
    {
287
//        d_m3Decode (0xc0,                  Const)
288
        d_m3Decode (0xc5,                  Entry)
289
        d_m3Decode (c_waOp_call,           Call)
290
        d_m3Decode (c_waOp_branch,         Branch)
291
        d_m3Decode (c_waOp_branchTable,    BranchTable)
292
        d_m3Decode (0x39,                  f64_Store)
293
    }
294
}
295
296
// WARNING/TODO: this isn't fully implemented. it blindly assumes each word is a Operation pointer
297
// and, if an operation happens to missing from the c_operations table it won't be recognized here
298
void  dump_code_page  (IM3CodePage i_codePage, pc_t i_startPC)
299
{
300
        m3log (code, "code page seq: %d", i_codePage->info.sequence);
301
302
        pc_t pc = i_startPC ? i_startPC : GetPageStartPC (i_codePage);
303
        pc_t end = GetPagePC (i_codePage);
304
305
        m3log (code, "---------------------------------------------------------------------------------------");
306
307
        while (pc < end)
308
        {
309
            const pc_t operationPC = pc;
310
            (void) operationPC;  // avoid unused variable warning if DEBUG is not defined
311
            IM3Operation op = (IM3Operation) (* pc++);
312
313
                OpInfo i = find_operation_info (op);
314
315
                if (i.info)
316
                {
317
                    char infoString [8*1024] = { 0 };
318
319
                    DecodeOperation (infoString, i.opcode, op, i.info, & pc);
320
321
                    m3log (code, "%p | %20s  %s", operationPC, i.info->name, infoString);
322
                }
323
                else
324
                    m3log (code, "%p | %p", operationPC, op);
325
326
        }
327
328
        m3log (code, "---------------------------------------------------------------------------------------");
329
330
        m3log (code, "free-lines: %d", i_codePage->info.numLines - i_codePage->info.lineIndex);
331
}
332
333
334
void  dump_type_stack  (IM3Compilation o)
335
{
336
    /* Reminders about how the stack works! :)
337
     -- args & locals remain on the type stack for duration of the function. Denoted with a constant 'A' and 'L' in this dump.
338
     -- the initial stack dumps originate from the CompileLocals () function, so these identifiers won't/can't be
339
     applied until this compilation stage is finished
340
     -- constants are not statically represented in the type stack (like args & constants) since they don't have/need
341
     write counts
342
343
     -- the number shown for static args and locals (value in wasmStack [i]) represents the write count for the variable
344
345
     -- (does Wasm ever write to an arg? I dunno/don't remember.)
346
     -- the number for the dynamic stack values represents the slot number.
347
     -- if the slot index points to arg, local or constant it's denoted with a lowercase 'a', 'l' or 'c'
348
349
     */
350
351
    // for the assert at end of dump:
352
    i32 regAllocated [2] = { (i32) IsRegisterAllocated (o, 0), (i32) IsRegisterAllocated (o, 1) };
353
354
    // display whether r0 or fp0 is allocated. these should then also be reflected somewhere in the stack too.
355
    d_m3Log(stack, "\n");
356
    d_m3Log(stack, "        ");
357
    printf ("%s %s    ", regAllocated [0] ? "(r0)" : "    ", regAllocated [1] ? "(fp0)" : "     ");
358
    printf("\n");
359
360
    for (u32 p = 1; p <= 2; ++p)
361
    {
362
        d_m3Log(stack, "        ");
363
364
        for (u16 i = 0; i < o->stackIndex; ++i)
365
        {
366
            if (i > 0 and i == o->stackFirstDynamicIndex)
367
                printf ("#");
368
369
            if (i == o->block.blockStackIndex)
370
                printf (">");
371
372
            const char * type = c_waCompactTypes [BaseTypeOf(o->typeStack [i])];
373
374
            const char * location = "";
375
376
            i32 slot = o->wasmStack [i];
377
378
            if (IsRegisterSlotAlias (slot))
379
            {
380
                bool isFp = IsFpRegisterSlotAlias (slot);
381
                location = isFp ? "/f" : "/r";
382
383
                regAllocated [isFp]--;
384
                slot = -1;
385
            }
386
            else
387
            {
388
                if (slot < o->slotFirstDynamicIndex)
389
                {
390
                    if (slot >= o->slotFirstConstIndex)
391
                        location = "c";
392
                    else if (slot >= o->function->numRetAndArgSlots)
393
                        location = "L";
394
                    else
395
                        location = "a";
396
                }
397
            }
398
399
            char item [100];
400
401
            if (slot >= 0)
402
                sprintf (item, "%s%s%d", type, location, slot);
403
            else
404
                sprintf (item, "%s%s", type, location);
405
406
            if (p == 1)
407
            {
408
                size_t s = strlen (item);
409
410
                sprintf (item, "%d", i);
411
412
                while (strlen (item) < s)
413
                    strcat (item, " ");
414
            }
415
416
            printf ("|%s ", item);
417
418
        }
419
        printf ("\n");
420
    }
421
422
//    for (u32 r = 0; r < 2; ++r)
423
//        d_m3Assert (regAllocated [r] == 0);         // reg allocation & stack out of sync
424
425
    u16 maxSlot = GetMaxUsedSlotPlusOne (o);
426
427
    if (maxSlot > o->slotFirstDynamicIndex)
428
    {
429
        d_m3Log (stack, "                      -");
430
431
        for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i)
432
            printf ("----");
433
434
        printf ("\n");
435
436
        d_m3Log (stack, "                 slot |");
437
        for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i)
438
            printf ("%3d|", i);
439
440
        printf ("\n");
441
        d_m3Log (stack, "                alloc |");
442
443
        for (u16 i = o->slotFirstDynamicIndex; i < maxSlot; ++i)
444
        {
445
            printf ("%3d|", o->m3Slots [i]);
446
        }
447
448
        printf ("\n");
449
    }
450
    d_m3Log(stack, "\n");
451
}
452
453
454
static const char *  GetOpcodeIndentionString  (i32 blockDepth)
455
{
456
    blockDepth += 1;
457
458
    if (blockDepth < 0)
459
        blockDepth = 0;
460
461
    static const char * s_spaces = ".......................................................................................";
462
    const char * indent = s_spaces + strlen (s_spaces);
463
    indent -= (blockDepth * 2);
464
    if (indent < s_spaces)
465
        indent = s_spaces;
466
467
    return indent;
468
}
469
470
471
const char *  get_indention_string  (IM3Compilation o)
472
{
473
    return GetOpcodeIndentionString (o->block.depth+4);
474
}
475
476
477
void  log_opcode  (IM3Compilation o, m3opcode_t i_opcode)
478
{
479
    i32 depth = o->block.depth;
480
    if (i_opcode == c_waOp_end or i_opcode == c_waOp_else)
481
        depth--;
482
    (void) depth;
483
484
    m3log (compile, "%4d | 0x%02x  %s %s", o->numOpcodes++, i_opcode, GetOpcodeIndentionString (depth), GetOpInfo(i_opcode)->name);
485
}
486
487
488
void  log_emit  (IM3Compilation o, IM3Operation i_operation)
489
{
490
    OpInfo i = find_operation_info (i_operation);
491
492
    d_m3Log(emit, "");
493
    if (i.info)
494
    {
495
        printf ("%p: %s\n", GetPagePC (o->page),  i.info->name);
496
    }
497
    else printf ("not found: %p\n", i_operation);
498
}
499
500
#endif // DEBUG
501
502
503
# if d_m3EnableOpProfiling
504
505
typedef struct M3ProfilerSlot
506
{
507
    cstr_t      opName;
508
    u64         hitCount;
509
}
510
M3ProfilerSlot;
511
512
static M3ProfilerSlot s_opProfilerCounts [d_m3ProfilerSlotMask + 1] = {};
513
514
void  ProfileHit  (cstr_t i_operationName)
515
{
516
    u64 ptr = (u64) i_operationName;
517
518
    M3ProfilerSlot * slot = & s_opProfilerCounts [ptr & d_m3ProfilerSlotMask];
519
520
    if (slot->opName)
521
    {
522
        if (slot->opName != i_operationName)
523
        {
524
            m3_Abort ("profiler slot collision; increase d_m3ProfilerSlotMask");
525
        }
526
    }
527
528
    slot->opName = i_operationName;
529
    slot->hitCount++;
530
}
531
532
533
void  m3_PrintProfilerInfo  ()
534
{
535
    M3ProfilerSlot dummy;
536
    M3ProfilerSlot * maxSlot = & dummy;
537
538
    do
539
    {
540
        maxSlot->hitCount = 0;
541
542
        for (u32 i = 0; i <= d_m3ProfilerSlotMask; ++i)
543
        {
544
            M3ProfilerSlot * slot = & s_opProfilerCounts [i];
545
546
            if (slot->opName)
547
            {
548
                if (slot->hitCount > maxSlot->hitCount)
549
                    maxSlot = slot;
550
            }
551
        }
552
553
        if (maxSlot->opName)
554
        {
555
            fprintf (stderr, "%13llu  %s\n", maxSlot->hitCount, maxSlot->opName);
556
            maxSlot->opName = NULL;
557
        }
558
    }
559
    while (maxSlot->hitCount);
560
}
561
562
# else
563
564
11.9k
void  m3_PrintProfilerInfo  () {}
565
566
# endif
567