Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/spesh/dump.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Auto-growing buffer. */
4
typedef struct {
5
    char   *buffer;
6
    size_t  alloc;
7
    size_t  pos;
8
} DumpStr;
9
0
static void append(DumpStr *ds, char *to_add) {
10
0
    size_t len = strlen(to_add);
11
0
    if (ds->pos + len >= ds->alloc) {
12
0
        ds->alloc *= 4;
13
0
        if (ds->pos + len >= ds->alloc)
14
0
            ds->alloc += len;
15
0
        ds->buffer = MVM_realloc(ds->buffer, ds->alloc);
16
0
    }
17
0
    memcpy(ds->buffer + ds->pos, to_add, len);
18
0
    ds->pos += len;
19
0
}
20
21
0
static size_t tell_ds(DumpStr *ds) {
22
0
    return ds->pos;
23
0
}
24
25
0
static void rewind_ds(DumpStr *ds, size_t target) {
26
0
    if (ds->pos > target) {
27
0
        ds->pos = target;
28
0
        ds->buffer[ds->pos + 1] = '\0';
29
0
    }
30
0
}
31
32
/* Formats a string and then appends it. */
33
MVM_FORMAT(printf, 2, 3)
34
0
static void appendf(DumpStr *ds, const char *fmt, ...) {
35
0
    char *c_message = MVM_malloc(1024);
36
0
    va_list args;
37
0
    va_start(args, fmt);
38
0
    vsnprintf(c_message, 1023, fmt, args);
39
0
    append(ds, c_message);
40
0
    MVM_free(c_message);
41
0
    va_end(args);
42
0
}
43
44
/* Turns a MoarVM string into a C string and appends it. */
45
0
static void append_str(MVMThreadContext *tc, DumpStr *ds, MVMString *s) {
46
0
    char *cs = MVM_string_utf8_encode_C_string(tc, s);
47
0
    append(ds, cs);
48
0
    MVM_free(cs);
49
0
}
50
51
/* Appends a null at the end. */
52
0
static void append_null(DumpStr *ds) {
53
0
    append(ds, " "); /* Delegate realloc if we're really unlucky. */
54
0
    ds->buffer[ds->pos - 1] = '\0';
55
0
}
56
57
typedef struct {
58
    MVMuint32 total_size;
59
    MVMuint32 inlined_size;
60
} SpeshGraphSizeStats;
61
62
/* Dumps a basic block. */
63
0
static void dump_bb(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g, MVMSpeshBB *bb, SpeshGraphSizeStats *stats) {
64
0
    MVMSpeshIns *cur_ins;
65
0
    MVMint64     i;
66
0
    MVMint32     size = 0;
67
0
68
0
    /* Heading. */
69
0
    appendf(ds, "  BB %d (%p):\n", bb->idx, bb);
70
0
71
0
    if (bb->inlined) {
72
0
        append(ds, "    Inlined\n");
73
0
    }
74
0
75
0
    {
76
0
        /* Also, we have a line number */
77
0
        MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc);
78
0
        MVMuint32 line_number;
79
0
        if (bbba) {
80
0
            line_number = bbba->line_number;
81
0
            MVM_free(bbba);
82
0
        } else {
83
0
            line_number = -1;
84
0
        }
85
0
        appendf(ds, "    line: %d (pc %d)\n", line_number, bb->initial_pc);
86
0
    }
87
0
88
0
    /* Instructions. */
89
0
    append(ds, "    Instructions:\n");
90
0
    cur_ins = bb->first_ins;
91
0
    while (cur_ins) {
92
0
        MVMSpeshAnn *ann = cur_ins->annotations;
93
0
        MVMuint32 line_number;
94
0
95
0
        while (ann) {
96
0
            /* These four annotations carry a deopt index that we can find a
97
0
             * corresponding line number for */
98
0
            if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS
99
0
                || ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS
100
0
                || ann->type == MVM_SPESH_ANN_DEOPT_INLINE
101
0
                || ann->type == MVM_SPESH_ANN_DEOPT_OSR) {
102
0
                MVMBytecodeAnnotation *ba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, g->deopt_addrs[2 * ann->data.deopt_idx]);
103
0
                if (ba) {
104
0
                    line_number = ba->line_number;
105
0
                    MVM_free(ba);
106
0
                } else {
107
0
                    line_number = -1;
108
0
                }
109
0
            }
110
0
            switch (ann->type) {
111
0
                case MVM_SPESH_ANN_FH_START:
112
0
                    appendf(ds, "      [Annotation: FH Start (%d)]\n",
113
0
                        ann->data.frame_handler_index);
114
0
                    break;
115
0
                case MVM_SPESH_ANN_FH_END:
116
0
                    appendf(ds, "      [Annotation: FH End (%d)]\n",
117
0
                        ann->data.frame_handler_index);
118
0
                    break;
119
0
                case MVM_SPESH_ANN_FH_GOTO:
120
0
                    appendf(ds, "      [Annotation: FH Goto (%d)]\n",
121
0
                        ann->data.frame_handler_index);
122
0
                    break;
123
0
                case MVM_SPESH_ANN_DEOPT_ONE_INS:
124
0
                    appendf(ds, "      [Annotation: INS Deopt One (idx %d -> pc %d; line %d)]\n",
125
0
                        ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number);
126
0
                    break;
127
0
                case MVM_SPESH_ANN_DEOPT_ALL_INS:
128
0
                    appendf(ds, "      [Annotation: INS Deopt All (idx %d -> pc %d; line %d)]\n",
129
0
                        ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number);
130
0
                    break;
131
0
                case MVM_SPESH_ANN_INLINE_START:
132
0
                    appendf(ds, "      [Annotation: Inline Start (%d)]\n",
133
0
                        ann->data.inline_idx);
134
0
                    break;
135
0
                case MVM_SPESH_ANN_INLINE_END:
136
0
                    appendf(ds, "      [Annotation: Inline End (%d)]\n",
137
0
                        ann->data.inline_idx);
138
0
                    break;
139
0
                case MVM_SPESH_ANN_DEOPT_INLINE:
140
0
                    appendf(ds, "      [Annotation: INS Deopt Inline (idx %d -> pc %d; line %d)]\n",
141
0
                        ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number);
142
0
                    break;
143
0
                case MVM_SPESH_ANN_DEOPT_OSR:
144
0
                    appendf(ds, "      [Annotation: INS Deopt OSR (idx %d -> pc %d); line %d]\n",
145
0
                        ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number);
146
0
                    break;
147
0
                case MVM_SPESH_ANN_LINENO: {
148
0
                    char *cstr = MVM_string_utf8_encode_C_string(tc,
149
0
                        MVM_cu_string(tc, g->sf->body.cu, ann->data.lineno.filename_string_index));
150
0
                    appendf(ds, "      [Annotation: Line Number: %s:%d]\n",
151
0
                        cstr, ann->data.lineno.line_number);
152
0
                    MVM_free(cstr);
153
0
                    break;
154
0
                }
155
0
                case MVM_SPESH_ANN_LOGGED:
156
0
                    appendf(ds, "      [Annotation: Logged (bytecode offset %d)]\n",
157
0
                        ann->data.bytecode_offset);
158
0
                    break;
159
0
                default:
160
0
                    appendf(ds, "      [Annotation: %d (unknown)]\n", ann->type);
161
0
            }
162
0
            ann = ann->next;
163
0
        }
164
0
165
0
        appendf(ds, "      %-15s ", cur_ins->info->name);
166
0
        if (cur_ins->info->opcode == MVM_SSA_PHI) {
167
0
            for (i = 0; i < cur_ins->info->num_operands; i++) {
168
0
                MVMint16 orig = cur_ins->operands[i].reg.orig;
169
0
                MVMint16 regi = cur_ins->operands[i].reg.i;
170
0
                if (i)
171
0
                    append(ds, ", ");
172
0
                if (orig < 10) append(ds, " ");
173
0
                if (regi < 10) append(ds, " ");
174
0
                appendf(ds, "r%d(%d)", orig, regi);
175
0
            }
176
0
        }
177
0
        else {
178
0
            /* Count the opcode itself */
179
0
            size += 2;
180
0
            for (i = 0; i < cur_ins->info->num_operands; i++) {
181
0
                if (i)
182
0
                    append(ds, ", ");
183
0
                switch (cur_ins->info->operands[i] & MVM_operand_rw_mask) {
184
0
                    case MVM_operand_read_reg:
185
0
                    case MVM_operand_write_reg: {
186
0
                        MVMint16 orig = cur_ins->operands[i].reg.orig;
187
0
                        MVMint16 regi = cur_ins->operands[i].reg.i;
188
0
                        if (orig < 10) append(ds, " ");
189
0
                        if (regi < 10) append(ds, " ");
190
0
                        appendf(ds, "r%d(%d)", orig, regi);
191
0
                        size += 4;
192
0
                        break;
193
0
                    }
194
0
                    case MVM_operand_read_lex:
195
0
                    case MVM_operand_write_lex: {
196
0
                        MVMStaticFrameBody *cursor = &g->sf->body;
197
0
                        MVMuint32 ascension;
198
0
                        appendf(ds, "lex(idx=%d,outers=%d", cur_ins->operands[i].lex.idx,
199
0
                            cur_ins->operands[i].lex.outers);
200
0
                        for (ascension = 0;
201
0
                                ascension < cur_ins->operands[i].lex.outers;
202
0
                                ascension++, cursor = &cursor->outer->body) { };
203
0
                        if (cursor->fully_deserialized) {
204
0
                            if (cur_ins->operands[i].lex.idx < cursor->num_lexicals) {
205
0
                                char *cstr = MVM_string_utf8_encode_C_string(tc, cursor->lexical_names_list[cur_ins->operands[i].lex.idx]->key);
206
0
                                appendf(ds, ",%s)", cstr);
207
0
                                MVM_free(cstr);
208
0
                            } else {
209
0
                                append(ds, ",<out of bounds>)");
210
0
                            }
211
0
                        } else {
212
0
                            append(ds, ",<pending deserialization>)");
213
0
                        }
214
0
                        size += 4;
215
0
                        break;
216
0
                    }
217
0
                    case MVM_operand_literal: {
218
0
                        MVMuint32 type = cur_ins->info->operands[i] & MVM_operand_type_mask;
219
0
                        switch (type) {
220
0
                        case MVM_operand_ins: {
221
0
                            MVMint32 bb_idx = cur_ins->operands[i].ins_bb->idx;
222
0
                            if (bb_idx < 100) append(ds, " ");
223
0
                            if (bb_idx < 10)  append(ds, " ");
224
0
                            appendf(ds, "BB(%d)", bb_idx);
225
0
                            size += 4;
226
0
                            break;
227
0
                        }
228
0
                        case MVM_operand_int8:
229
0
                            appendf(ds, "liti8(%"PRId8")", cur_ins->operands[i].lit_i8);
230
0
                            size += 2;
231
0
                            break;
232
0
                        case MVM_operand_int16:
233
0
                            appendf(ds, "liti16(%"PRId16")", cur_ins->operands[i].lit_i16);
234
0
                            size += 2;
235
0
                            break;
236
0
                        case MVM_operand_int32:
237
0
                            appendf(ds, "liti32(%"PRId32")", cur_ins->operands[i].lit_i32);
238
0
                            size += 4;
239
0
                            break;
240
0
                        case MVM_operand_uint32:
241
0
                            appendf(ds, "litui32(%"PRIu32")", cur_ins->operands[i].lit_ui32);
242
0
                            size += 4;
243
0
                            break;
244
0
                        case MVM_operand_int64:
245
0
                            appendf(ds, "liti64(%"PRId64")", cur_ins->operands[i].lit_i64);
246
0
                            size += 8;
247
0
                            break;
248
0
                        case MVM_operand_num32:
249
0
                            appendf(ds, "litn32(%f)", cur_ins->operands[i].lit_n32);
250
0
                            size += 4;
251
0
                            break;
252
0
                        case MVM_operand_num64:
253
0
                            appendf(ds, "litn64(%g)", cur_ins->operands[i].lit_n64);
254
0
                            size += 8;
255
0
                            break;
256
0
                        case MVM_operand_str: {
257
0
                            char *cstr = MVM_string_utf8_encode_C_string(tc,
258
0
                                MVM_cu_string(tc, g->sf->body.cu, cur_ins->operands[i].lit_str_idx));
259
0
                            appendf(ds, "lits(%s)", cstr);
260
0
                            MVM_free(cstr);
261
0
                            size += 8;
262
0
                            break;
263
0
                        }
264
0
                        case MVM_operand_callsite: {
265
0
                            MVMCallsite *callsite = g->sf->body.cu->body.callsites[cur_ins->operands[i].callsite_idx];
266
0
                            appendf(ds, "callsite(%p, %d arg, %d pos, %s, %s)",
267
0
                                    callsite,
268
0
                                    callsite->arg_count, callsite->num_pos,
269
0
                                    callsite->has_flattening ? "flattening" : "nonflattening",
270
0
                                    callsite->is_interned ? "interned" : "noninterned");
271
0
                            size += 2;
272
0
                            break;
273
0
274
0
                        }
275
0
                        case MVM_operand_spesh_slot:
276
0
                            appendf(ds, "sslot(%"PRId16")", cur_ins->operands[i].lit_i16);
277
0
                            size += 2;
278
0
                            break;
279
0
                        case MVM_operand_coderef: {
280
0
                            MVMCodeBody *body = &((MVMCode*)g->sf->body.cu->body.coderefs[cur_ins->operands[i].coderef_idx])->body;
281
0
                            MVMBytecodeAnnotation *anno = MVM_bytecode_resolve_annotation(tc, &body->sf->body, 0);
282
0
283
0
                            append(ds, "coderef(");
284
0
285
0
                            if (anno) {
286
0
                                char *filestr = MVM_string_utf8_encode_C_string(tc,
287
0
                                    MVM_cu_string(tc, g->sf->body.cu, anno->filename_string_heap_index));
288
0
                                appendf(ds, "%s:%d%s)", filestr, anno->line_number, body->outer ? " (closure)" : "");
289
0
                                MVM_free(filestr);
290
0
                            } else {
291
0
                                append(ds, "??\?)");
292
0
                            }
293
0
294
0
                            size += 2;
295
0
296
0
                            MVM_free(anno);
297
0
                            break;
298
0
                        }
299
0
                        default:
300
0
                            append(ds, "<nyi(lit)>");
301
0
                        }
302
0
                        break;
303
0
                    }
304
0
                    default:
305
0
                        append(ds, "<nyi>");
306
0
                }
307
0
            }
308
0
            if (cur_ins->info->opcode == MVM_OP_wval || cur_ins->info->opcode == MVM_OP_wval_wide) {
309
0
                /* We can try to find out what the debug_name of this thing is. */
310
0
                MVMint16 dep = cur_ins->operands[1].lit_i16;
311
0
                MVMint64 idx;
312
0
                MVMCollectable *result = NULL;
313
0
                MVMSerializationContext *sc;
314
0
                char *debug_name = NULL;
315
0
                const char *repr_name = NULL;
316
0
                if (cur_ins->info->opcode == MVM_OP_wval) {
317
0
                    idx = cur_ins->operands[2].lit_i16;
318
0
                } else {
319
0
                    idx = cur_ins->operands[2].lit_i64;
320
0
                }
321
0
                sc = MVM_sc_get_sc(tc, g->sf->body.cu, dep);
322
0
                if (sc)
323
0
                    result = (MVMCollectable *)MVM_sc_try_get_object(tc, sc, idx);
324
0
                if (result) {
325
0
                    if (result->flags & MVM_CF_STABLE) {
326
0
                        debug_name = MVM_6model_get_stable_debug_name(tc, (MVMSTable *)result);
327
0
                        repr_name  = ((MVMSTable *)result)->REPR->name;
328
0
                    } else {
329
0
                        debug_name = MVM_6model_get_debug_name(tc, (MVMObject *)result);
330
0
                        repr_name  = REPR(result)->name;
331
0
                    }
332
0
                    if (debug_name) {
333
0
                        appendf(ds, " (%s: %s)", repr_name, debug_name);
334
0
                    } else {
335
0
                        appendf(ds, " (%s: ?)", repr_name);
336
0
                    }
337
0
                } else {
338
0
                    appendf(ds, " (not deserialized)");
339
0
                }
340
0
            }
341
0
        }
342
0
        append(ds, "\n");
343
0
        cur_ins = cur_ins->next;
344
0
    }
345
0
346
0
    if (stats) {
347
0
        if (bb->inlined)
348
0
            stats->inlined_size += size;
349
0
        stats->total_size += size;
350
0
    }
351
0
352
0
    /* Predecessors and successors. */
353
0
    append(ds, "    Successors: ");
354
0
    for (i = 0; i < bb->num_succ; i++)
355
0
        appendf(ds, (i == 0 ? "%d" : ", %d"), bb->succ[i]->idx);
356
0
    append(ds, "\n    Predecessors: ");
357
0
    for (i = 0; i < bb->num_pred; i++)
358
0
        appendf(ds, (i == 0 ? "%d" : ", %d"), bb->pred[i]->idx);
359
0
    append(ds, "\n    Dominance children: ");
360
0
    for (i = 0; i < bb->num_children; i++)
361
0
        appendf(ds, (i == 0 ? "%d" : ", %d"), bb->children[i]->idx);
362
0
    append(ds, "\n\n");
363
0
}
364
365
/* Dumps the facts table. */
366
0
static void dump_facts(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g) {
367
0
    MVMuint16 i, j, num_locals, num_facts;
368
0
    num_locals = g->num_locals;
369
0
    for (i = 0; i < num_locals; i++) {
370
0
        num_facts = g->fact_counts[i];
371
0
        for (j = 0; j < num_facts; j++) {
372
0
            MVMint32 usages = g->facts[i][j].usages;
373
0
            MVMint32 flags  = g->facts[i][j].flags;
374
0
            if (i < 10) append(ds, " ");
375
0
            if (j < 10) append(ds, " ");
376
0
            if (flags || g->facts[i][j].dead_writer || g->facts[i][j].writer && g->facts[i][j].writer->info->opcode == MVM_SSA_PHI) {
377
0
                appendf(ds, "    r%d(%d): usages=%d, flags=%-5d", i, j, usages, flags);
378
0
                if (flags & 1) {
379
0
                    append(ds, " KnTyp");
380
0
                }
381
0
                if (flags & 2) {
382
0
                    append(ds, " KnVal");
383
0
                }
384
0
                if (flags & 4) {
385
0
                    append(ds, " Dcntd");
386
0
                }
387
0
                if (flags & 8) {
388
0
                    append(ds, " Concr");
389
0
                }
390
0
                if (flags & 16) {
391
0
                    append(ds, " TyObj");
392
0
                }
393
0
                if (flags & 32) {
394
0
                    append(ds, " KnDcT");
395
0
                }
396
0
                if (flags & 64) {
397
0
                    append(ds, " DCncr");
398
0
                }
399
0
                if (flags & 128) {
400
0
                    append(ds, " DcTyO");
401
0
                }
402
0
                if (flags & 256) {
403
0
                    append(ds, " LogGd");
404
0
                }
405
0
                if (flags & 512) {
406
0
                    append(ds, " HashI");
407
0
                }
408
0
                if (flags & 1024) {
409
0
                    append(ds, " ArrIt");
410
0
                }
411
0
                if (flags & 2048) {
412
0
                    append(ds, " KBxSr");
413
0
                }
414
0
                if (flags & 4096) {
415
0
                    append(ds, " MgWLG");
416
0
                }
417
0
                if (flags & 8192) {
418
0
                    append(ds, " KRWCn");
419
0
                }
420
0
                if (g->facts[i][j].dead_writer) {
421
0
                    append(ds, " DeadWriter");
422
0
                }
423
0
                if (g->facts[i][j].writer && g->facts[i][j].writer->info->opcode == MVM_SSA_PHI) {
424
0
                    appendf(ds, " (merged from %d regs)", g->facts[i][j].writer->info->num_operands - 1);
425
0
                }
426
0
            }
427
0
            else
428
0
                appendf(ds, "    r%d(%d): usages=%d, flags=%d", i, j, usages, flags);
429
0
            append(ds, "\n");
430
0
        }
431
0
        append(ds, "\n");
432
0
    }
433
0
}
434
435
0
static void dump_callsite(MVMThreadContext *tc, DumpStr *ds, MVMCallsite *cs) {
436
0
    MVMuint16 i;
437
0
    appendf(ds, "Callsite %p (%d args, %d pos)\n", cs, cs->arg_count, cs->num_pos);
438
0
    for (i = 0; i < (cs->arg_count - cs->num_pos) / 2; i++) {
439
0
        if (cs->arg_names[i]) {
440
0
            char * argname_utf8 = MVM_string_utf8_encode_C_string(tc, cs->arg_names[i]);
441
0
            appendf(ds, "  - %s\n", argname_utf8);
442
0
            MVM_free(argname_utf8);
443
0
        }
444
0
    }
445
0
    if (cs->num_pos)
446
0
        append(ds, "Positional flags: ");
447
0
    for (i = 0; i < cs->num_pos; i++) {
448
0
        MVMCallsiteEntry arg_flag = cs->arg_flags[i];
449
0
450
0
        if (i)
451
0
            append(ds, ", ");
452
0
453
0
        if (arg_flag == MVM_CALLSITE_ARG_OBJ) {
454
0
            append(ds, "obj");
455
0
        } else if (arg_flag == MVM_CALLSITE_ARG_INT) {
456
0
            append(ds, "int");
457
0
        } else if (arg_flag == MVM_CALLSITE_ARG_NUM) {
458
0
            append(ds, "num");
459
0
        } else if (arg_flag == MVM_CALLSITE_ARG_STR) {
460
0
            append(ds, "str");
461
0
        }
462
0
    }
463
0
    if (cs->num_pos)
464
0
        append(ds, "\n");
465
0
    append(ds, "\n");
466
0
}
467
468
0
static void dump_fileinfo(MVMThreadContext *tc, DumpStr *ds, MVMStaticFrame *sf) {
469
0
    MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &sf->body, 0);
470
0
    MVMCompUnit            *cu = sf->body.cu;
471
0
    MVMint32           str_idx = ann ? ann->filename_string_heap_index : 0;
472
0
    MVMint32           line_nr = ann ? ann->line_number : 1;
473
0
    MVMString        *filename = cu->body.filename;
474
0
    char        *filename_utf8 = "<unknown>";
475
0
    if (ann && str_idx < cu->body.num_strings) {
476
0
        filename = MVM_cu_string(tc, cu, str_idx);
477
0
    }
478
0
    if (filename)
479
0
        filename_utf8 = MVM_string_utf8_encode_C_string(tc, filename);
480
0
    appendf(ds, "%s:%d", filename_utf8, line_nr);
481
0
    if (filename)
482
0
        MVM_free(filename_utf8);
483
0
    MVM_free(ann);
484
0
}
485
486
/* Dump a spesh graph into string form, for debugging purposes. */
487
0
char * MVM_spesh_dump(MVMThreadContext *tc, MVMSpeshGraph *g) {
488
0
    MVMSpeshBB *cur_bb;
489
0
    SpeshGraphSizeStats stats;
490
0
    DumpStr ds;
491
0
492
0
    stats.total_size = 0;
493
0
    stats.inlined_size = 0;
494
0
495
0
    /* Allocate buffer. */
496
0
    ds.alloc  = 8192;
497
0
    ds.buffer = MVM_malloc(ds.alloc);
498
0
    ds.pos    = 0;
499
0
500
0
    /* Dump name and CUID. */
501
0
    append(&ds, "Spesh of '");
502
0
    append_str(tc, &ds, g->sf->body.name);
503
0
    append(&ds, "' (cuid: ");
504
0
    append_str(tc, &ds, g->sf->body.cuuid);
505
0
    append(&ds, ", file: ");
506
0
    dump_fileinfo(tc, &ds, g->sf);
507
0
    append(&ds, ")\n");
508
0
    if (g->cs)
509
0
        dump_callsite(tc, &ds, g->cs);
510
0
    if (!g->cs)
511
0
        append(&ds, "\n");
512
0
513
0
    /* Go over all the basic blocks and dump them. */
514
0
    cur_bb = g->entry;
515
0
    while (cur_bb) {
516
0
        dump_bb(tc, &ds, g, cur_bb, &stats);
517
0
        cur_bb = cur_bb->linear_next;
518
0
    }
519
0
520
0
    /* Dump facts. */
521
0
    if (g->facts) {
522
0
        append(&ds, "\nFacts:\n");
523
0
        dump_facts(tc, &ds, g);
524
0
    }
525
0
526
0
    /* Dump spesh slots. */
527
0
    if (g->num_spesh_slots) {
528
0
        MVMuint32 i;
529
0
        append(&ds, "\nSpesh slots:\n");
530
0
        for (i = 0; i < g->num_spesh_slots; i++) {
531
0
            MVMCollectable *value = g->spesh_slots[i];
532
0
            if (value == NULL)
533
0
                appendf(&ds, "    %d = NULL\n", i);
534
0
            else if (value->flags & MVM_CF_STABLE)
535
0
                appendf(&ds, "    %d = STable (%s)\n", i,
536
0
                    MVM_6model_get_stable_debug_name(tc, (MVMSTable *)value));
537
0
            else if (value->flags & MVM_CF_TYPE_OBJECT)
538
0
                appendf(&ds, "    %d = Type Object (%s)\n", i,
539
0
                    MVM_6model_get_debug_name(tc, (MVMObject *)value));
540
0
            else
541
0
                appendf(&ds, "    %d = Instance (%s)\n", i,
542
0
                    MVM_6model_get_debug_name(tc, (MVMObject *)value));
543
0
        }
544
0
    }
545
0
546
0
    append(&ds, "\n");
547
0
548
0
    /* Print out frame size */
549
0
    if (stats.inlined_size)
550
0
        appendf(&ds, "Frame size: %u bytes (%u from inlined frames)\n", stats.total_size, stats.inlined_size);
551
0
    else
552
0
        appendf(&ds, "Frame size: %u bytes\n", stats.total_size);
553
0
554
0
    append_null(&ds);
555
0
    return ds.buffer;
556
0
}
557
558
/* Dumps a spesh stats type typle. */
559
void dump_stats_type_tuple(MVMThreadContext *tc, DumpStr *ds, MVMCallsite *cs,
560
0
                           MVMSpeshStatsType *type_tuple, char *prefix) {
561
0
    MVMuint32 j;
562
0
    for (j = 0; j < cs->flag_count; j++) {
563
0
        MVMObject *type = type_tuple[j].type;
564
0
        if (type) {
565
0
            MVMObject *decont_type = type_tuple[j].decont_type;
566
0
            appendf(ds, "%sType %d: %s%s (%s)",
567
0
                prefix, j,
568
0
                (type_tuple[j].rw_cont ? "RW " : ""),
569
0
                MVM_6model_get_stable_debug_name(tc, type->st),
570
0
                (type_tuple[j].type_concrete ? "Conc" : "TypeObj"));
571
0
            if (decont_type)
572
0
                appendf(ds, " of %s (%s)",
573
0
                    MVM_6model_get_stable_debug_name(tc, decont_type->st),
574
0
                    (type_tuple[j].decont_type_concrete ? "Conc" : "TypeObj"));
575
0
            append(ds, "\n");
576
0
        }
577
0
    }
578
0
}
579
580
/* Dumps the statistics associated with a particular callsite object. */
581
0
void dump_stats_by_callsite(MVMThreadContext *tc, DumpStr *ds, MVMSpeshStatsByCallsite *css) {
582
0
    MVMuint32 i, j, k;
583
0
584
0
    if (css->cs)
585
0
        dump_callsite(tc, ds, css->cs);
586
0
    else
587
0
        append(ds, "No interned callsite\n");
588
0
    appendf(ds, "    Callsite hits: %d\n\n", css->hits);
589
0
    if (css->osr_hits)
590
0
        appendf(ds, "    OSR hits: %d\n\n", css->osr_hits);
591
0
    appendf(ds, "    Maximum stack depth: %d\n\n", css->max_depth);
592
0
593
0
    for (i = 0; i < css->num_by_type; i++) {
594
0
        MVMSpeshStatsByType *tss = &(css->by_type[i]);
595
0
        appendf(ds, "    Type tuple %d\n", i);
596
0
        dump_stats_type_tuple(tc, ds, css->cs, tss->arg_types, "        ");
597
0
        appendf(ds, "        Hits: %d\n", tss->hits);
598
0
        if (tss->osr_hits)
599
0
            appendf(ds, "        OSR hits: %d\n", tss->osr_hits);
600
0
        appendf(ds, "        Maximum stack depth: %d\n", tss->max_depth);
601
0
        if (tss->num_by_offset) {
602
0
            append(ds, "        Logged at offset:\n");
603
0
            for (j = 0; j < tss->num_by_offset; j++) {
604
0
                MVMSpeshStatsByOffset *oss = &(tss->by_offset[j]);
605
0
                appendf(ds, "            %d:\n", oss->bytecode_offset);
606
0
                for (k = 0; k < oss->num_types; k++)
607
0
                    appendf(ds, "                %d x type %s (%s)\n",
608
0
                        oss->types[k].count,
609
0
                        MVM_6model_get_stable_debug_name(tc, oss->types[k].type->st),
610
0
                        (oss->types[k].type_concrete ? "Conc" : "TypeObj"));
611
0
                for (k = 0; k < oss->num_invokes; k++) {
612
0
                    char *body_name = MVM_string_utf8_encode_C_string(tc, oss->invokes[k].sf->body.name);
613
0
                    char *body_cuuid = MVM_string_utf8_encode_C_string(tc, oss->invokes[k].sf->body.cuuid);
614
0
                    appendf(ds,
615
0
                        "                %d x static frame '%s' (%s) (caller is outer: %d, multi %d)\n",
616
0
                        oss->invokes[k].count,
617
0
                        body_name,
618
0
                        body_cuuid,
619
0
                        oss->invokes[k].caller_is_outer_count,
620
0
                        oss->invokes[k].was_multi_count);
621
0
                    MVM_free(body_name);
622
0
                    MVM_free(body_cuuid);
623
0
                }
624
0
                for (k = 0; k < oss->num_type_tuples; k++) {
625
0
                    appendf(ds, "                %d x type tuple:\n",
626
0
                        oss->type_tuples[k].count);
627
0
                    dump_stats_type_tuple(tc, ds, oss->type_tuples[k].cs,
628
0
                        oss->type_tuples[k].arg_types,
629
0
                        "                    ");
630
0
                }
631
0
                for (k = 0; k < oss->num_plugin_guards; k++)
632
0
                    appendf(ds, "                %d x spesh plugin guard index %d\n",
633
0
                        oss->plugin_guards[k].count,
634
0
                        oss->plugin_guards[k].guard_index);
635
0
            }
636
0
        }
637
0
        append(ds, "\n");
638
0
    }
639
0
}
640
641
/* Dumps the statistics associated with a static frame into a string. */
642
0
char * MVM_spesh_dump_stats(MVMThreadContext *tc, MVMStaticFrame *sf) {
643
0
    MVMSpeshStats *ss = sf->body.spesh->body.spesh_stats;
644
0
645
0
    DumpStr ds;
646
0
    ds.alloc  = 8192;
647
0
    ds.buffer = MVM_malloc(ds.alloc);
648
0
    ds.pos    = 0;
649
0
650
0
    /* Dump name and CUID. */
651
0
    append(&ds, "Latest statistics for '");
652
0
    append_str(tc, &ds, sf->body.name);
653
0
    append(&ds, "' (cuid: ");
654
0
    append_str(tc, &ds, sf->body.cuuid);
655
0
    append(&ds, ", file: ");
656
0
    dump_fileinfo(tc, &ds, sf);
657
0
    append(&ds, ")\n\n");
658
0
659
0
    /* Dump the spesh stats if present. */
660
0
    if (ss) {
661
0
        MVMuint32 i;
662
0
663
0
        appendf(&ds, "Total hits: %d\n", ss->hits);
664
0
        if (ss->osr_hits)
665
0
            appendf(&ds, "OSR hits: %d\n", ss->osr_hits);
666
0
        append(&ds, "\n");
667
0
668
0
        for (i = 0; i < ss->num_by_callsite; i++)
669
0
            dump_stats_by_callsite(tc, &ds, &(ss->by_callsite[i]));
670
0
671
0
        if (ss->num_static_values) {
672
0
            append(&ds, "Static values:\n");
673
0
            for (i = 0; i < ss->num_static_values; i++)
674
0
                appendf(&ds, "    - %s (%p) @ %d\n",
675
0
                    MVM_6model_get_stable_debug_name(tc, ss->static_values[i].value->st),
676
0
                    ss->static_values[i].value,
677
0
                    ss->static_values[i].bytecode_offset);
678
0
        }
679
0
    }
680
0
    else {
681
0
        append(&ds, "No spesh stats for this static frame\n");
682
0
    }
683
0
684
0
    append(&ds, "\n");
685
0
    append_null(&ds);
686
0
    return ds.buffer;
687
0
}
688
689
/* Dumps a planned specialization into a string. */
690
0
char * MVM_spesh_dump_planned(MVMThreadContext *tc, MVMSpeshPlanned *p) {
691
0
    DumpStr ds;
692
0
    ds.alloc  = 8192;
693
0
    ds.buffer = MVM_malloc(ds.alloc);
694
0
    ds.pos    = 0;
695
0
696
0
    /* Dump kind of specialization and target. */
697
0
    switch (p->kind) {
698
0
        case MVM_SPESH_PLANNED_CERTAIN:
699
0
            append(&ds, "Certain");
700
0
            break;
701
0
        case MVM_SPESH_PLANNED_OBSERVED_TYPES:
702
0
            append(&ds, "Observed type");
703
0
            break;
704
0
        case MVM_SPESH_PLANNED_DERIVED_TYPES:
705
0
            append(&ds, "Derived type");
706
0
            break;
707
0
    }
708
0
    append(&ds, " specialization of '");
709
0
    append_str(tc, &ds, p->sf->body.name);
710
0
    append(&ds, "' (cuid: ");
711
0
    append_str(tc, &ds, p->sf->body.cuuid);
712
0
    append(&ds, ", file: ");
713
0
    dump_fileinfo(tc, &ds, p->sf);
714
0
    append(&ds, ")\n\n");
715
0
716
0
    /* Dump the callsite of the specialization. */
717
0
    if (p->cs_stats->cs) {
718
0
        append(&ds, "The specialization is for the callsite:\n");
719
0
        dump_callsite(tc, &ds, p->cs_stats->cs);
720
0
    }
721
0
    else {
722
0
        append(&ds, "The specialization is for when there is no interned callsite.\n");
723
0
    }
724
0
725
0
    /* Dump reasoning. */
726
0
    switch (p->kind) {
727
0
        case MVM_SPESH_PLANNED_CERTAIN:
728
0
            if (p->cs_stats->hits >= MVM_spesh_threshold(tc, p->sf))
729
0
                appendf(&ds,
730
0
                    "It was planned due to the callsite receiving %u hits.\n",
731
0
                    p->cs_stats->hits);
732
0
            else if (p->cs_stats->osr_hits >= MVM_SPESH_PLAN_CS_MIN_OSR)
733
0
                appendf(&ds,
734
0
                    "It was planned due to the callsite receiving %u OSR hits.\n",
735
0
                    p->cs_stats->osr_hits);
736
0
            else
737
0
                append(&ds, "It was planned for unknown reasons.\n");
738
0
            break;
739
0
        case MVM_SPESH_PLANNED_OBSERVED_TYPES: {
740
0
            MVMCallsite *cs = p->cs_stats->cs;
741
0
            MVMuint32 hit_percent = p->cs_stats->hits
742
0
               ? (100 * p->type_stats[0]->hits) / p->cs_stats->hits
743
0
               : 0;
744
0
            MVMuint32 osr_hit_percent = p->cs_stats->osr_hits
745
0
               ? (100 * p->type_stats[0]->osr_hits) / p->cs_stats->osr_hits
746
0
               : 0;
747
0
            append(&ds, "It was planned for the type tuple:\n");
748
0
            dump_stats_type_tuple(tc, &ds, cs, p->type_tuple, "    ");
749
0
            if (osr_hit_percent >= MVM_SPESH_PLAN_TT_OBS_PERCENT_OSR)
750
0
                appendf(&ds, "Which received %u OSR hits (%u%% of the %u callsite OSR hits).\n",
751
0
                    p->type_stats[0]->osr_hits, osr_hit_percent, p->cs_stats->osr_hits);
752
0
            else if (hit_percent >= MVM_SPESH_PLAN_TT_OBS_PERCENT)
753
0
                appendf(&ds, "Which received %u hits (%u%% of the %u callsite hits).\n",
754
0
                    p->type_stats[0]->hits, hit_percent, p->cs_stats->hits);
755
0
            else
756
0
                append(&ds, "For unknown reasons.\n");
757
0
            break;
758
0
        }
759
0
        case MVM_SPESH_PLANNED_DERIVED_TYPES:
760
0
            break;
761
0
    }
762
0
763
0
    appendf(&ds, "\nThe maximum stack depth is %d.\n\n", p->max_depth);
764
0
    append_null(&ds);
765
0
    return ds.buffer;
766
0
}
767
768
/* Dumps a static frame's guard set into a string. */
769
0
char * MVM_spesh_dump_arg_guard(MVMThreadContext *tc, MVMStaticFrame *sf) {
770
0
    MVMSpeshArgGuard *ag = sf->body.spesh->body.spesh_arg_guard;
771
0
772
0
    DumpStr ds;
773
0
    ds.alloc  = 8192;
774
0
    ds.buffer = MVM_malloc(ds.alloc);
775
0
    ds.pos    = 0;
776
0
777
0
    /* Dump name and CUID. */
778
0
    append(&ds, "Latest guard tree for '");
779
0
    append_str(tc, &ds, sf->body.name);
780
0
    append(&ds, "' (cuid: ");
781
0
    append_str(tc, &ds, sf->body.cuuid);
782
0
    append(&ds, ", file: ");
783
0
    dump_fileinfo(tc, &ds, sf);
784
0
    append(&ds, ")\n\n");
785
0
786
0
    /* Dump nodes. */
787
0
    if (ag) {
788
0
        MVMuint32 i = 0;
789
0
        for (i = 0; i < ag->used_nodes; i++) {
790
0
            MVMSpeshArgGuardNode *agn = &(ag->nodes[i]);
791
0
            switch (agn->op) {
792
0
                case MVM_SPESH_GUARD_OP_CALLSITE:
793
0
                    appendf(&ds, "%u: CALLSITE %p | Y: %u, N: %u\n",
794
0
                        i, agn->cs, agn->yes, agn->no);
795
0
                    break;
796
0
                case MVM_SPESH_GUARD_OP_LOAD_ARG:
797
0
                    appendf(&ds, "%u: LOAD ARG %d | Y: %u\n",
798
0
                        i, agn->arg_index, agn->yes);
799
0
                    break;
800
0
                case MVM_SPESH_GUARD_OP_STABLE_CONC:
801
0
                    appendf(&ds, "%u: STABLE CONC %s | Y: %u, N: %u\n",
802
0
                        i, MVM_6model_get_stable_debug_name(tc, agn->st), agn->yes, agn->no);
803
0
                    break;
804
0
                case MVM_SPESH_GUARD_OP_STABLE_TYPE:
805
0
                    appendf(&ds, "%u: STABLE CONC %s | Y: %u, N: %u\n",
806
0
                        i, MVM_6model_get_stable_debug_name(tc, agn->st), agn->yes, agn->no);
807
0
                    break;
808
0
                case MVM_SPESH_GUARD_OP_DEREF_VALUE:
809
0
                    appendf(&ds, "%u: DEREF_VALUE %u | Y: %u, N: %u\n",
810
0
                        i, agn->offset, agn->yes, agn->no);
811
0
                    break;
812
0
                case MVM_SPESH_GUARD_OP_DEREF_RW:
813
0
                    appendf(&ds, "%u: DEREF_RW %u | Y: %u, N: %u\n",
814
0
                        i, agn->offset, agn->yes, agn->no);
815
0
                    break;
816
0
                case MVM_SPESH_GUARD_OP_CERTAIN_RESULT:
817
0
                    appendf(&ds, "%u: CERTAIN RESULT %u\n", i, agn->result);
818
0
                    break;
819
0
                case MVM_SPESH_GUARD_OP_RESULT:
820
0
                    appendf(&ds, "%u: RESULT %u\n", i, agn->result);
821
0
                    break;
822
0
            }
823
0
        }
824
0
    }
825
0
    else {
826
0
        append(&ds, "No argument guard nodes\n");
827
0
    }
828
0
829
0
    append(&ds, "\n");
830
0
    append_null(&ds);
831
0
    return ds.buffer;
832
0
}