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