xref: /php-src/ext/opcache/jit/ir/ir_dump.c (revision f87632e7)
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_block(ir_ctx * ctx,FILE * f,uint32_t b,ir_block * bb)213 static void ir_dump_cfg_block(ir_ctx *ctx, FILE *f, uint32_t b, ir_block *bb)
214 {
215 	fprintf(f, "BB%d:\n", b);
216 	fprintf(f, "\tstart=%d\n", bb->start);
217 	fprintf(f, "\tend=%d\n", bb->end);
218 	if (bb->successors_count) {
219 		uint32_t i;
220 
221 		fprintf(f, "\tsuccessors(%d) [BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
222 		for (i = 1; i < bb->successors_count; i++) {
223 			fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
224 		}
225 		fprintf(f, "]\n");
226 	}
227 	if (bb->predecessors_count) {
228 		uint32_t i;
229 
230 		fprintf(f, "\tpredecessors(%d) [BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
231 		for (i = 1; i < bb->predecessors_count; i++) {
232 			fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
233 		}
234 		fprintf(f, "]\n");
235 	}
236 	if (bb->dom_parent > 0) {
237 		fprintf(f, "\tdom_parent=BB%d\n", bb->dom_parent);
238 	}
239 	fprintf(f, "\tdom_depth=%d\n", bb->dom_depth);
240 	if (bb->dom_child > 0) {
241 		int child = bb->dom_child;
242 		fprintf(f, "\tdom_children [BB%d", child);
243 		child = ctx->cfg_blocks[child].dom_next_child;
244 		while (child > 0) {
245 			fprintf(f, ", BB%d", child);
246 			child = ctx->cfg_blocks[child].dom_next_child;
247 		}
248 		fprintf(f, "]\n");
249 	}
250 	if (bb->flags & IR_BB_ENTRY) {
251 		fprintf(f, "\tENTRY\n");
252 	}
253 	if (bb->flags & IR_BB_UNREACHABLE) {
254 		fprintf(f, "\tUNREACHABLE\n");
255 	}
256 	if (bb->flags & IR_BB_LOOP_HEADER) {
257 		if (bb->flags & IR_BB_LOOP_WITH_ENTRY) {
258 			fprintf(f, "\tLOOP_HEADER, LOOP_WITH_ENTRY\n");
259 		} else {
260 			fprintf(f, "\tLOOP_HEADER\n");
261 		}
262 	}
263 	if (bb->flags & IR_BB_IRREDUCIBLE_LOOP) {
264 		fprintf(stderr, "\tIRREDUCIBLE_LOOP\n");
265 	}
266 	if (bb->loop_header > 0) {
267 		fprintf(f, "\tloop_header=BB%d\n", bb->loop_header);
268 	}
269 	if (bb->loop_depth != 0) {
270 		fprintf(f, "\tloop_depth=%d\n", bb->loop_depth);
271 	}
272 	if (bb->flags & IR_BB_OSR_ENTRY_LOADS) {
273 		ir_list *list = (ir_list*)ctx->osr_entry_loads;
274 		uint32_t pos = 0, i, count;
275 
276 		IR_ASSERT(list);
277 		while (1) {
278 			i = ir_list_at(list, pos);
279 			if (b == i) {
280 				break;
281 			}
282 			IR_ASSERT(i != 0); /* end marker */
283 			pos++;
284 			count = ir_list_at(list, pos);
285 			pos += count + 1;
286 		}
287 		pos++;
288 		count = ir_list_at(list, pos);
289 		pos++;
290 
291 		for (i = 0; i < count; i++, pos++) {
292 			ir_ref ref = ir_list_at(list, pos);
293 			fprintf(f, "\tOSR_ENTRY_LOAD=d_%d\n", ref);
294 		}
295 	}
296 	if (bb->flags & IR_BB_DESSA_MOVES) {
297 		ir_dump_dessa_moves(ctx, b, bb, f);
298 	}
299 }
300 
ir_dump_cfg(ir_ctx * ctx,FILE * f)301 void ir_dump_cfg(ir_ctx *ctx, FILE *f)
302 {
303 	if (ctx->cfg_blocks) {
304 		uint32_t  b, i, bb_count = ctx->cfg_blocks_count;
305 		ir_block *bb = ctx->cfg_blocks + 1;
306 
307 		fprintf(f, "{ # CFG\n");
308 		if (ctx->cfg_schedule) {
309 			for (i = 1; i <= bb_count; i++) {
310 				b = ctx->cfg_schedule[i];
311 				bb = &ctx->cfg_blocks[b];
312 				ir_dump_cfg_block(ctx, f, b, bb);
313 			}
314 		} else {
315 			for (b = 1; b <= bb_count; b++, bb++) {
316 				ir_dump_cfg_block(ctx, f, b, bb);
317 			}
318 		}
319 		fprintf(f, "}\n");
320 	}
321 }
322 
ir_dump_cfg_map(const ir_ctx * ctx,FILE * f)323 void ir_dump_cfg_map(const ir_ctx *ctx, FILE *f)
324 {
325 	ir_ref i;
326     uint32_t *_blocks = ctx->cfg_map;
327 
328     if (_blocks) {
329 		fprintf(f, "{ # CFG map (insn -> bb)\n");
330 		for (i = IR_UNUSED + 1; i < ctx->insns_count; i++) {
331 			fprintf(f, "%d -> %d\n", i, _blocks[i]);
332 		}
333 		fprintf(f, "}\n");
334 	}
335 }
336 
ir_dump_live_ranges(const ir_ctx * ctx,FILE * f)337 void ir_dump_live_ranges(const ir_ctx *ctx, FILE *f)
338 {
339     ir_ref i, j, n;
340 
341 	if (!ctx->live_intervals) {
342 		return;
343 	}
344 	fprintf(f, "{ # LIVE-RANGES (vregs_count=%d)\n", ctx->vregs_count);
345 	for (i = 0; i <= ctx->vregs_count; i++) {
346 		ir_live_interval *ival = ctx->live_intervals[i];
347 
348 		if (ival) {
349 			ir_live_range *p;
350 			ir_use_pos *use_pos;
351 
352 			if (i == 0) {
353 				fprintf(f, "TMP");
354 			} else {
355 				for (j = 1; j < ctx->insns_count; j++) {
356 					if (ctx->vregs[j] == (uint32_t)i) {
357 						break;
358 					}
359 				}
360 				fprintf(f, "R%d (d_%d", i, j);
361 				for (j++; j < ctx->insns_count; j++) {
362 					if (ctx->vregs[j] == (uint32_t)i) {
363 						fprintf(f, ", d_%d", j);
364 					}
365 				}
366 				fprintf(f, ")");
367 				if (ival->stack_spill_pos != -1) {
368 					if (ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
369 						IR_ASSERT(ctx->spill_base >= 0);
370 						fprintf(f, " [SPILL=0x%x(%%%s)]", ival->stack_spill_pos, ir_reg_name(ctx->spill_base, IR_ADDR));
371 					} else {
372 						fprintf(f, " [SPILL=0x%x]", ival->stack_spill_pos);
373 					}
374 				}
375 			}
376 			if (ival->next) {
377 				fprintf(f, "\n\t");
378 			} else if (ival->reg != IR_REG_NONE) {
379 				fprintf(f, " ");
380 			}
381 			do {
382 				if (ival->reg != IR_REG_NONE) {
383 					fprintf(f, "[%%%s]", ir_reg_name(ival->reg, ival->type));
384 				}
385 				p = &ival->range;
386 				fprintf(f, ": [%d.%d-%d.%d)",
387 					IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
388 					IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
389 				if (i == 0) {
390 					/* This is a TMP register */
391 					if (ival->tmp_ref == IR_LIVE_POS_TO_REF(p->start)) {
392 						fprintf(f, "/%d", ival->tmp_op_num);
393 					} else {
394 						fprintf(f, "/%d.%d", ival->tmp_ref, ival->tmp_op_num);
395 					}
396 				} else {
397 					p = p->next;
398 					while (p) {
399 						fprintf(f, ", [%d.%d-%d.%d)",
400 							IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
401 							IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
402 						p = p->next;
403 					}
404 				}
405 				use_pos = ival->use_pos;
406 				while (use_pos) {
407 					if (use_pos->flags & IR_PHI_USE) {
408 						IR_ASSERT(use_pos->op_num > 0);
409 						fprintf(f, ", PHI_USE(%d.%d, phi=d_%d/%d)",
410 							IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
411 							-use_pos->hint_ref, use_pos->op_num);
412 					} else if (use_pos->flags & IR_FUSED_USE) {
413 						fprintf(f, ", USE(%d.%d/%d.%d",
414 							IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
415 							-use_pos->hint_ref, use_pos->op_num);
416 						if (use_pos->hint >= 0) {
417 							fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type));
418 						}
419 						fprintf(f, ")");
420 						if (use_pos->flags & IR_USE_MUST_BE_IN_REG) {
421 							fprintf(f, "!");
422 						}
423 					} else {
424 						if (!use_pos->op_num) {
425 							fprintf(f, ", DEF(%d.%d",
426 								IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos));
427 						} else {
428 							fprintf(f, ", USE(%d.%d/%d",
429 								IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
430 								use_pos->op_num);
431 						}
432 						if (use_pos->hint >= 0) {
433 							fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type));
434 						}
435 						if (use_pos->hint_ref) {
436 							fprintf(f, ", hint=R%d", ctx->vregs[use_pos->hint_ref]);
437 						}
438 						fprintf(f, ")");
439 						if (use_pos->flags & IR_USE_MUST_BE_IN_REG) {
440 							fprintf(f, "!");
441 						}
442 					}
443 					use_pos = use_pos->next;
444 				}
445 				if (ival->next) {
446 					fprintf(f, "\n\t");
447 				}
448 				ival = ival->next;
449 			} while (ival);
450 			fprintf(f, "\n");
451 		}
452 	}
453 #if 1
454 	n = ctx->vregs_count + ir_regs_number() + 2;
455 	for (i = ctx->vregs_count + 1; i <= n; i++) {
456 		ir_live_interval *ival = ctx->live_intervals[i];
457 
458 		if (ival) {
459 			ir_live_range *p = &ival->range;
460 			fprintf(f, "[%%%s] : [%d.%d-%d.%d)",
461 				ir_reg_name(ival->reg, ival->type),
462 				IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
463 				IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
464 			p = p->next;
465 			while (p) {
466 				fprintf(f, ", [%d.%d-%d.%d)",
467 					IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
468 					IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
469 				p = p->next;
470 			}
471 			fprintf(f, "\n");
472 		}
473 	}
474 #endif
475 	fprintf(f, "}\n");
476 }
477 
ir_dump_codegen(const ir_ctx * ctx,FILE * f)478 void ir_dump_codegen(const ir_ctx *ctx, FILE *f)
479 {
480 	ir_ref i, j, n, ref, *p;
481 	ir_insn *insn;
482 	uint32_t flags, _b, b;
483 	ir_block *bb;
484 	bool first;
485 
486 	fprintf(f, "{\n");
487 	for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
488 		fprintf(f, "\t%s c_%d = ", ir_type_cname[insn->type], i);
489 		if (insn->op == IR_FUNC) {
490 			fprintf(f, "func %s", ir_get_str(ctx, insn->val.name));
491 			ir_print_proto(ctx, insn->proto, f);
492 		} else if (insn->op == IR_SYM) {
493 			fprintf(f, "sym(%s)", ir_get_str(ctx, insn->val.name));
494 		} else if (insn->op == IR_FUNC_ADDR) {
495 			fprintf(f, "func *");
496 			ir_print_const(ctx, insn, f, true);
497 			ir_print_proto(ctx, insn->proto, f);
498 		} else {
499 			ir_print_const(ctx, insn, f, true);
500 		}
501 		fprintf(f, ";\n");
502 	}
503 
504 	for (_b = 1; _b <= ctx->cfg_blocks_count; _b++) {
505 		if (ctx->cfg_schedule) {
506 			b = ctx->cfg_schedule[_b];
507 		} else {
508 			b = _b;
509 		}
510 		bb = &ctx->cfg_blocks[b];
511 		if ((bb->flags & (IR_BB_START|IR_BB_ENTRY|IR_BB_EMPTY)) == IR_BB_EMPTY) {
512 			continue;
513 		}
514 
515 		fprintf(f, "#BB%d: end=l_%d", b, bb->end);
516 		if (bb->flags & IR_BB_UNREACHABLE) {
517 			fprintf(f, ", U");
518 		}
519 		if (bb->dom_parent > 0) {
520 			fprintf(f, ", idom=BB%d(%d)", bb->dom_parent, bb->dom_depth);
521 		}
522 		if (bb->loop_depth != 0) {
523 			if (bb->flags & IR_BB_LOOP_HEADER) {
524 				if (bb->loop_header > 0) {
525 					fprintf(f, ", loop=HDR,BB%d(%d)", bb->loop_header, bb->loop_depth);
526 				} else {
527 					IR_ASSERT(bb->loop_depth == 1);
528 					fprintf(f, ", loop=HDR(%d)", bb->loop_depth);
529 				}
530 			} else {
531 				IR_ASSERT(bb->loop_header > 0);
532 				fprintf(f, ", loop=BB%d(%d)", bb->loop_header, bb->loop_depth);
533 			}
534 		}
535 		if (bb->predecessors_count) {
536 			uint32_t i;
537 
538 			fprintf(f, ", pred(%d)=[BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
539 			for (i = 1; i < bb->predecessors_count; i++) {
540 				fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
541 			}
542 			fprintf(f, "]");
543 		}
544 		if (bb->successors_count) {
545 			uint32_t i;
546 
547 			fprintf(f, ", succ(%d)=[BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
548 			for (i = 1; i < bb->successors_count; i++) {
549 				fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
550 			}
551 			fprintf(f, "]");
552 		}
553 		fprintf(f, "\n");
554 
555 		for (i = bb->start, insn = ctx->ir_base + i; i <= bb->end;) {
556 			flags = ir_op_flags[insn->op];
557 			if (flags & IR_OP_FLAG_CONTROL) {
558 				if (!(flags & IR_OP_FLAG_MEM) || insn->type == IR_VOID) {
559 					fprintf(f, "\tl_%d = ", i);
560 				} else {
561 					fprintf(f, "\t%s d_%d", ir_type_cname[insn->type], i);
562 					if (ctx->vregs && ctx->vregs[i]) {
563 						fprintf(f, " {R%d}", ctx->vregs[i]);
564 					}
565 					if (ctx->regs) {
566 						int8_t reg = ctx->regs[i][0];
567 						if (reg != IR_REG_NONE) {
568 							fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
569 								(reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
570 						}
571 					}
572 					fprintf(f, ", l_%d = ", i);
573 				}
574 			} else {
575 				fprintf(f, "\t");
576 				if (flags & IR_OP_FLAG_DATA) {
577 					fprintf(f, "%s d_%d", ir_type_cname[insn->type], i);
578 					if (ctx->vregs && ctx->vregs[i]) {
579 						fprintf(f, " {R%d}", ctx->vregs[i]);
580 					}
581 					if (ctx->regs) {
582 						int8_t reg = ctx->regs[i][0];
583 						if (reg != IR_REG_NONE) {
584 							fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
585 								(reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
586 						}
587 					}
588 					fprintf(f, " = ");
589 				}
590 			}
591 			fprintf(f, "%s", ir_op_name[insn->op]);
592 			n = ir_operands_count(ctx, insn);
593 			if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) {
594 				fprintf(f, "/%d", n);
595 			} else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL) && n != 2) {
596 				fprintf(f, "/%d", n - 2);
597 			} else if (insn->op == IR_PHI && n != 3) {
598 				fprintf(f, "/%d", n - 1);
599 			} else if (insn->op == IR_SNAPSHOT) {
600 				fprintf(f, "/%d", n - 1);
601 			}
602 			first = 1;
603 			for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
604 				uint32_t opnd_kind = IR_OPND_KIND(flags, j);
605 
606 				ref = *p;
607 				if (ref) {
608 					switch (opnd_kind) {
609 						case IR_OPND_DATA:
610 							if (IR_IS_CONST_REF(ref)) {
611 								fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
612 							} else {
613 								fprintf(f, "%sd_%d", first ? "(" : ", ", ref);
614 							}
615 							if (ctx->vregs && ref > 0 && ctx->vregs[ref]) {
616 								fprintf(f, " {R%d}", ctx->vregs[ref]);
617 							}
618 							if (ctx->regs) {
619 								int8_t *regs = ctx->regs[i];
620 								int8_t reg = regs[j];
621 								if (reg != IR_REG_NONE) {
622 									fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[ref].type),
623 										(reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
624 								}
625 							}
626 							first = 0;
627 							break;
628 						case IR_OPND_CONTROL:
629 						case IR_OPND_CONTROL_DEP:
630 						case IR_OPND_CONTROL_REF:
631 							fprintf(f, "%sl_%d", first ? "(" : ", ", ref);
632 							first = 0;
633 							break;
634 						case IR_OPND_STR:
635 							fprintf(f, "%s\"%s\"", first ? "(" : ", ", ir_get_str(ctx, ref));
636 							first = 0;
637 							break;
638 						case IR_OPND_PROTO:
639 							fprintf(f, "%sfunc ", first ? "(" : ", ");
640 							ir_print_proto(ctx, ref, f);
641 							break;
642 						case IR_OPND_PROB:
643 							if (ref == 0) {
644 								break;
645 							}
646 							IR_FALLTHROUGH;
647 						case IR_OPND_NUM:
648 							fprintf(f, "%s%d", first ? "(" : ", ", ref);
649 							first = 0;
650 							break;
651 					}
652 				} else if (opnd_kind == IR_OPND_NUM) {
653 					fprintf(f, "%s%d", first ? "(" : ", ", ref);
654 					first = 0;
655 				} else if (j != n &&
656 						(IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) {
657 					fprintf(f, "%snull", first ? "(" : ", ");
658 					first = 0;
659 				}
660 			}
661 			if (first) {
662 				fprintf(f, ";");
663 			} else {
664 				fprintf(f, ");");
665 			}
666 			if (((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) && ctx->binding) {
667 				ir_ref var = ir_binding_find(ctx, i);
668 				if (var) {
669 					IR_ASSERT(var < 0);
670 					fprintf(f, " # BIND(0x%x);", -var);
671 				}
672 			}
673 			if (ctx->rules) {
674 				uint32_t rule = ctx->rules[i];
675 				uint32_t id = rule & IR_RULE_MASK;
676 
677 				if (id < IR_LAST_OP) {
678 					fprintf(f, " # RULE(%s", ir_op_name[id]);
679 				} else {
680 					IR_ASSERT(id > IR_LAST_OP /*&& id < IR_LAST_RULE*/);
681 					fprintf(f, " # RULE(%s", ir_rule_name[id - IR_LAST_OP]);
682 				}
683 				if (rule & IR_FUSED) {
684 					fprintf(f, ":FUSED");
685 				}
686 				if (rule & IR_SKIPPED) {
687 					fprintf(f, ":SKIPPED");
688 				}
689 				if (rule & IR_SIMPLE) {
690 					fprintf(f, ":SIMPLE");
691 				}
692 				fprintf(f, ")");
693 			}
694 			fprintf(f, "\n");
695 			n = ir_insn_inputs_to_len(n);
696 			i += n;
697 			insn += n;
698 		}
699 
700 		if (bb->flags & IR_BB_DESSA_MOVES) {
701 			ir_dump_dessa_moves(ctx, b, bb, f);
702 		}
703 
704 		insn = &ctx->ir_base[bb->end];
705 		if (insn->op == IR_END || insn->op == IR_LOOP_END) {
706 			uint32_t succ;
707 
708 			if (bb->successors_count == 1) {
709 				succ = ctx->cfg_edges[bb->successors];
710 			} else {
711 				/* END may have a fake control edge to ENTRY */
712 				IR_ASSERT(bb->successors_count == 2);
713 				succ = ctx->cfg_edges[bb->successors];
714 				if (ctx->ir_base[ctx->cfg_blocks[succ].start].op == IR_ENTRY) {
715 					succ = ctx->cfg_edges[bb->successors + 1];
716 #ifdef IR_DEBUG
717 				} else {
718 					uint32_t fake_succ = ctx->cfg_edges[bb->successors + 1];
719 					IR_ASSERT(ctx->ir_base[ctx->cfg_blocks[fake_succ].start].op == IR_ENTRY);
720 #endif
721 				}
722 			}
723 			succ = ir_skip_empty_target_blocks(ctx, succ);
724 			if (ctx->cfg_schedule) {
725 				if (_b == ctx->cfg_blocks_count || succ != ctx->cfg_schedule[_b + 1]) {
726 					fprintf(f, "\t# GOTO BB%d\n", succ);
727 				}
728 			} else {
729 				if (succ != b + 1) {
730 					fprintf(f, "\t# GOTO BB%d\n", succ);
731 				}
732 			}
733 		} else if (insn->op == IR_IF) {
734 			uint32_t true_block, false_block;
735 
736 			ir_get_true_false_blocks(ctx, b, &true_block, &false_block);
737 			fprintf(f, "\t# IF_TRUE BB%d, IF_FALSE BB%d\n", true_block, false_block);
738 		} else if (insn->op == IR_SWITCH) {
739 			fprintf(f, "\t# SWITCH ...\n");
740 		}
741 	}
742 	fprintf(f, "}\n");
743 }
744