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: |
15 | void 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 | |
22 | typedef struct OpInfo |
23 | { |
24 | IM3OpInfo info; |
25 | m3opcode_t opcode; |
26 | } |
27 | OpInfo; |
28 | |
29 | void 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 | |
40 | void * 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 | |
48 | void 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 | |
61 | cstr_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 | |
73 | cstr_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 | |
101 | size_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 | |
124 | cstr_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 | |
132 | cstr_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 | |
173 | static |
174 | OpInfo 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 | |
209 | d_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 | |
218 | d_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 | |
227 | d_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 | |
241 | d_m3Decoder (Branch) |
242 | { |
243 | void * target = fetch (void *); |
244 | sprintf (o_string, "%p" , target); |
245 | } |
246 | |
247 | d_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 | |
268 | d_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 | |
277 | void 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 |
294 | void 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 | |
329 | void 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 | |
449 | static 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 | |
466 | const char * get_indention_string (IM3Compilation o) |
467 | { |
468 | return GetOpcodeIndentionString (o->block.depth+4); |
469 | } |
470 | |
471 | |
472 | void 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 | |
482 | void 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 | |
499 | typedef struct M3ProfilerSlot |
500 | { |
501 | cstr_t opName; |
502 | u64 hitCount; |
503 | } |
504 | M3ProfilerSlot; |
505 | |
506 | static M3ProfilerSlot s_opProfilerCounts [d_m3ProfilerSlotMask + 1] = {}; |
507 | |
508 | void 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 | |
527 | void 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 | |
558 | void m3_PrintProfilerInfo () {} |
559 | |
560 | # endif |
561 | |
562 | |