/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 | c_message[vsnprintf(c_message, 1023, fmt, args)] = 0; |
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 | | /* Dumps a basic block. */ |
58 | 0 | static void dump_bb(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g, MVMSpeshBB *bb) { |
59 | 0 | MVMSpeshIns *cur_ins; |
60 | 0 | MVMint64 i; |
61 | 0 |
|
62 | 0 | /* Heading. */ |
63 | 0 | appendf(ds, " BB %d (%p):\n", bb->idx, bb); |
64 | 0 |
|
65 | 0 | if (bb->inlined) { |
66 | 0 | append(ds, " Inlined\n"); |
67 | 0 | } |
68 | 0 |
|
69 | 0 | { |
70 | 0 | /* Also, we have a line number */ |
71 | 0 | MVMBytecodeAnnotation *bbba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, bb->initial_pc); |
72 | 0 | MVMuint32 line_number; |
73 | 0 | if (bbba) { |
74 | 0 | line_number = bbba->line_number; |
75 | 0 | MVM_free(bbba); |
76 | 0 | } else { |
77 | 0 | line_number = -1; |
78 | 0 | } |
79 | 0 | appendf(ds, " line: %d (pc %d)\n", line_number, bb->initial_pc); |
80 | 0 | } |
81 | 0 |
|
82 | 0 | /* Instructions. */ |
83 | 0 | append(ds, " Instructions:\n"); |
84 | 0 | cur_ins = bb->first_ins; |
85 | 0 | while (cur_ins) { |
86 | 0 | MVMSpeshAnn *ann = cur_ins->annotations; |
87 | 0 | MVMuint32 line_number; |
88 | 0 |
|
89 | 0 | while (ann) { |
90 | 0 | /* These four annotations carry a deopt index that we can find a |
91 | 0 | * corresponding line number for */ |
92 | 0 | if (ann->type == MVM_SPESH_ANN_DEOPT_ONE_INS |
93 | 0 | || ann->type == MVM_SPESH_ANN_DEOPT_ALL_INS |
94 | 0 | || ann->type == MVM_SPESH_ANN_DEOPT_INLINE |
95 | 0 | || ann->type == MVM_SPESH_ANN_DEOPT_OSR) { |
96 | 0 | MVMBytecodeAnnotation *ba = MVM_bytecode_resolve_annotation(tc, &g->sf->body, g->deopt_addrs[2 * ann->data.deopt_idx]); |
97 | 0 | if (ba) { |
98 | 0 | line_number = ba->line_number; |
99 | 0 | MVM_free(ba); |
100 | 0 | } else { |
101 | 0 | line_number = -1; |
102 | 0 | } |
103 | 0 | } |
104 | 0 | switch (ann->type) { |
105 | 0 | case MVM_SPESH_ANN_FH_START: |
106 | 0 | appendf(ds, " [Annotation: FH Start (%d)]\n", |
107 | 0 | ann->data.frame_handler_index); |
108 | 0 | break; |
109 | 0 | case MVM_SPESH_ANN_FH_END: |
110 | 0 | appendf(ds, " [Annotation: FH End (%d)]\n", |
111 | 0 | ann->data.frame_handler_index); |
112 | 0 | break; |
113 | 0 | case MVM_SPESH_ANN_FH_GOTO: |
114 | 0 | appendf(ds, " [Annotation: FH Goto (%d)]\n", |
115 | 0 | ann->data.frame_handler_index); |
116 | 0 | break; |
117 | 0 | case MVM_SPESH_ANN_DEOPT_ONE_INS: |
118 | 0 | appendf(ds, " [Annotation: INS Deopt One (idx %d -> pc %d; line %d)]\n", |
119 | 0 | ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); |
120 | 0 | break; |
121 | 0 | case MVM_SPESH_ANN_DEOPT_ALL_INS: |
122 | 0 | appendf(ds, " [Annotation: INS Deopt All (idx %d -> pc %d; line %d)]\n", |
123 | 0 | ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); |
124 | 0 | break; |
125 | 0 | case MVM_SPESH_ANN_INLINE_START: |
126 | 0 | appendf(ds, " [Annotation: Inline Start (%d)]\n", |
127 | 0 | ann->data.inline_idx); |
128 | 0 | break; |
129 | 0 | case MVM_SPESH_ANN_INLINE_END: |
130 | 0 | appendf(ds, " [Annotation: Inline End (%d)]\n", |
131 | 0 | ann->data.inline_idx); |
132 | 0 | break; |
133 | 0 | case MVM_SPESH_ANN_DEOPT_INLINE: |
134 | 0 | appendf(ds, " [Annotation: INS Deopt Inline (idx %d -> pc %d; line %d)]\n", |
135 | 0 | ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); |
136 | 0 | break; |
137 | 0 | case MVM_SPESH_ANN_DEOPT_OSR: |
138 | 0 | appendf(ds, " [Annotation: INS Deopt OSR (idx %d -> pc %d); line %d]\n", |
139 | 0 | ann->data.deopt_idx, g->deopt_addrs[2 * ann->data.deopt_idx], line_number); |
140 | 0 | break; |
141 | 0 | default: |
142 | 0 | appendf(ds, " [Annotation: %d (unknown)]\n", ann->type); |
143 | 0 | } |
144 | 0 | ann = ann->next; |
145 | 0 | } |
146 | 0 |
|
147 | 0 | appendf(ds, " %-15s ", cur_ins->info->name); |
148 | 0 | if (cur_ins->info->opcode == MVM_SSA_PHI) { |
149 | 0 | for (i = 0; i < cur_ins->info->num_operands; i++) { |
150 | 0 | MVMint16 orig = cur_ins->operands[i].reg.orig; |
151 | 0 | MVMint16 regi = cur_ins->operands[i].reg.i; |
152 | 0 | if (i) |
153 | 0 | append(ds, ", "); |
154 | 0 | if (orig < 10) append(ds, " "); |
155 | 0 | if (regi < 10) append(ds, " "); |
156 | 0 | appendf(ds, "r%d(%d)", orig, regi); |
157 | 0 | } |
158 | 0 | } |
159 | 0 | else { |
160 | 0 | for (i = 0; i < cur_ins->info->num_operands; i++) { |
161 | 0 | if (i) |
162 | 0 | append(ds, ", "); |
163 | 0 | switch (cur_ins->info->operands[i] & MVM_operand_rw_mask) { |
164 | 0 | case MVM_operand_read_reg: |
165 | 0 | case MVM_operand_write_reg: { |
166 | 0 | MVMint16 orig = cur_ins->operands[i].reg.orig; |
167 | 0 | MVMint16 regi = cur_ins->operands[i].reg.i; |
168 | 0 | if (orig < 10) append(ds, " "); |
169 | 0 | if (regi < 10) append(ds, " "); |
170 | 0 | appendf(ds, "r%d(%d)", orig, regi); |
171 | 0 | break; |
172 | 0 | } |
173 | 0 | case MVM_operand_read_lex: |
174 | 0 | case MVM_operand_write_lex: { |
175 | 0 | MVMStaticFrameBody *cursor = &g->sf->body; |
176 | 0 | MVMuint32 ascension; |
177 | 0 | appendf(ds, "lex(idx=%d,outers=%d", cur_ins->operands[i].lex.idx, |
178 | 0 | cur_ins->operands[i].lex.outers); |
179 | 0 | for (ascension = 0; |
180 | 0 | ascension < cur_ins->operands[i].lex.outers; |
181 | 0 | ascension++, cursor = &cursor->outer->body) { }; |
182 | 0 | if (cursor->fully_deserialized) { |
183 | 0 | if (cur_ins->operands[i].lex.idx < cursor->num_lexicals) { |
184 | 0 | char *cstr = MVM_string_utf8_encode_C_string(tc, cursor->lexical_names_list[cur_ins->operands[i].lex.idx]->key); |
185 | 0 | appendf(ds, ",%s)", cstr); |
186 | 0 | MVM_free(cstr); |
187 | 0 | } else { |
188 | 0 | append(ds, ",<out of bounds>)"); |
189 | 0 | } |
190 | 0 | } else { |
191 | 0 | append(ds, ",<pending deserialization>)"); |
192 | 0 | } |
193 | 0 | break; |
194 | 0 | } |
195 | 0 | case MVM_operand_literal: { |
196 | 0 | MVMuint32 type = cur_ins->info->operands[i] & MVM_operand_type_mask; |
197 | 0 | switch (type) { |
198 | 0 | case MVM_operand_ins: { |
199 | 0 | MVMint32 bb_idx = cur_ins->operands[i].ins_bb->idx; |
200 | 0 | if (bb_idx < 100) append(ds, " "); |
201 | 0 | if (bb_idx < 10) append(ds, " "); |
202 | 0 | appendf(ds, "BB(%d)", bb_idx); |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | case MVM_operand_int8: |
206 | 0 | appendf(ds, "liti8(%"PRId8")", cur_ins->operands[i].lit_i8); |
207 | 0 | break; |
208 | 0 | case MVM_operand_int16: |
209 | 0 | appendf(ds, "liti16(%"PRId16")", cur_ins->operands[i].lit_i16); |
210 | 0 | break; |
211 | 0 | case MVM_operand_int32: |
212 | 0 | appendf(ds, "liti32(%"PRId32")", cur_ins->operands[i].lit_i32); |
213 | 0 | break; |
214 | 0 | case MVM_operand_int64: |
215 | 0 | appendf(ds, "liti64(%"PRId64")", cur_ins->operands[i].lit_i64); |
216 | 0 | break; |
217 | 0 | case MVM_operand_num32: |
218 | 0 | appendf(ds, "litn32(%f)", cur_ins->operands[i].lit_n32); |
219 | 0 | break; |
220 | 0 | case MVM_operand_num64: |
221 | 0 | appendf(ds, "litn64(%g)", cur_ins->operands[i].lit_n64); |
222 | 0 | break; |
223 | 0 | case MVM_operand_str: { |
224 | 0 | char *cstr = MVM_string_utf8_encode_C_string(tc, |
225 | 0 | MVM_cu_string(tc, g->sf->body.cu, cur_ins->operands[i].lit_str_idx)); |
226 | 0 | appendf(ds, "lits(%s)", cstr); |
227 | 0 | MVM_free(cstr); |
228 | 0 | break; |
229 | 0 | } |
230 | 0 | case MVM_operand_callsite: { |
231 | 0 | MVMCallsite *callsite = g->sf->body.cu->body.callsites[cur_ins->operands[i].callsite_idx]; |
232 | 0 | appendf(ds, "callsite(%p, %d arg, %d pos, %s, %s)", |
233 | 0 | callsite, |
234 | 0 | callsite->arg_count, callsite->num_pos, |
235 | 0 | callsite->has_flattening ? "flattening" : "nonflattening", |
236 | 0 | callsite->is_interned ? "interned" : "noninterned"); |
237 | 0 | break; |
238 | 0 |
|
239 | 0 | } |
240 | 0 | case MVM_operand_spesh_slot: |
241 | 0 | appendf(ds, "sslot(%"PRId16")", cur_ins->operands[i].lit_i16); |
242 | 0 | break; |
243 | 0 | case MVM_operand_coderef: { |
244 | 0 | MVMCodeBody *body = &((MVMCode*)g->sf->body.cu->body.coderefs[cur_ins->operands[i].coderef_idx])->body; |
245 | 0 | MVMBytecodeAnnotation *anno = MVM_bytecode_resolve_annotation(tc, &body->sf->body, 0); |
246 | 0 |
|
247 | 0 | append(ds, "coderef("); |
248 | 0 |
|
249 | 0 | if (anno) { |
250 | 0 | char *filestr = MVM_string_utf8_encode_C_string(tc, |
251 | 0 | MVM_cu_string(tc, g->sf->body.cu, anno->filename_string_heap_index)); |
252 | 0 | appendf(ds, "%s:%d%s)", filestr, anno->line_number, body->outer ? " (closure)" : ""); |
253 | 0 | MVM_free(filestr); |
254 | 0 | } else { |
255 | 0 | append(ds, "??\?)"); |
256 | 0 | } |
257 | 0 |
|
258 | 0 | MVM_free(anno); |
259 | 0 | break; |
260 | 0 | } |
261 | 0 | default: |
262 | 0 | append(ds, "<nyi(lit)>"); |
263 | 0 | } |
264 | 0 | break; |
265 | 0 | } |
266 | 0 | default: |
267 | 0 | append(ds, "<nyi>"); |
268 | 0 | } |
269 | 0 | } |
270 | 0 | if (cur_ins->info->opcode == MVM_OP_wval || cur_ins->info->opcode == MVM_OP_wval_wide) { |
271 | 0 | /* We can try to find out what the debug_name of this thing is. */ |
272 | 0 | MVMint16 dep = cur_ins->operands[1].lit_i16; |
273 | 0 | MVMint64 idx; |
274 | 0 | MVMCollectable *result = NULL; |
275 | 0 | MVMSerializationContext *sc; |
276 | 0 | char *debug_name = NULL; |
277 | 0 | const char *repr_name = NULL; |
278 | 0 | if (cur_ins->info->opcode == MVM_OP_wval) { |
279 | 0 | idx = cur_ins->operands[2].lit_i16; |
280 | 0 | } else { |
281 | 0 | idx = cur_ins->operands[2].lit_i64; |
282 | 0 | } |
283 | 0 | sc = MVM_sc_get_sc(tc, g->sf->body.cu, dep); |
284 | 0 | if (sc) |
285 | 0 | result = (MVMCollectable *)MVM_sc_try_get_object(tc, sc, idx); |
286 | 0 | if (result) { |
287 | 0 | if (result->flags & MVM_CF_STABLE) { |
288 | 0 | debug_name = ((MVMSTable *)result)->debug_name; |
289 | 0 | repr_name = ((MVMSTable *)result)->REPR->name; |
290 | 0 | } else { |
291 | 0 | debug_name = STABLE(result)->debug_name; |
292 | 0 | repr_name = REPR(result)->name; |
293 | 0 | } |
294 | 0 | if (debug_name) { |
295 | 0 | appendf(ds, " (%s: %s)", repr_name, debug_name); |
296 | 0 | } else { |
297 | 0 | appendf(ds, " (%s: ?)", repr_name); |
298 | 0 | } |
299 | 0 | } else { |
300 | 0 | appendf(ds, " (not deserialized)"); |
301 | 0 | } |
302 | 0 | } |
303 | 0 | } |
304 | 0 | append(ds, "\n"); |
305 | 0 | cur_ins = cur_ins->next; |
306 | 0 | } |
307 | 0 |
|
308 | 0 | /* Predecessors and successors. */ |
309 | 0 | append(ds, " Successors: "); |
310 | 0 | for (i = 0; i < bb->num_succ; i++) |
311 | 0 | appendf(ds, (i == 0 ? "%d" : ", %d"), bb->succ[i]->idx); |
312 | 0 | append(ds, "\n Predeccessors: "); |
313 | 0 | for (i = 0; i < bb->num_pred; i++) |
314 | 0 | appendf(ds, (i == 0 ? "%d" : ", %d"), bb->pred[i]->idx); |
315 | 0 | append(ds, "\n Dominance children: "); |
316 | 0 | for (i = 0; i < bb->num_children; i++) |
317 | 0 | appendf(ds, (i == 0 ? "%d" : ", %d"), bb->children[i]->idx); |
318 | 0 | append(ds, "\n\n"); |
319 | 0 | } |
320 | | |
321 | | /* Dumps the facts table. */ |
322 | 0 | static void dump_facts(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g) { |
323 | 0 | MVMuint16 i, j, num_locals, num_facts; |
324 | 0 | num_locals = g->num_locals; |
325 | 0 | for (i = 0; i < num_locals; i++) { |
326 | 0 | num_facts = g->fact_counts[i]; |
327 | 0 | for (j = 0; j < num_facts; j++) { |
328 | 0 | MVMint32 usages = g->facts[i][j].usages; |
329 | 0 | MVMint32 flags = g->facts[i][j].flags; |
330 | 0 | if (i < 10) append(ds, " "); |
331 | 0 | if (j < 10) append(ds, " "); |
332 | 0 | appendf(ds, " r%d(%d): usages=%d, flags=%-5d", i, j, usages, flags); |
333 | 0 | if (flags & 1) { |
334 | 0 | append(ds, " KnTyp"); |
335 | 0 | } |
336 | 0 | if (flags & 2) { |
337 | 0 | append(ds, " KnVal"); |
338 | 0 | } |
339 | 0 | if (flags & 4) { |
340 | 0 | append(ds, " Dcntd"); |
341 | 0 | } |
342 | 0 | if (flags & 8) { |
343 | 0 | append(ds, " Concr"); |
344 | 0 | } |
345 | 0 | if (flags & 16) { |
346 | 0 | append(ds, " TyObj"); |
347 | 0 | } |
348 | 0 | if (flags & 32) { |
349 | 0 | append(ds, " KnDcT"); |
350 | 0 | } |
351 | 0 | if (flags & 64) { |
352 | 0 | append(ds, " DCncr"); |
353 | 0 | } |
354 | 0 | if (flags & 128) { |
355 | 0 | append(ds, " DcTyO"); |
356 | 0 | } |
357 | 0 | if (flags & 256) { |
358 | 0 | append(ds, " LogGd"); |
359 | 0 | } |
360 | 0 | if (flags & 512) { |
361 | 0 | append(ds, " HashI"); |
362 | 0 | } |
363 | 0 | if (flags & 1024) { |
364 | 0 | append(ds, " ArrIt"); |
365 | 0 | } |
366 | 0 | if (flags & 2048) { |
367 | 0 | append(ds, " KBxSr"); |
368 | 0 | } |
369 | 0 | if (flags & 4096) { |
370 | 0 | append(ds, " MgWLG"); |
371 | 0 | } |
372 | 0 | if (flags & 8192) { |
373 | 0 | append(ds, " KRWCn"); |
374 | 0 | } |
375 | 0 | if (g->facts[i][j].writer && g->facts[i][j].writer->info->opcode == MVM_SSA_PHI) { |
376 | 0 | appendf(ds, " (merged from %d regs)", g->facts[i][j].writer->info->num_operands - 1); |
377 | 0 | } |
378 | 0 | append(ds, "\n"); |
379 | 0 | } |
380 | 0 | } |
381 | 0 | } |
382 | | |
383 | | /* Dumps a table of all logged values */ |
384 | 0 | static void dump_log_values(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g) { |
385 | 0 | MVMint16 log_index; |
386 | 0 | MVMint16 seen_table_size = g->num_log_slots * MVM_SPESH_LOG_RUNS; |
387 | 0 | size_t ds_pos_before = tell_ds(ds); |
388 | 0 | MVMint16 interesting = 0; |
389 | 0 |
|
390 | 0 | MVMCollectable **seen_table = alloca(sizeof(MVMCollectable *) *seen_table_size); |
391 | 0 | memset(seen_table, 0, sizeof(MVMCollectable *) * seen_table_size); |
392 | 0 |
|
393 | 0 | append(ds, "Logged values:\n"); |
394 | 0 |
|
395 | 0 | for (log_index = 0; log_index < g->num_log_slots; log_index++) { |
396 | 0 | MVMint16 run_index; |
397 | 0 |
|
398 | 0 | appendf(ds, " % 3d ", log_index); |
399 | 0 |
|
400 | 0 | for (run_index = 0; run_index < MVM_SPESH_LOG_RUNS; run_index++) { |
401 | 0 | MVMuint16 log_slot = log_index * MVM_SPESH_LOG_RUNS + run_index; |
402 | 0 | MVMCollectable *log_obj = g->log_slots[log_slot]; |
403 | 0 | MVMint16 log_obj_idx; |
404 | 0 |
|
405 | 0 | if (log_obj) { |
406 | 0 | for (log_obj_idx = 0; log_obj_idx < seen_table_size; log_obj_idx++) { |
407 | 0 | if (seen_table[log_obj_idx] == log_obj) { |
408 | 0 | break; |
409 | 0 | } else if (seen_table[log_obj_idx] == 0) { |
410 | 0 | seen_table[log_obj_idx] = log_obj; |
411 | 0 | break; |
412 | 0 | } |
413 | 0 | } |
414 | 0 |
|
415 | 0 | appendf(ds, "% 4d ", log_obj_idx + 1); |
416 | 0 | interesting = 1; |
417 | 0 | } else { |
418 | 0 | appendf(ds, "%4s ", "_"); |
419 | 0 | } |
420 | 0 |
|
421 | 0 | } |
422 | 0 |
|
423 | 0 | append(ds, "\n"); |
424 | 0 | } |
425 | 0 | append(ds, "\n"); |
426 | 0 |
|
427 | 0 | for (log_index = 0; log_index < seen_table_size && seen_table[log_index]; log_index++) { |
428 | 0 | appendf(ds, " %d: %p", log_index + 1, seen_table[log_index]); |
429 | 0 | if (STABLE(seen_table[log_index])->REPR->ID == MVM_REPR_ID_P6int) { |
430 | 0 | if (IS_CONCRETE(seen_table[log_index])) |
431 | 0 | appendf(ds, " P6int(%"PRId64")", MVM_repr_get_int(tc, (MVMObject*)seen_table[log_index])); |
432 | 0 | else |
433 | 0 | append(ds, " P6int(type object)"); |
434 | 0 | } else { |
435 | 0 | append(ds, " "); |
436 | 0 | append(ds, (char *)STABLE(seen_table[log_index])->REPR->name); |
437 | 0 | if (!IS_CONCRETE(seen_table[log_index])) |
438 | 0 | append(ds, "(type object)"); |
439 | 0 | if (STABLE(seen_table[log_index])->debug_name) { |
440 | 0 | appendf(ds, " debugname: %s", STABLE(seen_table[log_index])->debug_name); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | append(ds, "\n"); |
444 | 0 | } |
445 | 0 |
|
446 | 0 | append(ds, "\n"); |
447 | 0 |
|
448 | 0 | if (!interesting) { |
449 | 0 | rewind_ds(ds, ds_pos_before); |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | 0 | static void dump_callsite(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g) { |
454 | 0 | MVMuint16 i; |
455 | 0 | appendf(ds, "Callsite %p (%d args, %d pos)\n", g->cs, g->cs->arg_count, g->cs->num_pos); |
456 | 0 | for (i = 0; i < (g->cs->arg_count - g->cs->num_pos) / 2; i++) { |
457 | 0 | if (g->cs->arg_names[i]) { |
458 | 0 | char * argname_utf8 = MVM_string_utf8_encode_C_string(tc, g->cs->arg_names[i]); |
459 | 0 | appendf(ds, " - %s\n", argname_utf8); |
460 | 0 | MVM_free(argname_utf8); |
461 | 0 | } |
462 | 0 | } |
463 | 0 | if (g->cs->num_pos) |
464 | 0 | append(ds, "Positional flags: "); |
465 | 0 | for (i = 0; i < g->cs->num_pos; i++) { |
466 | 0 | MVMCallsiteEntry arg_flag = g->cs->arg_flags[i]; |
467 | 0 |
|
468 | 0 | if (i) |
469 | 0 | append(ds, ", "); |
470 | 0 |
|
471 | 0 | if (arg_flag == MVM_CALLSITE_ARG_OBJ) { |
472 | 0 | append(ds, "obj"); |
473 | 0 | } else if (arg_flag == MVM_CALLSITE_ARG_INT) { |
474 | 0 | append(ds, "int"); |
475 | 0 | } else if (arg_flag == MVM_CALLSITE_ARG_NUM) { |
476 | 0 | append(ds, "num"); |
477 | 0 | } else if (arg_flag == MVM_CALLSITE_ARG_STR) { |
478 | 0 | append(ds, "str"); |
479 | 0 | } |
480 | 0 | } |
481 | 0 | if (g->cs->num_pos) |
482 | 0 | append(ds, "\n"); |
483 | 0 | append(ds, "\n"); |
484 | 0 | } |
485 | | |
486 | 0 | static void dump_arg_guards(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g) { |
487 | 0 | MVMuint16 i; |
488 | 0 | appendf(ds, "%d argument guards\n", g->num_arg_guards); |
489 | 0 |
|
490 | 0 | for (i = 0; i < g->num_arg_guards; i++) { |
491 | 0 | MVMSpeshGuard *guard = &g->arg_guards[i]; |
492 | 0 | switch (guard->kind) { |
493 | 0 | case MVM_SPESH_GUARD_CONC: |
494 | 0 | appendf(ds, " concrete(%d)\n", guard->slot); |
495 | 0 | break; |
496 | 0 | case MVM_SPESH_GUARD_TYPE: |
497 | 0 | appendf(ds, " type(%d, %p)", guard->slot, guard->match); |
498 | 0 | if (((MVMSTable*)(guard->match))->debug_name) { |
499 | 0 | appendf(ds, " debugname: %s", ((MVMSTable*)(guard->match))->debug_name); |
500 | 0 | } |
501 | 0 | append(ds, "\n"); |
502 | 0 | break; |
503 | 0 | case MVM_SPESH_GUARD_DC_CONC: |
504 | 0 | appendf(ds, " deconted_concrete(%d)\n", guard->slot); |
505 | 0 | break; |
506 | 0 | case MVM_SPESH_GUARD_DC_TYPE: |
507 | 0 | appendf(ds, " deconted_type(%d, %p)", guard->slot, guard->match); |
508 | 0 | if (((MVMSTable*)(guard->match))->debug_name) { |
509 | 0 | appendf(ds, " debugname: %s", ((MVMSTable*)(guard->match))->debug_name); |
510 | 0 | } |
511 | 0 | append(ds, "\n"); |
512 | 0 | break; |
513 | 0 | case MVM_SPESH_GUARD_DC_CONC_RW: |
514 | 0 | appendf(ds, " deconted_concrete_rw(%d)\n", guard->slot); |
515 | 0 | break; |
516 | 0 | case MVM_SPESH_GUARD_DC_TYPE_RW: |
517 | 0 | appendf(ds, " deconted_type_rw(%d, %p)", guard->slot, guard->match); |
518 | 0 | if (((MVMSTable*)(guard->match))->debug_name) { |
519 | 0 | appendf(ds, " debugname: %s", ((MVMSTable*)(guard->match))->debug_name); |
520 | 0 | } |
521 | 0 | append(ds, "\n"); |
522 | 0 | break; |
523 | 0 | } |
524 | 0 | } |
525 | 0 | append(ds, "\n"); |
526 | 0 | } |
527 | | |
528 | 0 | static void dump_fileinfo(MVMThreadContext *tc, DumpStr *ds, MVMSpeshGraph *g) { |
529 | 0 | MVMBytecodeAnnotation *ann = MVM_bytecode_resolve_annotation(tc, &g->sf->body, 0); |
530 | 0 | MVMCompUnit *cu = g->sf->body.cu; |
531 | 0 | MVMint32 str_idx = ann ? ann->filename_string_heap_index : 0; |
532 | 0 | MVMint32 line_nr = ann ? ann->line_number : 1; |
533 | 0 | MVMString *filename = cu->body.filename; |
534 | 0 | char *filename_utf8 = "<unknown>"; |
535 | 0 | if (ann && str_idx < cu->body.num_strings) { |
536 | 0 | filename = MVM_cu_string(tc, cu, str_idx); |
537 | 0 | } |
538 | 0 | if (filename) |
539 | 0 | filename_utf8 = MVM_string_utf8_encode_C_string(tc, filename); |
540 | 0 | appendf(ds, "%s:%d", filename_utf8, line_nr); |
541 | 0 | if (filename) |
542 | 0 | MVM_free(filename_utf8); |
543 | 0 | MVM_free(ann); |
544 | 0 | } |
545 | | |
546 | | /* Dump a spesh graph into string form, for debugging purposes. */ |
547 | 0 | char * MVM_spesh_dump(MVMThreadContext *tc, MVMSpeshGraph *g) { |
548 | 0 | MVMSpeshBB *cur_bb; |
549 | 0 |
|
550 | 0 | /* Allocate buffer. */ |
551 | 0 | DumpStr ds; |
552 | 0 | ds.alloc = 8192; |
553 | 0 | ds.buffer = MVM_malloc(ds.alloc); |
554 | 0 | ds.pos = 0; |
555 | 0 |
|
556 | 0 | /* Dump name and CUID. */ |
557 | 0 | append(&ds, "Spesh of '"); |
558 | 0 | append_str(tc, &ds, g->sf->body.name); |
559 | 0 | append(&ds, "' (cuid: "); |
560 | 0 | append_str(tc, &ds, g->sf->body.cuuid); |
561 | 0 | append(&ds, ", file: "); |
562 | 0 | dump_fileinfo(tc, &ds, g); |
563 | 0 | append(&ds, ")\n"); |
564 | 0 | if (g->cs) |
565 | 0 | dump_callsite(tc, &ds, g); |
566 | 0 | if (g->num_arg_guards) |
567 | 0 | dump_arg_guards(tc, &ds, g); |
568 | 0 | if (!g->cs && !g->num_arg_guards) |
569 | 0 | append(&ds, "\n"); |
570 | 0 |
|
571 | 0 | /* Go over all the basic blocks and dump them. */ |
572 | 0 | cur_bb = g->entry; |
573 | 0 | while (cur_bb) { |
574 | 0 | dump_bb(tc, &ds, g, cur_bb); |
575 | 0 | cur_bb = cur_bb->linear_next; |
576 | 0 | } |
577 | 0 |
|
578 | 0 | /* Dump facts. */ |
579 | 0 | append(&ds, "\nFacts:\n"); |
580 | 0 | dump_facts(tc, &ds, g); |
581 | 0 |
|
582 | 0 | if (g->num_spesh_slots || g->num_log_slots) { |
583 | 0 | append(&ds, "\nStats:\n"); |
584 | 0 | appendf(&ds, " %d spesh slots\n", g->num_spesh_slots); |
585 | 0 | appendf(&ds, " %d log slots\n", g->num_log_slots); |
586 | 0 | } |
587 | 0 |
|
588 | 0 | if (g->num_log_slots) { |
589 | 0 | dump_log_values(tc, &ds, g); |
590 | 0 | } |
591 | 0 |
|
592 | 0 | append(&ds, "\n"); |
593 | 0 | append_null(&ds); |
594 | 0 | return ds.buffer; |
595 | 0 | } |