xref: /PHP-8.2/ext/opcache/jit/ir/ir_check.c (revision acda3af0)
1 /*
2  * IR - Lightweight JIT Compilation Framework
3  * (IR verification)
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_consistency_check(void)11 void ir_consistency_check(void)
12 {
13 	IR_ASSERT(IR_UNUSED == 0);
14 	IR_ASSERT(IR_NOP == 0);
15 
16 	IR_ASSERT((int)IR_BOOL   == (int)IR_C_BOOL);
17 	IR_ASSERT((int)IR_U8     == (int)IR_C_U8);
18 	IR_ASSERT((int)IR_U16    == (int)IR_C_U16);
19 	IR_ASSERT((int)IR_U32    == (int)IR_C_U32);
20 	IR_ASSERT((int)IR_U64    == (int)IR_C_U64);
21 	IR_ASSERT((int)IR_ADDR   == (int)IR_C_ADDR);
22 	IR_ASSERT((int)IR_CHAR   == (int)IR_C_CHAR);
23 	IR_ASSERT((int)IR_I8     == (int)IR_C_I8);
24 	IR_ASSERT((int)IR_I16    == (int)IR_C_I16);
25 	IR_ASSERT((int)IR_I32    == (int)IR_C_I32);
26 	IR_ASSERT((int)IR_I64    == (int)IR_C_I64);
27 	IR_ASSERT((int)IR_DOUBLE == (int)IR_C_DOUBLE);
28 	IR_ASSERT((int)IR_FLOAT  == (int)IR_C_FLOAT);
29 
30 	IR_ASSERT((IR_EQ ^ 1) == IR_NE);
31 	IR_ASSERT((IR_LT ^ 3) == IR_GT);
32 	IR_ASSERT((IR_GT ^ 3) == IR_LT);
33 	IR_ASSERT((IR_LE ^ 3) == IR_GE);
34 	IR_ASSERT((IR_GE ^ 3) == IR_LE);
35 	IR_ASSERT((IR_ULT ^ 3) == IR_UGT);
36 	IR_ASSERT((IR_UGT ^ 3) == IR_ULT);
37 	IR_ASSERT((IR_ULE ^ 3) == IR_UGE);
38 	IR_ASSERT((IR_UGE ^ 3) == IR_ULE);
39 
40 	IR_ASSERT(IR_ADD + 1 == IR_SUB);
41 }
42 
ir_check_use_list(const ir_ctx * ctx,ir_ref from,ir_ref to)43 static bool ir_check_use_list(const ir_ctx *ctx, ir_ref from, ir_ref to)
44 {
45 	ir_ref n, j, *p;
46 	ir_use_list *use_list = &ctx->use_lists[from];
47 
48 	n = use_list->count;
49 	for (j = 0, p = &ctx->use_edges[use_list->refs]; j < n; j++, p++) {
50 		if (*p == to) {
51 			return 1;
52 		}
53 	}
54 	return 0;
55 }
56 
ir_check_input_list(const ir_ctx * ctx,ir_ref from,ir_ref to)57 static bool ir_check_input_list(const ir_ctx *ctx, ir_ref from, ir_ref to)
58 {
59 	ir_insn *insn = &ctx->ir_base[to];
60 	ir_ref n, j, *p;
61 
62 	n = ir_input_edges_count(ctx, insn);
63 	for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
64 		if (*p == from) {
65 			return 1;
66 		}
67 	}
68 	return 0;
69 }
70 
ir_check_domination(const ir_ctx * ctx,ir_ref def,ir_ref use)71 static bool ir_check_domination(const ir_ctx *ctx, ir_ref def, ir_ref use)
72 {
73 	uint32_t b1 = ctx->cfg_map[def];
74 	uint32_t b2 = ctx->cfg_map[use];
75 	ir_block *blocks = ctx->cfg_blocks;
76 	uint32_t b1_depth = blocks[b1].dom_depth;
77 	const ir_block *bb2 = &blocks[b2];
78 
79 	if (b1 == b2) {
80 		return def < use;
81 	}
82 	while (bb2->dom_depth > b1_depth) {
83 		b2 = bb2->dom_parent;
84 		bb2 = &blocks[b2];
85 	}
86 	return b1 == b2;
87 }
88 
ir_check(const ir_ctx * ctx)89 bool ir_check(const ir_ctx *ctx)
90 {
91 	ir_ref i, j, n, *p, use;
92 	ir_insn *insn, *use_insn;
93 	ir_type type;
94 	uint32_t flags;
95 	bool ok = 1;
96 
97 	for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
98 		if (insn->op >= IR_LAST_OP) {
99 			fprintf(stderr, "ir_base[%d].op invalid opcode (%d)\n", i, insn->op);
100 			ok = 0;
101 			break;
102 		}
103 		flags = ir_op_flags[insn->op];
104 		n = ir_input_edges_count(ctx, insn);
105 		for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
106 			use = *p;
107 			if (use != IR_UNUSED) {
108 				if (IR_IS_CONST_REF(use)) {
109 					if (use >= ctx->consts_count) {
110 						fprintf(stderr, "ir_base[%d].ops[%d] constant reference (%d) is out of range\n", i, j, use);
111 						ok = 0;
112 					}
113 				} else {
114 					if (use >= ctx->insns_count) {
115 						fprintf(stderr, "ir_base[%d].ops[%d] insn reference (%d) is out of range\n", i, j, use);
116 						ok = 0;
117 					}
118 					use_insn = &ctx->ir_base[use];
119 					switch (IR_OPND_KIND(flags, j)) {
120 						case IR_OPND_DATA:
121 							if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_DATA)) {
122 								if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_MEM)
123 								 || use_insn->type == IR_VOID) {
124 									fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be DATA\n", i, j, use);
125 									ok = 0;
126 								}
127 							}
128 							if (use >= i
129 							 && !(insn->op == IR_PHI
130 							  && (!(ctx->flags2 & IR_LINEAR) || ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN))) {
131 								fprintf(stderr, "ir_base[%d].ops[%d] invalid forward reference (%d)\n", i, j, use);
132 								ok = 0;
133 							}
134 							if (flags & IR_OP_FLAG_DATA) {
135 								switch (insn->op) {
136 									case IR_COND:
137 										if (j == 1) {
138 											break;
139 										}
140 										IR_FALLTHROUGH;
141 									case IR_ADD:
142 									case IR_SUB:
143 									case IR_MUL:
144 									case IR_DIV:
145 									case IR_MOD:
146 									case IR_NEG:
147 									case IR_ABS:
148 									case IR_ADD_OV:
149 									case IR_SUB_OV:
150 									case IR_MUL_OV:
151 									case IR_NOT:
152 									case IR_OR:
153 									case IR_AND:
154 									case IR_XOR:
155 									case IR_SHL:
156 									case IR_SHR:
157 									case IR_SAR:
158 									case IR_ROL:
159 									case IR_ROR:
160 									case IR_BSWAP:
161 									case IR_MIN:
162 									case IR_MAX:
163 									case IR_PHI:
164 									case IR_COPY:
165 									case IR_PI:
166 										if (insn->type != use_insn->type) {
167 											if (j == 2
168 											 && (insn->op == IR_SHL
169 											  || insn->op == IR_SHR
170 											  || insn->op == IR_SAR
171 											  || insn->op == IR_ROL
172 											  || insn->op == IR_ROR)
173 											 && ir_type_size[use_insn->type] < ir_type_size[insn->type]) {
174 												/* second argument of SHIFT may be incompatible with result */
175 												break;
176 											}
177 											if (insn->op == IR_NOT && insn->type == IR_BOOL) {
178 												/* boolean not */
179 												break;
180 											}
181 											if (sizeof(void*) == 8) {
182 												if (insn->type == IR_ADDR && (use_insn->type == IR_U64 || use_insn->type == IR_I64)) {
183 													break;
184 												}
185 											} else {
186 												if (insn->type == IR_ADDR && (use_insn->type == IR_U32 || use_insn->type == IR_I32)) {
187 													break;
188 												}
189 											}
190 											fprintf(stderr, "ir_base[%d].ops[%d] (%d) type is incompatible with result type (%d != %d)\n",
191 												i, j, use, use_insn->type, insn->type);
192 											ok = 0;
193 										}
194 										break;
195 								}
196 							}
197 							if ((ctx->flags2 & IR_LINEAR)
198 							 && ctx->cfg_map
199 							 && insn->op != IR_PHI
200 							 && !ir_check_domination(ctx, use, i)) {
201 								fprintf(stderr, "ir_base[%d].ops[%d] -> %d, %d doesn't dominate %d\n", i, j, use, use, i);
202 								ok = 0;
203 							}
204 							break;
205 						case IR_OPND_CONTROL:
206 							if (flags & IR_OP_FLAG_BB_START) {
207 								if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_BB_END)) {
208 									fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be BB_END\n", i, j, use);
209 									ok = 0;
210 								}
211 							} else {
212 								if (ir_op_flags[use_insn->op] & IR_OP_FLAG_BB_END) {
213 									fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must not be BB_END\n", i, j, use);
214 									ok = 0;
215 								}
216 							}
217 							break;
218 						case IR_OPND_CONTROL_DEP:
219 							if (use >= i
220 							 && !(insn->op == IR_LOOP_BEGIN)) {
221 								fprintf(stderr, "ir_base[%d].ops[%d] invalid forward reference (%d)\n", i, j, use);
222 								ok = 0;
223 							} else if (insn->op == IR_PHI) {
224 								ir_insn *merge_insn = &ctx->ir_base[insn->op1];
225 								if (merge_insn->op != IR_MERGE && merge_insn->op != IR_LOOP_BEGIN) {
226 									fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be MERGE or LOOP_BEGIN\n", i, j, use);
227 									ok = 0;
228 								}
229 							}
230 							break;
231 						case IR_OPND_CONTROL_REF:
232 							if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_CONTROL)) {
233 								fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be CONTROL\n", i, j, use);
234 								ok = 0;
235 							}
236 							break;
237 						default:
238 							fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) of unsupported kind\n", i, j, use);
239 							ok = 0;
240 					}
241 				}
242 			} else if ((insn->op == IR_RETURN || insn->op == IR_UNREACHABLE) && j == 2) {
243 				/* pass (function returns void) */
244 			} else if (insn->op == IR_BEGIN && j == 1) {
245 				/* pass (start of unreachable basic block) */
246 			} else if (IR_OPND_KIND(flags, j) != IR_OPND_CONTROL_REF
247 					&& (insn->op != IR_SNAPSHOT || j == 1)) {
248 				fprintf(stderr, "ir_base[%d].ops[%d] missing reference (%d)\n", i, j, use);
249 				ok = 0;
250 			}
251 			if (ctx->use_lists
252 			 && use > 0
253 			 && !ir_check_use_list(ctx, use, i)) {
254 				fprintf(stderr, "ir_base[%d].ops[%d] is not in use list (%d)\n", i, j, use);
255 				ok = 0;
256 			}
257 		}
258 
259 		switch (insn->op) {
260 			case IR_PHI:
261 				if (insn->inputs_count != ctx->ir_base[insn->op1].inputs_count + 1) {
262 					fprintf(stderr, "ir_base[%d] inconsistent PHI inputs_count (%d != %d)\n",
263 						i, insn->inputs_count, ctx->ir_base[insn->op1].inputs_count + 1);
264 					ok = 0;
265 				}
266 				break;
267 			case IR_LOAD:
268 			case IR_STORE:
269 				type = ctx->ir_base[insn->op2].type;
270 				if (type != IR_ADDR
271 				 && (!IR_IS_TYPE_INT(type) || ir_type_size[type] != ir_type_size[IR_ADDR])) {
272 					fprintf(stderr, "ir_base[%d].op2 must have ADDR type (%s)\n",
273 						i, ir_type_name[type]);
274 					ok = 0;
275 				}
276 				break;
277 			case IR_VLOAD:
278 			case IR_VSTORE:
279 				if (ctx->ir_base[insn->op2].op != IR_VAR) {
280 					fprintf(stderr, "ir_base[%d].op2 must be 'VAR' (%s)\n",
281 						i, ir_op_name[ctx->ir_base[insn->op2].op]);
282 					ok = 0;
283 				}
284 				break;
285 			case IR_RETURN:
286 				if (ctx->ret_type != (insn->op2 ? ctx->ir_base[insn->op2].type : IR_VOID)) {
287 					fprintf(stderr, "ir_base[%d].type incompatible return type\n", i);
288 					ok = 0;
289 				}
290 				break;
291 			case IR_TAILCALL:
292 				if (ctx->ret_type != insn->type) {
293 					fprintf(stderr, "ir_base[%d].type incompatible return type\n", i);
294 					ok = 0;
295 				}
296 				break;
297 		}
298 
299 		if (ctx->use_lists) {
300 			ir_use_list *use_list = &ctx->use_lists[i];
301 			ir_ref count;
302 
303 			for (j = 0, p = &ctx->use_edges[use_list->refs]; j < use_list->count; j++, p++) {
304 				use = *p;
305 				if (!ir_check_input_list(ctx, i, use)) {
306 					fprintf(stderr, "ir_base[%d] is in use list of ir_base[%d]\n", use, i);
307 					ok = 0;
308 				}
309 			}
310 
311 			if ((flags & IR_OP_FLAG_CONTROL) && !(flags & IR_OP_FLAG_MEM)) {
312 				switch (insn->op) {
313 					case IR_SWITCH:
314 						/* may have many successors */
315 						if (use_list->count < 1) {
316 							fprintf(stderr, "ir_base[%d].op (SWITCH) must have at least 1 successor (%d)\n", i, use_list->count);
317 							ok = 0;
318 						}
319 						break;
320 					case IR_IF:
321 						if (use_list->count != 2) {
322 							fprintf(stderr, "ir_base[%d].op (IF) must have 2 successors (%d)\n", i, use_list->count);
323 							ok = 0;
324 						}
325 						break;
326 					case IR_UNREACHABLE:
327 					case IR_RETURN:
328 						if (use_list->count == 1) {
329 							/* UNREACHABLE and RETURN may be linked with the following ENTRY by a fake edge */
330 							if (ctx->ir_base[ctx->use_edges[use_list->refs]].op == IR_ENTRY) {
331 								break;
332 							}
333 						}
334 						IR_FALLTHROUGH;
335 					case IR_IJMP:
336 						if (use_list->count != 0) {
337 							fprintf(stderr, "ir_base[%d].op (%s) must not have successors (%d)\n",
338 								i, ir_op_name[insn->op], use_list->count);
339 							ok = 0;
340 						}
341 						break;
342 					default:
343 						/* skip data references */
344 						count = use_list->count;
345 						for (j = 0, p = &ctx->use_edges[use_list->refs]; j < use_list->count; j++, p++) {
346 							use = *p;
347 							if (!(ir_op_flags[ctx->ir_base[use].op] & IR_OP_FLAG_CONTROL)) {
348 								count--;
349 							}
350 						}
351 						if (count != 1) {
352 							if (insn->op == IR_CALL && count == 2) {
353 								/* result of CALL may be used as data in control instruction */
354 								break;
355 							}
356 							if ((insn->op == IR_LOOP_END || insn->op == IR_END) && count == 2) {
357 								/* LOOP_END/END may be linked with the following ENTRY by a fake edge */
358 								if (ctx->ir_base[ctx->use_edges[use_list->refs]].op == IR_ENTRY) {
359 									count--;
360 								}
361 								if (ctx->ir_base[ctx->use_edges[use_list->refs + 1]].op == IR_ENTRY) {
362 									count--;
363 								}
364 								if (count == 1) {
365 									break;
366 								}
367 							}
368 							fprintf(stderr, "ir_base[%d].op (%s) must have 1 successor (%d)\n",
369 								i, ir_op_name[insn->op], count);
370 							ok = 0;
371 						}
372 						break;
373 				}
374 			}
375 		}
376 		n = ir_insn_inputs_to_len(n);
377 		i += n;
378 		insn += n;
379 	}
380 
381 //	if (!ok) {
382 //		ir_dump_codegen(ctx, stderr);
383 //	}
384 	IR_ASSERT(ok);
385 	return ok;
386 }
387