php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
ir_check.c
Go to the documentation of this file.
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
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
43static bool ir_check_use_list(const ir_ctx *ctx, ir_ref from, ir_ref to)
44{
45 ir_ref n, *p;
46 ir_use_list *use_list = &ctx->use_lists[from];
47
48 n = use_list->count;
49 for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
50 if (*p == to) {
51 return 1;
52 }
53 }
54 return 0;
55}
56
57static 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
71static 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
89bool ir_check(const ir_ctx *ctx)
90{
91 ir_ref i, j, n, *p, use;
92 ir_insn *insn, *use_insn;
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 (IR_OPND_KIND(flags, j) != IR_OPND_DATA) {
110 fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must not be constant\n", i, j, use);
111 ok = 0;
112 } else if (use >= ctx->consts_count) {
113 fprintf(stderr, "ir_base[%d].ops[%d] constant reference (%d) is out of range\n", i, j, use);
114 ok = 0;
115 }
116 } else {
117 if (use >= ctx->insns_count) {
118 fprintf(stderr, "ir_base[%d].ops[%d] insn reference (%d) is out of range\n", i, j, use);
119 ok = 0;
120 }
121 use_insn = &ctx->ir_base[use];
122 switch (IR_OPND_KIND(flags, j)) {
123 case IR_OPND_DATA:
124 if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_DATA)) {
125 if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_MEM)
126 || use_insn->type == IR_VOID) {
127 fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be DATA\n", i, j, use);
128 ok = 0;
129 }
130 }
131 if ((ctx->flags2 & IR_LINEAR)
132 && use >= i
133 && !(insn->op == IR_PHI && ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN)) {
134 fprintf(stderr, "ir_base[%d].ops[%d] invalid forward reference (%d)\n", i, j, use);
135 ok = 0;
136 }
137 if (flags & IR_OP_FLAG_DATA) {
138 switch (insn->op) {
139 case IR_COND:
140 if (j == 1) {
141 break;
142 }
144 case IR_ADD:
145 case IR_SUB:
146 case IR_MUL:
147 case IR_DIV:
148 case IR_MOD:
149 case IR_NEG:
150 case IR_ABS:
151 case IR_ADD_OV:
152 case IR_SUB_OV:
153 case IR_MUL_OV:
154 case IR_NOT:
155 case IR_OR:
156 case IR_AND:
157 case IR_XOR:
158 case IR_SHL:
159 case IR_SHR:
160 case IR_SAR:
161 case IR_ROL:
162 case IR_ROR:
163 case IR_BSWAP:
164 case IR_MIN:
165 case IR_MAX:
166 case IR_PHI:
167 case IR_COPY:
168 case IR_PI:
169 if (insn->type != use_insn->type) {
170 if (j == 2
171 && (insn->op == IR_SHL
172 || insn->op == IR_SHR
173 || insn->op == IR_SAR
174 || insn->op == IR_ROL
175 || insn->op == IR_ROR)
176 && ir_type_size[use_insn->type] < ir_type_size[insn->type]) {
177 /* second argument of SHIFT may be incompatible with result */
178 break;
179 }
180 if (insn->op == IR_NOT && insn->type == IR_BOOL) {
181 /* boolean not */
182 break;
183 }
184 if (insn->type == IR_ADDR && (use_insn->type == IR_UINTPTR_T || use_insn->type == IR_INTPTR_T)) {
185 break;
186 } else if (use_insn->type == IR_ADDR && (insn->type == IR_UINTPTR_T || insn->type == IR_INTPTR_T)) {
187 break;
188 }
189 fprintf(stderr, "ir_base[%d].ops[%d] (%d) type is incompatible with result type (%d != %d)\n",
190 i, j, use, use_insn->type, insn->type);
191 ok = 0;
192 }
193 break;
194 }
195 }
196 if ((ctx->flags2 & IR_LINEAR)
197 && ctx->cfg_map
198 && insn->op != IR_PHI
199 && !ir_check_domination(ctx, use, i)) {
200 fprintf(stderr, "ir_base[%d].ops[%d] -> %d, %d doesn't dominate %d\n", i, j, use, use, i);
201 ok = 0;
202 }
203 break;
204 case IR_OPND_CONTROL:
206 if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_BB_END)) {
207 fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be BB_END\n", i, j, use);
208 ok = 0;
209 }
210 } else {
211 if (ir_op_flags[use_insn->op] & IR_OP_FLAG_BB_END) {
212 fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must not be BB_END\n", i, j, use);
213 ok = 0;
214 }
215 }
216 break;
218 if ((ctx->flags2 & IR_LINEAR)
219 && 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;
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) */
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 case IR_PARAM:
298 if (i > 2 && ctx->ir_base[i - 1].op != IR_PARAM) {
299 fprintf(stderr, "ir_base[%d].op PARAMs must be used only right after START\n", i);
300 ok = 0;
301 }
302 break;
303 }
304
305 if (ctx->use_lists) {
306 ir_use_list *use_list = &ctx->use_lists[i];
307 ir_ref count, n = use_list->count;
308
309 for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
310 use = *p;
311 if (!ir_check_input_list(ctx, i, use)) {
312 fprintf(stderr, "ir_base[%d] is in use list of ir_base[%d]\n", use, i);
313 ok = 0;
314 }
315 }
316
318 switch (insn->op) {
319 case IR_SWITCH:
320 /* may have many successors */
321 if (use_list->count < 1) {
322 fprintf(stderr, "ir_base[%d].op (SWITCH) must have at least 1 successor (%d)\n", i, use_list->count);
323 ok = 0;
324 }
325 break;
326 case IR_IF:
327 if (use_list->count != 2) {
328 fprintf(stderr, "ir_base[%d].op (IF) must have 2 successors (%d)\n", i, use_list->count);
329 ok = 0;
330 }
331 break;
332 case IR_UNREACHABLE:
333 case IR_RETURN:
334 if (use_list->count == 1) {
335 /* UNREACHABLE and RETURN may be linked with the following ENTRY by a fake edge */
336 if (ctx->ir_base[ctx->use_edges[use_list->refs]].op == IR_ENTRY) {
337 break;
338 }
339 }
341 case IR_IJMP:
342 if (use_list->count != 0) {
343 fprintf(stderr, "ir_base[%d].op (%s) must not have successors (%d)\n",
344 i, ir_op_name[insn->op], use_list->count);
345 ok = 0;
346 }
347 break;
348 default:
349 /* skip data references */
350 count = n = use_list->count;
351 for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
352 use = *p;
353 if (!(ir_op_flags[ctx->ir_base[use].op] & IR_OP_FLAG_CONTROL)) {
354 count--;
355 }
356 }
357 if (count != 1) {
358 if (insn->op == IR_CALL && count == 2) {
359 /* result of CALL may be used as data in control instruction */
360 break;
361 }
362 if ((insn->op == IR_LOOP_END || insn->op == IR_END) && count == 2) {
363 /* LOOP_END/END may be linked with the following ENTRY by a fake edge */
364 if (ctx->ir_base[ctx->use_edges[use_list->refs]].op == IR_ENTRY) {
365 count--;
366 }
367 if (ctx->ir_base[ctx->use_edges[use_list->refs + 1]].op == IR_ENTRY) {
368 count--;
369 }
370 if (count == 1) {
371 break;
372 }
373 }
374 if (count == 0 && (insn->op == IR_END || insn->op == IR_LOOP_END)) {
375 /* Dead block */
376 break;
377 }
378 fprintf(stderr, "ir_base[%d].op (%s) must have 1 successor (%d)\n",
379 i, ir_op_name[insn->op], count);
380 ok = 0;
381 }
382 break;
383 }
384 }
385 }
387 i += n;
388 insn += n;
389 }
390
391// if (!ok) {
392// ir_dump_codegen(ctx, stderr);
393// }
394 IR_ASSERT(ok);
395 return ok;
396}
fprintf($stream, string $format, mixed ... $values)
count(Countable|array $value, int $mode=COUNT_NORMAL)
zend_ffi_type * type
Definition ffi.c:3812
zend_long n
Definition ffi.c:4979
again j
const char * ir_type_name[IR_LAST_TYPE]
Definition ir.c:56
const uint32_t ir_op_flags[IR_LAST_OP]
Definition ir.c:294
const char * ir_op_name[IR_LAST_OP]
Definition ir.c:71
const uint8_t ir_type_size[IR_LAST_TYPE]
Definition ir.c:61
#define IR_UINTPTR_T
Definition ir.h:166
enum _ir_type ir_type
#define IR_IS_TYPE_INT(t)
Definition ir.h:145
#define IR_UNUSED
Definition ir.h:395
@ IR_VOID
Definition ir.h:151
int32_t ir_ref
Definition ir.h:390
#define IR_INTPTR_T
Definition ir.h:167
#define IR_IS_CONST_REF(ref)
Definition ir.h:392
@ IR_LAST_OP
Definition ir.h:376
struct _ir_ctx ir_ctx
Definition ir.h:550
struct _ir_use_list ir_use_list
Definition ir.h:551
struct _ir_insn ir_insn
struct _ir_block ir_block
Definition ir.h:552
void ir_consistency_check(void)
Definition ir_check.c:11
bool ir_check(const ir_ctx *ctx)
Definition ir_check.c:89
#define IR_OPND_CONTROL_REF
Definition ir_private.h:949
IR_ALWAYS_INLINE ir_ref ir_input_edges_count(const ir_ctx *ctx, const ir_insn *insn)
Definition ir_private.h:981
#define IR_MIN(a, b)
Definition ir_private.h:63
#define IR_OPND_DATA
Definition ir_private.h:946
#define IR_OPND_CONTROL_DEP
Definition ir_private.h:948
#define IR_OPND_CONTROL
Definition ir_private.h:947
#define IR_FALLTHROUGH
Definition ir_private.h:54
#define IR_ASSERT(x)
Definition ir_private.h:17
#define IR_MAX(a, b)
Definition ir_private.h:62
#define IR_OPND_KIND(flags, i)
Definition ir_private.h:963
#define IR_LINEAR
IR_ALWAYS_INLINE uint32_t ir_insn_inputs_to_len(uint32_t inputs_count)
Definition ir_private.h:992
#define IR_OP_FLAG_CONTROL
Definition ir_private.h:931
#define IR_OP_FLAG_BB_START
Definition ir_private.h:934
#define IR_OP_FLAG_BB_END
Definition ir_private.h:935
#define IR_OP_FLAG_MEM
Definition ir_private.h:932
#define IR_OP_FLAG_DATA
Definition ir_private.h:930
p
Definition session.c:1105
uint32_t dom_parent
uint32_t dom_depth
ir_ref consts_count
Definition ir.h:577
ir_block * cfg_blocks
Definition ir.h:592
ir_type ret_type
Definition ir.h:581
ir_use_list * use_lists
Definition ir.h:587
ir_insn * ir_base
Definition ir.h:574
ir_ref insns_count
Definition ir.h:575
uint32_t flags2
Definition ir.h:580
ir_ref * use_edges
Definition ir.h:588
uint32_t * cfg_map
Definition ir.h:594