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