1 /*
2 * IR - Lightweight JIT Compilation Framework
3 * (debug dumps)
4 * Copyright (C) 2022 Zend by Perforce.
5 * Authors: Dmitry Stogov <dmitry@php.net>
6 */
7
8 #include "ir.h"
9 #include "ir_private.h"
10
ir_dump(const ir_ctx * ctx,FILE * f)11 void ir_dump(const ir_ctx *ctx, FILE *f)
12 {
13 ir_ref i, j, n, ref, *p;
14 ir_insn *insn;
15 uint32_t flags;
16
17 for (i = 1 - ctx->consts_count, insn = ctx->ir_base + i; i < IR_UNUSED; i++, insn++) {
18 fprintf(f, "%05d %s %s(", i, ir_op_name[insn->op], ir_type_name[insn->type]);
19 ir_print_const(ctx, insn, f, true);
20 fprintf(f, ")\n");
21 }
22
23 for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count; i++, insn++) {
24 flags = ir_op_flags[insn->op];
25 fprintf(f, "%05d %s", i, ir_op_name[insn->op]);
26 if ((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) {
27 fprintf(f, " %s", ir_type_name[insn->type]);
28 }
29 n = ir_operands_count(ctx, insn);
30 for (j = 1, p = insn->ops + 1; j <= 3; j++, p++) {
31 ref = *p;
32 if (ref) {
33 fprintf(f, " %05d", ref);
34 }
35 }
36 if (n > 3) {
37 n -= 3;
38 do {
39 i++;
40 insn++;
41 fprintf(f, "\n%05d", i);
42 for (j = 0; j < 4; j++, p++) {
43 ref = *p;
44 if (ref) {
45 fprintf(f, " %05d", ref);
46 }
47 }
48 n -= 4;
49 } while (n > 0);
50 }
51 fprintf(f, "\n");
52 }
53 }
54
ir_dump_dot(const ir_ctx * ctx,const char * name,FILE * f)55 void ir_dump_dot(const ir_ctx *ctx, const char *name, FILE *f)
56 {
57 int DATA_WEIGHT = 0;
58 int CONTROL_WEIGHT = 5;
59 int REF_WEIGHT = 4;
60 ir_ref i, j, n, ref, *p;
61 ir_insn *insn;
62 uint32_t flags;
63
64 fprintf(f, "digraph %s {\n", name);
65 fprintf(f, "\trankdir=TB;\n");
66 for (i = 1 - ctx->consts_count, insn = ctx->ir_base + i; i < IR_UNUSED; i++, insn++) {
67 fprintf(f, "\tc%d [label=\"C%d: CONST %s(", -i, -i, ir_type_name[insn->type]);
68 /* FIXME(tony): We still cannot handle strings with escaped double quote inside */
69 ir_print_const(ctx, insn, f, false);
70 fprintf(f, ")\",style=filled,fillcolor=yellow];\n");
71 }
72
73 for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
74 flags = ir_op_flags[insn->op];
75 if (flags & IR_OP_FLAG_CONTROL) {
76 if (insn->op == IR_START) {
77 fprintf(f, "\t{rank=min; n%d [label=\"%d: %s\",shape=box,style=\"rounded,filled\",fillcolor=red];}\n", i, i, ir_op_name[insn->op]);
78 } else if (insn->op == IR_ENTRY) {
79 fprintf(f, "\t{n%d [label=\"%d: %s\",shape=box,style=\"rounded,filled\",fillcolor=red];}\n", i, i, ir_op_name[insn->op]);
80 } else if (flags & IR_OP_FLAG_TERMINATOR) {
81 fprintf(f, "\t{rank=max; n%d [label=\"%d: %s\",shape=box,style=\"rounded,filled\",fillcolor=red];}\n", i, i, ir_op_name[insn->op]);
82 } else if (flags & IR_OP_FLAG_MEM) {
83 fprintf(f, "\tn%d [label=\"%d: %s\",shape=box,style=filled,fillcolor=pink];\n", i, i, ir_op_name[insn->op]);
84 } else {
85 fprintf(f, "\tn%d [label=\"%d: %s\",shape=box,style=filled,fillcolor=lightcoral];\n", i, i, ir_op_name[insn->op]);
86 }
87 } else if (flags & IR_OP_FLAG_DATA) {
88 if (IR_OPND_KIND(flags, 1) == IR_OPND_DATA) {
89 /* not a leaf */
90 fprintf(f, "\tn%d [label=\"%d: %s\"", i, i, ir_op_name[insn->op]);
91 fprintf(f, ",shape=diamond,style=filled,fillcolor=deepskyblue];\n");
92 } else {
93 if (insn->op == IR_PARAM) {
94 fprintf(f, "\tn%d [label=\"%d: %s %s \\\"%s\\\"\",style=filled,fillcolor=lightblue];\n",
95 i, i, ir_op_name[insn->op], ir_type_name[insn->type], ir_get_str(ctx, insn->op2));
96 } else if (insn->op == IR_VAR) {
97 fprintf(f, "\tn%d [label=\"%d: %s %s \\\"%s\\\"\"];\n", i, i, ir_op_name[insn->op], ir_type_name[insn->type], ir_get_str(ctx, insn->op2));
98 } else {
99 fprintf(f, "\tn%d [label=\"%d: %s %s\",style=filled,fillcolor=deepskyblue];\n", i, i, ir_op_name[insn->op], ir_type_name[insn->type]);
100 }
101 }
102 }
103 n = ir_operands_count(ctx, insn);
104 for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
105 ref = *p;
106 if (ref) {
107 switch (IR_OPND_KIND(flags, j)) {
108 case IR_OPND_DATA:
109 if (IR_IS_CONST_REF(ref)) {
110 fprintf(f, "\tc%d -> n%d [color=blue,weight=%d];\n", -ref, i, DATA_WEIGHT);
111 } else if (insn->op == IR_PHI
112 && ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN
113 && ctx->ir_base[ir_insn_op(&ctx->ir_base[insn->op1], j - 1)].op == IR_LOOP_END) {
114 fprintf(f, "\tn%d -> n%d [color=blue,dir=back];\n", i, ref);
115 } else {
116 fprintf(f, "\tn%d -> n%d [color=blue,weight=%d];\n", ref, i, DATA_WEIGHT);
117 }
118 break;
119 case IR_OPND_CONTROL:
120 if (insn->op == IR_LOOP_BEGIN && ctx->ir_base[ref].op == IR_LOOP_END) {
121 fprintf(f, "\tn%d -> n%d [style=bold,color=red,dir=back];\n", i, ref);
122 } else if (insn->op == IR_ENTRY) {
123 fprintf(f, "\tn%d -> n%d [style=bold,color=red,style=dashed,weight=%d];\n", ref, i, CONTROL_WEIGHT);
124 } else {
125 fprintf(f, "\tn%d -> n%d [style=bold,color=red,weight=%d];\n", ref, i, CONTROL_WEIGHT);
126 }
127 break;
128 case IR_OPND_CONTROL_DEP:
129 case IR_OPND_CONTROL_REF:
130 fprintf(f, "\tn%d -> n%d [style=dashed,dir=back,weight=%d];\n", ref, i, REF_WEIGHT);
131 break;
132 }
133 }
134 }
135 n = ir_insn_inputs_to_len(n);
136 i += n;
137 insn += n;
138 }
139 fprintf(f, "}\n");
140 }
141
ir_dump_use_lists(const ir_ctx * ctx,FILE * f)142 void ir_dump_use_lists(const ir_ctx *ctx, FILE *f)
143 {
144 ir_ref i, j, n, *p;
145 ir_use_list *list;
146
147 if (ctx->use_lists) {
148 fprintf(f, "{ # Use Lists\n");
149 for (i = 1, list = &ctx->use_lists[1]; i < ctx->insns_count; i++, list++) {
150 n = list->count;
151 if (n > 0) {
152 p = &ctx->use_edges[list->refs];
153 fprintf(f, "%05d(%d): [%05d", i, n, *p);
154 p++;
155 for (j = 1; j < n; j++, p++) {
156 fprintf(f, ", %05d", *p);
157 }
158 fprintf(f, "]\n");
159 }
160 }
161 fprintf(f, "}\n");
162 }
163 }
164
ir_dump_dessa_moves(const ir_ctx * ctx,int b,ir_block * bb,FILE * f)165 static void ir_dump_dessa_moves(const ir_ctx *ctx, int b, ir_block *bb, FILE *f)
166 {
167 uint32_t succ;
168 ir_block *succ_bb;
169 ir_use_list *use_list;
170 ir_ref k, i, *p, use_ref, input;
171 ir_insn *use_insn;
172
173 IR_ASSERT(bb->successors_count == 1);
174 succ = ctx->cfg_edges[bb->successors];
175 succ_bb = &ctx->cfg_blocks[succ];
176 IR_ASSERT(succ_bb->predecessors_count > 1);
177 use_list = &ctx->use_lists[succ_bb->start];
178 k = ir_phi_input_number(ctx, succ_bb, b);
179
180 for (i = 0, p = &ctx->use_edges[use_list->refs]; i < use_list->count; i++, p++) {
181 use_ref = *p;
182 use_insn = &ctx->ir_base[use_ref];
183 if (use_insn->op == IR_PHI) {
184 input = ir_insn_op(use_insn, k);
185 if (IR_IS_CONST_REF(input)) {
186 fprintf(f, "\t# DESSA MOV c_%d", -input);
187 } else if (ctx->vregs[input] != ctx->vregs[use_ref]) {
188 fprintf(f, "\t# DESSA MOV d_%d {R%d}", input, ctx->vregs[input]);
189 } else {
190 continue;
191 }
192 if (ctx->regs) {
193 int8_t *regs = ctx->regs[use_ref];
194 int8_t reg = regs[k];
195 if (reg != IR_REG_NONE) {
196 fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[input].type),
197 (reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
198 }
199 }
200 fprintf(f, " -> d_%d {R%d}", use_ref, ctx->vregs[use_ref]);
201 if (ctx->regs) {
202 int8_t reg = ctx->regs[use_ref][0];
203 if (reg != IR_REG_NONE) {
204 fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[use_ref].type),
205 (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
206 }
207 }
208 fprintf(f, "\n");
209 }
210 }
211 }
212
ir_dump_cfg(ir_ctx * ctx,FILE * f)213 void ir_dump_cfg(ir_ctx *ctx, FILE *f)
214 {
215 if (ctx->cfg_blocks) {
216 uint32_t b, i, bb_count = ctx->cfg_blocks_count;
217 ir_block *bb = ctx->cfg_blocks + 1;
218
219 fprintf(f, "{ # CFG\n");
220 for (b = 1; b <= bb_count; b++, bb++) {
221 fprintf(f, "BB%d:\n", b);
222 fprintf(f, "\tstart=%d\n", bb->start);
223 fprintf(f, "\tend=%d\n", bb->end);
224 if (bb->successors_count) {
225 fprintf(f, "\tsuccessors(%d) [BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
226 for (i = 1; i < bb->successors_count; i++) {
227 fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
228 }
229 fprintf(f, "]\n");
230 }
231 if (bb->predecessors_count) {
232 fprintf(f, "\tpredecessors(%d) [BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
233 for (i = 1; i < bb->predecessors_count; i++) {
234 fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
235 }
236 fprintf(f, "]\n");
237 }
238 if (bb->dom_parent > 0) {
239 fprintf(f, "\tdom_parent=BB%d\n", bb->dom_parent);
240 }
241 fprintf(f, "\tdom_depth=%d\n", bb->dom_depth);
242 if (bb->dom_child > 0) {
243 int child = bb->dom_child;
244 fprintf(f, "\tdom_children [BB%d", child);
245 child = ctx->cfg_blocks[child].dom_next_child;
246 while (child > 0) {
247 fprintf(f, ", BB%d", child);
248 child = ctx->cfg_blocks[child].dom_next_child;
249 }
250 fprintf(f, "]\n");
251 }
252 if (bb->flags & IR_BB_ENTRY) {
253 fprintf(f, "\tENTRY\n");
254 }
255 if (bb->flags & IR_BB_UNREACHABLE) {
256 fprintf(f, "\tUNREACHABLE\n");
257 }
258 if (bb->flags & IR_BB_LOOP_HEADER) {
259 if (bb->flags & IR_BB_LOOP_WITH_ENTRY) {
260 fprintf(f, "\tLOOP_HEADER, LOOP_WITH_ENTRY\n");
261 } else {
262 fprintf(f, "\tLOOP_HEADER\n");
263 }
264 }
265 if (bb->flags & IR_BB_IRREDUCIBLE_LOOP) {
266 fprintf(stderr, "\tIRREDUCIBLE_LOOP\n");
267 }
268 if (bb->loop_header > 0) {
269 fprintf(f, "\tloop_header=BB%d\n", bb->loop_header);
270 }
271 if (bb->loop_depth != 0) {
272 fprintf(f, "\tloop_depth=%d\n", bb->loop_depth);
273 }
274 if (bb->flags & IR_BB_OSR_ENTRY_LOADS) {
275 ir_list *list = (ir_list*)ctx->osr_entry_loads;
276 uint32_t pos = 0, i, count;
277
278 IR_ASSERT(list);
279 while (1) {
280 i = ir_list_at(list, pos);
281 if (b == i) {
282 break;
283 }
284 IR_ASSERT(i != 0); /* end marker */
285 pos++;
286 count = ir_list_at(list, pos);
287 pos += count + 1;
288 }
289 pos++;
290 count = ir_list_at(list, pos);
291 pos++;
292
293 for (i = 0; i < count; i++, pos++) {
294 ir_ref ref = ir_list_at(list, pos);
295 fprintf(f, "\tOSR_ENTRY_LOAD=d_%d\n", ref);
296 }
297 }
298 if (bb->flags & IR_BB_DESSA_MOVES) {
299 ir_dump_dessa_moves(ctx, b, bb, f);
300 }
301 }
302 fprintf(f, "}\n");
303 }
304 }
305
ir_dump_cfg_map(const ir_ctx * ctx,FILE * f)306 void ir_dump_cfg_map(const ir_ctx *ctx, FILE *f)
307 {
308 ir_ref i;
309 uint32_t *_blocks = ctx->cfg_map;
310
311 if (_blocks) {
312 fprintf(f, "{ # CFG map (insn -> bb)\n");
313 for (i = IR_UNUSED + 1; i < ctx->insns_count; i++) {
314 fprintf(f, "%d -> %d\n", i, _blocks[i]);
315 }
316 fprintf(f, "}\n");
317 }
318 }
319
ir_dump_live_ranges(const ir_ctx * ctx,FILE * f)320 void ir_dump_live_ranges(const ir_ctx *ctx, FILE *f)
321 {
322 ir_ref i, j, n;
323
324 if (!ctx->live_intervals) {
325 return;
326 }
327 fprintf(f, "{ # LIVE-RANGES (vregs_count=%d)\n", ctx->vregs_count);
328 for (i = 0; i <= ctx->vregs_count; i++) {
329 ir_live_interval *ival = ctx->live_intervals[i];
330
331 if (ival) {
332 ir_live_range *p;
333 ir_use_pos *use_pos;
334
335 if (i == 0) {
336 fprintf(f, "TMP");
337 } else {
338 for (j = 1; j < ctx->insns_count; j++) {
339 if (ctx->vregs[j] == (uint32_t)i) {
340 break;
341 }
342 }
343 fprintf(f, "R%d (d_%d", i, j);
344 for (j++; j < ctx->insns_count; j++) {
345 if (ctx->vregs[j] == (uint32_t)i) {
346 fprintf(f, ", d_%d", j);
347 }
348 }
349 fprintf(f, ")");
350 if (ival->stack_spill_pos != -1) {
351 if (ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
352 IR_ASSERT(ctx->spill_base >= 0);
353 fprintf(f, " [SPILL=0x%x(%%%s)]", ival->stack_spill_pos, ir_reg_name(ctx->spill_base, IR_ADDR));
354 } else {
355 fprintf(f, " [SPILL=0x%x]", ival->stack_spill_pos);
356 }
357 }
358 }
359 if (ival->next) {
360 fprintf(f, "\n\t");
361 } else if (ival->reg != IR_REG_NONE) {
362 fprintf(f, " ");
363 }
364 do {
365 if (ival->reg != IR_REG_NONE) {
366 fprintf(f, "[%%%s]", ir_reg_name(ival->reg, ival->type));
367 }
368 p = &ival->range;
369 fprintf(f, ": [%d.%d-%d.%d)",
370 IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
371 IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
372 if (i == 0) {
373 /* This is a TMP register */
374 if (ival->tmp_ref == IR_LIVE_POS_TO_REF(p->start)) {
375 fprintf(f, "/%d", ival->tmp_op_num);
376 } else {
377 fprintf(f, "/%d.%d", ival->tmp_ref, ival->tmp_op_num);
378 }
379 } else {
380 p = p->next;
381 while (p) {
382 fprintf(f, ", [%d.%d-%d.%d)",
383 IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
384 IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
385 p = p->next;
386 }
387 }
388 use_pos = ival->use_pos;
389 while (use_pos) {
390 if (use_pos->flags & IR_PHI_USE) {
391 IR_ASSERT(use_pos->op_num > 0);
392 fprintf(f, ", PHI_USE(%d.%d, phi=d_%d/%d)",
393 IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
394 -use_pos->hint_ref, use_pos->op_num);
395 } else if (use_pos->flags & IR_FUSED_USE) {
396 fprintf(f, ", USE(%d.%d/%d.%d",
397 IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
398 -use_pos->hint_ref, use_pos->op_num);
399 if (use_pos->hint >= 0) {
400 fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type));
401 }
402 fprintf(f, ")");
403 if (use_pos->flags & IR_USE_MUST_BE_IN_REG) {
404 fprintf(f, "!");
405 }
406 } else {
407 if (!use_pos->op_num) {
408 fprintf(f, ", DEF(%d.%d",
409 IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos));
410 } else {
411 fprintf(f, ", USE(%d.%d/%d",
412 IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
413 use_pos->op_num);
414 }
415 if (use_pos->hint >= 0) {
416 fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type));
417 }
418 if (use_pos->hint_ref) {
419 fprintf(f, ", hint=R%d", ctx->vregs[use_pos->hint_ref]);
420 }
421 fprintf(f, ")");
422 if (use_pos->flags & IR_USE_MUST_BE_IN_REG) {
423 fprintf(f, "!");
424 }
425 }
426 use_pos = use_pos->next;
427 }
428 if (ival->next) {
429 fprintf(f, "\n\t");
430 }
431 ival = ival->next;
432 } while (ival);
433 fprintf(f, "\n");
434 }
435 }
436 #if 1
437 n = ctx->vregs_count + ir_regs_number() + 2;
438 for (i = ctx->vregs_count + 1; i <= n; i++) {
439 ir_live_interval *ival = ctx->live_intervals[i];
440
441 if (ival) {
442 ir_live_range *p = &ival->range;
443 fprintf(f, "[%%%s] : [%d.%d-%d.%d)",
444 ir_reg_name(ival->reg, ival->type),
445 IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
446 IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
447 p = p->next;
448 while (p) {
449 fprintf(f, ", [%d.%d-%d.%d)",
450 IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
451 IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
452 p = p->next;
453 }
454 fprintf(f, "\n");
455 }
456 }
457 #endif
458 fprintf(f, "}\n");
459 }
460
ir_dump_codegen(const ir_ctx * ctx,FILE * f)461 void ir_dump_codegen(const ir_ctx *ctx, FILE *f)
462 {
463 ir_ref i, j, n, ref, *p;
464 ir_insn *insn;
465 uint32_t flags, b;
466 ir_block *bb;
467 bool first;
468
469 fprintf(f, "{\n");
470 for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
471 fprintf(f, "\t%s c_%d = ", ir_type_cname[insn->type], i);
472 if (insn->op == IR_FUNC) {
473 fprintf(f, "func %s", ir_get_str(ctx, insn->val.name));
474 ir_print_proto(ctx, insn->proto, f);
475 } else if (insn->op == IR_SYM) {
476 fprintf(f, "sym(%s)", ir_get_str(ctx, insn->val.name));
477 } else if (insn->op == IR_FUNC_ADDR) {
478 fprintf(f, "func *");
479 ir_print_const(ctx, insn, f, true);
480 ir_print_proto(ctx, insn->proto, f);
481 } else {
482 ir_print_const(ctx, insn, f, true);
483 }
484 fprintf(f, ";\n");
485 }
486
487 for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
488 fprintf(f, "#BB%d:\n", b);
489
490 for (i = bb->start, insn = ctx->ir_base + i; i <= bb->end;) {
491 flags = ir_op_flags[insn->op];
492 if (flags & IR_OP_FLAG_CONTROL) {
493 if (!(flags & IR_OP_FLAG_MEM) || insn->type == IR_VOID) {
494 fprintf(f, "\tl_%d = ", i);
495 } else {
496 fprintf(f, "\t%s d_%d", ir_type_cname[insn->type], i);
497 if (ctx->vregs && ctx->vregs[i]) {
498 fprintf(f, " {R%d}", ctx->vregs[i]);
499 }
500 if (ctx->regs) {
501 int8_t reg = ctx->regs[i][0];
502 if (reg != IR_REG_NONE) {
503 fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
504 (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
505 }
506 }
507 fprintf(f, ", l_%d = ", i);
508 }
509 } else {
510 fprintf(f, "\t");
511 if (flags & IR_OP_FLAG_DATA) {
512 fprintf(f, "%s d_%d", ir_type_cname[insn->type], i);
513 if (ctx->vregs && ctx->vregs[i]) {
514 fprintf(f, " {R%d}", ctx->vregs[i]);
515 }
516 if (ctx->regs) {
517 int8_t reg = ctx->regs[i][0];
518 if (reg != IR_REG_NONE) {
519 fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
520 (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
521 }
522 }
523 fprintf(f, " = ");
524 }
525 }
526 fprintf(f, "%s", ir_op_name[insn->op]);
527 n = ir_operands_count(ctx, insn);
528 if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) {
529 fprintf(f, "/%d", n);
530 } else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL) && n != 2) {
531 fprintf(f, "/%d", n - 2);
532 } else if (insn->op == IR_PHI && n != 3) {
533 fprintf(f, "/%d", n - 1);
534 } else if (insn->op == IR_SNAPSHOT) {
535 fprintf(f, "/%d", n - 1);
536 }
537 first = 1;
538 for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
539 uint32_t opnd_kind = IR_OPND_KIND(flags, j);
540
541 ref = *p;
542 if (ref) {
543 switch (opnd_kind) {
544 case IR_OPND_DATA:
545 if (IR_IS_CONST_REF(ref)) {
546 fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
547 } else {
548 fprintf(f, "%sd_%d", first ? "(" : ", ", ref);
549 }
550 if (ctx->vregs && ref > 0 && ctx->vregs[ref]) {
551 fprintf(f, " {R%d}", ctx->vregs[ref]);
552 }
553 if (ctx->regs) {
554 int8_t *regs = ctx->regs[i];
555 int8_t reg = regs[j];
556 if (reg != IR_REG_NONE) {
557 fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[ref].type),
558 (reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
559 }
560 }
561 first = 0;
562 break;
563 case IR_OPND_CONTROL:
564 case IR_OPND_CONTROL_DEP:
565 case IR_OPND_CONTROL_REF:
566 fprintf(f, "%sl_%d", first ? "(" : ", ", ref);
567 first = 0;
568 break;
569 case IR_OPND_STR:
570 fprintf(f, "%s\"%s\"", first ? "(" : ", ", ir_get_str(ctx, ref));
571 first = 0;
572 break;
573 case IR_OPND_PROTO:
574 fprintf(f, "%sfunc ", first ? "(" : ", ");
575 ir_print_proto(ctx, ref, f);
576 break;
577 case IR_OPND_PROB:
578 if (ref == 0) {
579 break;
580 }
581 IR_FALLTHROUGH;
582 case IR_OPND_NUM:
583 fprintf(f, "%s%d", first ? "(" : ", ", ref);
584 first = 0;
585 break;
586 }
587 } else if (opnd_kind == IR_OPND_NUM) {
588 fprintf(f, "%s%d", first ? "(" : ", ", ref);
589 first = 0;
590 } else if (j != n &&
591 (IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) {
592 fprintf(f, "%snull", first ? "(" : ", ");
593 first = 0;
594 }
595 }
596 if (first) {
597 fprintf(f, ";");
598 } else {
599 fprintf(f, ");");
600 }
601 if (((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) && ctx->binding) {
602 ir_ref var = ir_binding_find(ctx, i);
603 if (var) {
604 IR_ASSERT(var < 0);
605 fprintf(f, " # BIND(0x%x);", -var);
606 }
607 }
608 if (ctx->rules) {
609 uint32_t rule = ctx->rules[i];
610 uint32_t id = rule & ~(IR_FUSED_REG|IR_FUSED|IR_SKIPPED|IR_SIMPLE);
611
612 if (id < IR_LAST_OP) {
613 fprintf(f, " # RULE(%s", ir_op_name[id]);
614 } else {
615 IR_ASSERT(id > IR_LAST_OP /*&& id < IR_LAST_RULE*/);
616 fprintf(f, " # RULE(%s", ir_rule_name[id - IR_LAST_OP]);
617 }
618 if (rule & IR_FUSED) {
619 fprintf(f, ":FUSED");
620 }
621 if (rule & IR_SKIPPED) {
622 fprintf(f, ":SKIPPED");
623 }
624 if (rule & IR_SIMPLE) {
625 fprintf(f, ":SIMPLE");
626 }
627 fprintf(f, ")");
628 }
629 fprintf(f, "\n");
630 n = ir_insn_inputs_to_len(n);
631 i += n;
632 insn += n;
633 }
634
635 if (bb->flags & IR_BB_DESSA_MOVES) {
636 ir_dump_dessa_moves(ctx, b, bb, f);
637 }
638
639 insn = &ctx->ir_base[bb->end];
640 if (insn->op == IR_END || insn->op == IR_LOOP_END) {
641 uint32_t succ;
642
643 if (bb->successors_count == 1) {
644 succ = ctx->cfg_edges[bb->successors];
645 } else {
646 /* END may have a fake control edge to ENTRY */
647 IR_ASSERT(bb->successors_count == 2);
648 succ = ctx->cfg_edges[bb->successors];
649 if (ctx->ir_base[ctx->cfg_blocks[succ].start].op == IR_ENTRY) {
650 succ = ctx->cfg_edges[bb->successors + 1];
651 #ifdef IR_DEBUG
652 } else {
653 uint32_t fake_succ = ctx->cfg_edges[bb->successors + 1];
654 IR_ASSERT(ctx->ir_base[ctx->cfg_blocks[fake_succ].start].op == IR_ENTRY);
655 #endif
656 }
657 }
658 if (succ != b + 1) {
659 fprintf(f, "\t# GOTO BB%d\n", succ);
660 }
661 } else if (insn->op == IR_IF) {
662 uint32_t true_block, false_block, *p;
663
664 p = &ctx->cfg_edges[bb->successors];
665 true_block = *p;
666 if (ctx->ir_base[ctx->cfg_blocks[true_block].start].op == IR_IF_TRUE) {
667 false_block = *(p+1);
668 IR_ASSERT(ctx->ir_base[ctx->cfg_blocks[false_block].start].op == IR_IF_FALSE);
669 } else {
670 false_block = true_block;
671 IR_ASSERT(ctx->ir_base[ctx->cfg_blocks[false_block].start].op == IR_IF_FALSE);
672 true_block = *(p+1);
673 IR_ASSERT(ctx->ir_base[ctx->cfg_blocks[true_block].start].op == IR_IF_TRUE);
674 }
675 fprintf(f, "\t# IF_TRUE BB%d, IF_FALSE BB%d\n", true_block, false_block);
676 } else if (insn->op == IR_SWITCH) {
677 fprintf(f, "\t# SWITCH ...\n");
678 }
679 }
680 fprintf(f, "}\n");
681 }
682