Coverage Report

Created: 2025-08-26 06:22

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