xref: /php-src/ext/opcache/jit/ir/ir_save.c (revision 9fae55f5)
1 /*
2  * IR - Lightweight JIT Compilation Framework
3  * (IR saver)
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_print_proto(const ir_ctx * ctx,ir_ref func_proto,FILE * f)11 void ir_print_proto(const ir_ctx *ctx, ir_ref func_proto, FILE *f)
12 {
13 	ir_ref j;
14 
15 	if (func_proto) {
16 		const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, func_proto);
17 
18 		fprintf(f, "(");
19 		if (proto->params_count > 0) {
20 			fprintf(f, "%s", ir_type_cname[proto->param_types[0]]);
21 			for (j = 1; j < proto->params_count; j++) {
22 				fprintf(f, ", %s", ir_type_cname[proto->param_types[j]]);
23 			}
24 			if (proto->flags & IR_VARARG_FUNC) {
25 				fprintf(f, ", ...");
26 			}
27 		} else if (proto->flags & IR_VARARG_FUNC) {
28 			fprintf(f, "...");
29 		}
30 		fprintf(f, "): %s", ir_type_cname[proto->ret_type]);
31 		if (proto->flags & IR_FASTCALL_FUNC) {
32 			fprintf(f, " __fastcall");
33 		} else if (proto->flags & IR_BUILTIN_FUNC) {
34 			fprintf(f, " __builtin");
35 		}
36 	} else {
37 		fprintf(f, "(): int32_t");
38 	}
39 }
40 
ir_save_dessa_moves(const ir_ctx * ctx,int b,ir_block * bb,FILE * f)41 static void ir_save_dessa_moves(const ir_ctx *ctx, int b, ir_block *bb, FILE *f)
42 {
43 	uint32_t succ;
44 	ir_block *succ_bb;
45 	ir_use_list *use_list;
46 	ir_ref k, i, *p, use_ref, input;
47 	ir_insn *use_insn;
48 
49 	IR_ASSERT(bb->successors_count == 1);
50 	succ = ctx->cfg_edges[bb->successors];
51 	succ_bb = &ctx->cfg_blocks[succ];
52 	IR_ASSERT(succ_bb->predecessors_count > 1);
53 	use_list = &ctx->use_lists[succ_bb->start];
54 	k = ir_phi_input_number(ctx, succ_bb, b);
55 
56 	for (i = 0, p = &ctx->use_edges[use_list->refs]; i < use_list->count; i++, p++) {
57 		use_ref = *p;
58 		use_insn = &ctx->ir_base[use_ref];
59 		if (use_insn->op == IR_PHI) {
60 			input = ir_insn_op(use_insn, k);
61 			if (IR_IS_CONST_REF(input)) {
62 				fprintf(f, "\t# DESSA MOV c_%d", -input);
63 			} else if (ctx->vregs[input] != ctx->vregs[use_ref]) {
64 				fprintf(f, "\t# DESSA MOV d_%d {R%d}", input, ctx->vregs[input]);
65 			} else {
66 				continue;
67 			}
68 			if (ctx->regs) {
69 				int8_t *regs = ctx->regs[use_ref];
70 				int8_t reg = regs[k];
71 				if (reg != IR_REG_NONE) {
72 					fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[input].type),
73 						(reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
74 				}
75 			}
76 			fprintf(f, " -> d_%d {R%d}", use_ref, ctx->vregs[use_ref]);
77 			if (ctx->regs) {
78 				int8_t reg = ctx->regs[use_ref][0];
79 				if (reg != IR_REG_NONE) {
80 					fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[use_ref].type),
81 						(reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
82 				}
83 			}
84 			fprintf(f, "\n");
85 		}
86 	}
87 }
88 
ir_save(const ir_ctx * ctx,uint32_t save_flags,FILE * f)89 void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f)
90 {
91 	ir_ref i, j, n, ref, *p;
92 	ir_insn *insn;
93 	uint32_t flags;
94 	bool first;
95 
96 	fprintf(f, "{\n");
97 	for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
98 		fprintf(f, "\t%s c_%d = ", ir_type_cname[insn->type], i);
99 		if (insn->op == IR_FUNC) {
100 			fprintf(f, "func %s", ir_get_str(ctx, insn->val.name));
101 			ir_print_proto(ctx, insn->proto, f);
102 		} else if (insn->op == IR_SYM) {
103 			fprintf(f, "sym(%s)", ir_get_str(ctx, insn->val.name));
104 		} else if (insn->op == IR_FUNC_ADDR) {
105 			fprintf(f, "func *");
106 			ir_print_const(ctx, insn, f, true);
107 			ir_print_proto(ctx, insn->proto, f);
108 		} else {
109 			ir_print_const(ctx, insn, f, true);
110 		}
111 		fprintf(f, ";\n");
112 	}
113 
114 	for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
115 		flags = ir_op_flags[insn->op];
116 
117 		if ((save_flags & IR_SAVE_CFG)
118 		 && ctx->cfg_map
119 		 && (int32_t)ctx->cfg_map[i] > 0 /* the node may be scheduled incompletely */
120 		 && ctx->cfg_blocks[ctx->cfg_map[i]].start == i) {
121 			uint32_t b = ctx->cfg_map[i];
122 			ir_block *bb = &ctx->cfg_blocks[b];
123 			fprintf(f, "#BB%d: end=l_%d", b, bb->end);
124 			if (bb->flags & IR_BB_UNREACHABLE) {
125 				fprintf(f, ", U");
126 			}
127 			if (bb->dom_parent > 0) {
128 				fprintf(f, ", idom=BB%d(%d)", bb->dom_parent, bb->dom_depth);
129 			}
130 			if (bb->loop_depth != 0) {
131 				if (bb->flags & IR_BB_LOOP_HEADER) {
132 					if (bb->loop_header > 0) {
133 						fprintf(f, ", loop=HDR,BB%d(%d)", bb->loop_header, bb->loop_depth);
134 					} else {
135 						IR_ASSERT(bb->loop_depth == 1);
136 						fprintf(f, ", loop=HDR(%d)", bb->loop_depth);
137 					}
138 				} else {
139 					IR_ASSERT(bb->loop_header > 0);
140 					fprintf(f, ", loop=BB%d(%d)", bb->loop_header, bb->loop_depth);
141 				}
142 			}
143 			if (bb->predecessors_count) {
144 				uint32_t i;
145 
146 				fprintf(f, ", pred(%d)=[BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
147 				for (i = 1; i < bb->predecessors_count; i++) {
148 					fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
149 				}
150 				fprintf(f, "]");
151 			}
152 			if (bb->successors_count) {
153 				uint32_t i;
154 
155 				fprintf(f, ", succ(%d)=[BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
156 				for (i = 1; i < bb->successors_count; i++) {
157 					fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
158 				}
159 				fprintf(f, "]");
160 			}
161 			fprintf(f, "\n");
162 		}
163 
164 		if (flags & IR_OP_FLAG_CONTROL) {
165 			if (!(flags & IR_OP_FLAG_MEM) || insn->type == IR_VOID) {
166 				fprintf(f, "\tl_%d = ", i);
167 			} else {
168 				fprintf(f, "\t%s d_%d", ir_type_cname[insn->type], i);
169 				if (save_flags & IR_SAVE_REGS) {
170 					if (ctx->vregs && ctx->vregs[i]) {
171 						fprintf(f, " {R%d}", ctx->vregs[i]);
172 					}
173 					if (ctx->regs) {
174 						int8_t reg = ctx->regs[i][0];
175 						if (reg != IR_REG_NONE) {
176 							fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
177 								(reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
178 						}
179 					}
180 				}
181 				fprintf(f, ", l_%d = ", i);
182 			}
183 		} else {
184 			fprintf(f, "\t");
185 			if (flags & IR_OP_FLAG_DATA) {
186 				fprintf(f, "%s d_%d", ir_type_cname[insn->type], i);
187 				if (save_flags & IR_SAVE_REGS) {
188 					if (ctx->vregs && ctx->vregs[i]) {
189 						fprintf(f, " {R%d}", ctx->vregs[i]);
190 					}
191 					if (ctx->regs) {
192 						int8_t reg = ctx->regs[i][0];
193 						if (reg != IR_REG_NONE) {
194 							fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
195 								(reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
196 						}
197 					}
198 				}
199 				fprintf(f, " = ");
200 			}
201 		}
202 		fprintf(f, "%s", ir_op_name[insn->op]);
203 		n = ir_operands_count(ctx, insn);
204 		if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) {
205 			fprintf(f, "/%d", n);
206 		} else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL) && n != 2) {
207 			fprintf(f, "/%d", n - 2);
208 		} else if (insn->op == IR_PHI && n != 3) {
209 			fprintf(f, "/%d", n - 1);
210 		} else if (insn->op == IR_SNAPSHOT) {
211 			fprintf(f, "/%d", n - 1);
212 		}
213 		first = 1;
214 		for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
215 			uint32_t opnd_kind = IR_OPND_KIND(flags, j);
216 
217 			ref = *p;
218 			if (ref) {
219 				switch (opnd_kind) {
220 					case IR_OPND_DATA:
221 						if (IR_IS_CONST_REF(ref)) {
222 							fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
223 						} else {
224 							fprintf(f, "%sd_%d", first ? "(" : ", ", ref);
225 						}
226 						if (save_flags & IR_SAVE_REGS) {
227 							if (ctx->vregs && ref > 0 && ctx->vregs[ref]) {
228 								fprintf(f, " {R%d}", ctx->vregs[ref]);
229 							}
230 							if (ctx->regs) {
231 								int8_t *regs = ctx->regs[i];
232 								int8_t reg = regs[j];
233 								if (reg != IR_REG_NONE) {
234 									fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[ref].type),
235 										(reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
236 								}
237 							}
238 						}
239 						first = 0;
240 						break;
241 					case IR_OPND_CONTROL:
242 					case IR_OPND_CONTROL_DEP:
243 					case IR_OPND_CONTROL_REF:
244 						fprintf(f, "%sl_%d", first ? "(" : ", ", ref);
245 						first = 0;
246 						break;
247 					case IR_OPND_STR:
248 						fprintf(f, "%s\"%s\"", first ? "(" : ", ", ir_get_str(ctx, ref));
249 						first = 0;
250 						break;
251 					case IR_OPND_PROTO:
252 						fprintf(f, "%sfunc ", first ? "(" : ", ");
253 						ir_print_proto(ctx, ref, f);
254 						break;
255 					case IR_OPND_PROB:
256 						if (ref == 0) {
257 							break;
258 						}
259 						IR_FALLTHROUGH;
260 					case IR_OPND_NUM:
261 						fprintf(f, "%s%d", first ? "(" : ", ", ref);
262 						first = 0;
263 						break;
264 				}
265 			} else if (opnd_kind == IR_OPND_NUM) {
266 				fprintf(f, "%s%d", first ? "(" : ", ", ref);
267 				first = 0;
268 			} else if (j != n &&
269 					(IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) {
270 				fprintf(f, "%snull", first ? "(" : ", ");
271 				first = 0;
272 			}
273 		}
274 		if (first) {
275 			fprintf(f, ";");
276 		} else {
277 			fprintf(f, ");");
278 		}
279 		first = 1;
280 		if (((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) && ctx->binding) {
281 			ir_ref var = ir_binding_find(ctx, i);
282 			if (var) {
283 				IR_ASSERT(var < 0);
284 				fprintf(f, " # BIND(0x%x);", -var);
285 				first = 0;
286 			}
287 		}
288 
289 		if ((save_flags & IR_SAVE_CFG_MAP)
290 		 && ctx->cfg_map
291 		 && ctx->cfg_map[i] > 0) { /* the node may be scheduled incompletely */
292 			if (first) {
293 				fprintf(f, " #");
294 				first = 0;
295 			}
296 			fprintf(f, " BLOCK=BB%d;", ctx->cfg_map[i]);
297 		}
298 
299 		if ((save_flags & IR_SAVE_RULES)
300 		 && ctx->rules) {
301 			uint32_t rule = ctx->rules[i];
302 			uint32_t id = rule & IR_RULE_MASK;
303 
304 			if (first) {
305 				fprintf(f, " #");
306 				first = 0;
307 			}
308 			if (id < IR_LAST_OP) {
309 				fprintf(f, " RULE(%s", ir_op_name[id]);
310 			} else {
311 				IR_ASSERT(id > IR_LAST_OP /*&& id < IR_LAST_RULE*/);
312 				fprintf(f, " RULE(%s", ir_rule_name[id - IR_LAST_OP]);
313 			}
314 			if (rule & IR_FUSED) {
315 				fprintf(f, ":FUSED");
316 			}
317 			if (rule & IR_SKIPPED) {
318 				fprintf(f, ":SKIPPED");
319 			}
320 			if (rule & IR_SIMPLE) {
321 				fprintf(f, ":SIMPLE");
322 			}
323 			fprintf(f, ");");
324 		}
325 
326 		if ((save_flags & IR_SAVE_USE_LISTS)
327 		 && ctx->use_lists
328 		 && ctx->use_lists[i].count) {
329 			ir_use_list *use_list = &ctx->use_lists[i];
330 			ir_ref n = use_list->count;
331 			ir_ref *p = ctx->use_edges + use_list->refs;
332 
333 			if (first) {
334 				fprintf(f, " #");
335 				first = 0;
336 			}
337 			fprintf(f, " USE_LIST(%d)=[%05d", n, *p);
338 			for (p++, n--; n; p++, n--) {
339 				fprintf(f, ", %05d", *p);
340 			}
341 			fprintf(f, "];");
342 		}
343 		fprintf(f, "\n");
344 
345 		if ((save_flags & (IR_SAVE_CFG|IR_SAVE_REGS)) == (IR_SAVE_CFG|IR_SAVE_REGS)
346 		 && ctx->cfg_map
347 		 && ctx->cfg_map[i]
348 		 && ctx->cfg_blocks[ctx->cfg_map[i]].end == i) {
349 			uint32_t b = ctx->cfg_map[i];
350 			ir_block *bb = &ctx->cfg_blocks[b];
351 			if (bb->flags & IR_BB_DESSA_MOVES) {
352 				ir_save_dessa_moves(ctx, b, bb, f);
353 			}
354 		}
355 
356 		n = ir_insn_inputs_to_len(n);
357 		i += n;
358 		insn += n;
359 	}
360 	fprintf(f, "}\n");
361 }
362