/src/php-src/Zend/Optimizer/ssa_integrity.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine, SSA validation | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) The PHP Group | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 3.01 of the PHP license, | |
8 | | | that is bundled with this package in the file LICENSE, and is | |
9 | | | available through the world-wide-web at the following url: | |
10 | | | https://www.php.net/license/3_01.txt | |
11 | | | If you did not receive a copy of the PHP license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@php.net so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Nikita Popov <nikic@php.net> | |
16 | | +----------------------------------------------------------------------+ |
17 | | */ |
18 | | |
19 | | #include "Optimizer/zend_optimizer_internal.h" |
20 | | |
21 | | /* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and |
22 | | * CFG are upheld and prints messages to stderr if this is not the case. */ |
23 | | |
24 | 5.51M | static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) { |
25 | 5.51M | int use; |
26 | 19.3M | FOREACH_USE(&ssa->vars[var], use) { |
27 | 19.3M | if (use == check) { |
28 | 5.51M | return true; |
29 | 5.51M | } |
30 | 19.3M | } FOREACH_USE_END(); |
31 | 0 | return false; |
32 | 5.51M | } |
33 | | |
34 | 1.29M | static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) { |
35 | 1.29M | zend_ssa_phi *phi; |
36 | 2.80M | FOREACH_PHI_USE(&ssa->vars[var], phi) { |
37 | 2.80M | if (phi == check) { |
38 | 1.29M | return true; |
39 | 1.29M | } |
40 | 2.80M | } FOREACH_PHI_USE_END(); |
41 | 0 | return false; |
42 | 1.29M | } |
43 | | |
44 | 5.50M | static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) { |
45 | 5.50M | zend_ssa_op *ssa_op = &ssa->ops[op]; |
46 | 5.50M | return (ssa_op->op1_use == check) |
47 | 1.31M | || (ssa_op->op2_use == check) |
48 | 89.6k | || (ssa_op->result_use == check); |
49 | 5.50M | } |
50 | | |
51 | 4.34M | static inline bool is_defined_by_op(zend_ssa *ssa, int op, int check) { |
52 | 4.34M | zend_ssa_op *ssa_op = &ssa->ops[op]; |
53 | 4.34M | return (ssa_op->op1_def == check) |
54 | 3.82M | || (ssa_op->op2_def == check) |
55 | 3.79M | || (ssa_op->result_def == check); |
56 | 4.34M | } |
57 | | |
58 | 1.28M | static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) { |
59 | 1.28M | int source; |
60 | 4.98M | FOREACH_PHI_SOURCE(phi, source) { |
61 | 4.98M | if (source == check) { |
62 | 1.28M | return true; |
63 | 1.28M | } |
64 | 4.98M | } FOREACH_PHI_SOURCE_END(); |
65 | 0 | return false; |
66 | 1.28M | } |
67 | | |
68 | 970k | static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) { |
69 | 970k | int *predecessors = &cfg->predecessors[block->predecessor_offset]; |
70 | 1.30M | for (uint32_t i = 0; i < block->predecessors_count; i++) { |
71 | 1.30M | if (predecessors[i] == check) { |
72 | 970k | return true; |
73 | 970k | } |
74 | 1.30M | } |
75 | 0 | return false; |
76 | 970k | } |
77 | | |
78 | 969k | static inline bool is_in_successors(zend_basic_block *block, int check) { |
79 | 1.30M | for (uint32_t s = 0; s < block->successors_count; s++) { |
80 | 1.30M | if (block->successors[s] == check) { |
81 | 969k | return true; |
82 | 969k | } |
83 | 1.30M | } |
84 | 0 | return false; |
85 | 969k | } |
86 | | |
87 | 21.6M | static inline bool is_var_type(uint8_t type) { |
88 | 21.6M | return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0; |
89 | 21.6M | } |
90 | | |
91 | 5.51M | static inline bool is_defined(const zend_ssa *ssa, const zend_op_array *op_array, int var) { |
92 | 5.51M | const zend_ssa_var *ssa_var = &ssa->vars[var]; |
93 | 5.51M | return ssa_var->definition >= 0 || ssa_var->definition_phi || var < op_array->last_var; |
94 | 5.51M | } |
95 | | |
96 | 0 | #define FAIL(...) do { \ |
97 | 0 | if (status == SUCCESS) { \ |
98 | 0 | fprintf(stderr, "\nIn function %s::%s (%s):\n", \ |
99 | 0 | op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \ |
100 | 0 | op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \ |
101 | 0 | } \ |
102 | 0 | fprintf(stderr, __VA_ARGS__); \ |
103 | 0 | status = FAILURE; \ |
104 | 0 | } while (0) |
105 | | |
106 | | #define VARFMT "%d (%s%s)" |
107 | | #define VAR(i) \ |
108 | 0 | (i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \ |
109 | 0 | (ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "") |
110 | | |
111 | | #define INSTRFMT "%d (%s)" |
112 | | #define INSTR(i) \ |
113 | | (i), (zend_get_opcode_name(op_array->opcodes[i].opcode)) |
114 | | |
115 | 339k | void ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) { |
116 | 339k | zend_cfg *cfg = &ssa->cfg; |
117 | 339k | zend_ssa_phi *phi; |
118 | 339k | int i; |
119 | 339k | zend_result status = SUCCESS; |
120 | | |
121 | | /* Vars */ |
122 | 6.14M | for (i = 0; i < ssa->vars_count; i++) { |
123 | 5.80M | zend_ssa_var *var = &ssa->vars[i]; |
124 | 5.80M | int use; |
125 | 5.80M | uint32_t type = ssa->var_info[i].type; |
126 | | |
127 | 5.80M | if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) { |
128 | 129k | if (var->use_chain >= 0) { |
129 | 0 | FAIL("var " VARFMT " without def has op uses\n", VAR(i)); |
130 | 0 | } |
131 | 129k | if (var->phi_use_chain) { |
132 | 0 | FAIL("var " VARFMT " without def has phi uses\n", VAR(i)); |
133 | 0 | } |
134 | 129k | } |
135 | 5.80M | if (var->definition >= 0 && var->definition_phi) { |
136 | 0 | FAIL("var " VARFMT " has both def and def_phi\n", VAR(i)); |
137 | 0 | } |
138 | 5.80M | if (var->definition >= 0) { |
139 | 4.34M | if (!is_defined_by_op(ssa, var->definition, i)) { |
140 | 0 | FAIL("var " VARFMT " not defined by op " INSTRFMT "\n", |
141 | 0 | VAR(i), INSTR(var->definition)); |
142 | 0 | } |
143 | 4.34M | } |
144 | 5.80M | if (var->definition_phi) { |
145 | 748k | if (var->definition_phi->ssa_var != i) { |
146 | 0 | FAIL("var " VARFMT " not defined by given phi\n", VAR(i)); |
147 | 0 | } |
148 | 748k | } |
149 | | |
150 | | /* Floyd's cycle detection algorithm, applied for use chain. */ |
151 | 5.80M | use = var->use_chain; |
152 | 5.80M | int second_use = use; |
153 | 6.34M | while (use >= 0 && second_use >= 0) { |
154 | 4.95M | use = zend_ssa_next_use(ssa->ops, var - ssa->vars, use); |
155 | 4.95M | second_use = zend_ssa_next_use(ssa->ops, var - ssa->vars, second_use); |
156 | 4.95M | if (second_use < 0) { |
157 | 4.40M | break; |
158 | 4.40M | } |
159 | 546k | second_use = zend_ssa_next_use(ssa->ops, var - ssa->vars, second_use); |
160 | 546k | if (use == second_use) { |
161 | 0 | FAIL("cycle in uses of " VARFMT "\n", VAR(i)); |
162 | 0 | goto finish; |
163 | 0 | } |
164 | 546k | } |
165 | | |
166 | 11.3M | FOREACH_USE(var, use) { |
167 | 11.3M | if (!is_used_by_op(ssa, use, i)) { |
168 | 0 | fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use); |
169 | 0 | } |
170 | 11.3M | } FOREACH_USE_END(); |
171 | | |
172 | | /* Floyd's cycle detection algorithm, applied for phi nodes. */ |
173 | 5.80M | phi = var->phi_use_chain; |
174 | 5.80M | zend_ssa_phi *second_phi = phi; |
175 | 6.00M | while (phi && second_phi) { |
176 | 1.08M | phi = zend_ssa_next_use_phi(ssa, var - ssa->vars, phi); |
177 | 1.08M | second_phi = zend_ssa_next_use_phi(ssa, var - ssa->vars, second_phi); |
178 | 1.08M | if (!second_phi) { |
179 | 887k | break; |
180 | 887k | } |
181 | 198k | second_phi = zend_ssa_next_use_phi(ssa, var - ssa->vars, second_phi); |
182 | 198k | if (phi == second_phi) { |
183 | 0 | FAIL("cycle in phi uses of " VARFMT "\n", VAR(i)); |
184 | 0 | goto finish; |
185 | 0 | } |
186 | 198k | } |
187 | | |
188 | 7.08M | FOREACH_PHI_USE(var, phi) { |
189 | 7.08M | if (!is_in_phi_sources(ssa, phi, i)) { |
190 | 0 | FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var); |
191 | 0 | } |
192 | 7.08M | } FOREACH_PHI_USE_END(); |
193 | | |
194 | 5.80M | if ((type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY)) && !(type & MAY_BE_ARRAY_OF_ANY)) { |
195 | 0 | FAIL("var " VARFMT " has array key type but not value type\n", VAR(i)); |
196 | 0 | } |
197 | 5.80M | if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & (MAY_BE_ARRAY_KEY_ANY-MAY_BE_ARRAY_EMPTY))) { |
198 | 0 | FAIL("var " VARFMT " has array value type but not key type\n", VAR(i)); |
199 | 0 | } |
200 | 5.80M | if ((type & MAY_BE_REF) && ssa->var_info[i].ce) { |
201 | 0 | FAIL("var " VARFMT " may be ref but has ce\n", VAR(i)); |
202 | 0 | } |
203 | 5.80M | } |
204 | | |
205 | | /* Instructions */ |
206 | 9.93M | FOREACH_INSTR_NUM(i) { |
207 | 9.93M | zend_ssa_op *ssa_op = &ssa->ops[i]; |
208 | 9.93M | zend_op *opline = &op_array->opcodes[i]; |
209 | 9.93M | if (is_var_type(opline->op1_type)) { |
210 | 4.18M | if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) { |
211 | 0 | FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i)); |
212 | 0 | } |
213 | 4.18M | } else { |
214 | 3.04M | if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) { |
215 | 0 | FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i)); |
216 | 0 | } |
217 | 3.04M | } |
218 | 9.93M | if (is_var_type(opline->op2_type)) { |
219 | 1.23M | if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) { |
220 | 0 | FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i)); |
221 | 0 | } |
222 | 5.99M | } else { |
223 | 5.99M | if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) { |
224 | 0 | FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i)); |
225 | 0 | } |
226 | 5.99M | } |
227 | 9.93M | if (is_var_type(opline->result_type)) { |
228 | 3.79M | if (ssa_op->result_use < 0 && ssa_op->result_def < 0) { |
229 | 0 | FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i)); |
230 | 0 | } |
231 | 3.79M | } else { |
232 | 3.43M | if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) { |
233 | 0 | FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i)); |
234 | 0 | } |
235 | 3.43M | } |
236 | | |
237 | 9.93M | if (ssa_op->op1_use >= 0) { |
238 | 4.18M | if (ssa_op->op1_use >= ssa->vars_count) { |
239 | 0 | FAIL("op1 use %d out of range\n", ssa_op->op1_use); |
240 | 0 | } |
241 | 4.18M | if (!is_defined(ssa, op_array, ssa_op->op1_use)) { |
242 | 0 | FAIL("op1 use of " VARFMT " in " INSTRFMT " is not defined\n", |
243 | 0 | VAR(ssa_op->op1_use), INSTR(i)); |
244 | 0 | } |
245 | 4.18M | if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) { |
246 | 0 | FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n", |
247 | 0 | VAR(ssa_op->op1_use), INSTR(i)); |
248 | 0 | } |
249 | 4.18M | if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) { |
250 | 0 | FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n", |
251 | 0 | VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i)); |
252 | 0 | } |
253 | 4.18M | } |
254 | 9.93M | if (ssa_op->op2_use >= 0) { |
255 | 1.23M | if (ssa_op->op2_use >= ssa->vars_count) { |
256 | 0 | FAIL("op2 use %d out of range\n", ssa_op->op2_use); |
257 | 0 | } |
258 | 1.23M | if (!is_defined(ssa, op_array, ssa_op->op2_use)) { |
259 | 0 | FAIL("op2 use of " VARFMT " in " INSTRFMT " is not defined\n", |
260 | 0 | VAR(ssa_op->op2_use), INSTR(i)); |
261 | 0 | } |
262 | 1.23M | if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) { |
263 | 0 | FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n", |
264 | 0 | VAR(ssa_op->op2_use), INSTR(i)); |
265 | 0 | } |
266 | 1.23M | if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) { |
267 | 0 | FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n", |
268 | 0 | VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i)); |
269 | 0 | } |
270 | 1.23M | } |
271 | 9.93M | if (ssa_op->result_use >= 0) { |
272 | 89.6k | if (ssa_op->result_use >= ssa->vars_count) { |
273 | 0 | FAIL("result use %d out of range\n", ssa_op->result_use); |
274 | 0 | } |
275 | 89.6k | if (!is_defined(ssa, op_array, ssa_op->result_use)) { |
276 | 0 | FAIL("result use of " VARFMT " in " INSTRFMT " is not defined\n", |
277 | 0 | VAR(ssa_op->result_use), INSTR(i)); |
278 | 0 | } |
279 | 89.6k | if (!is_in_use_chain(ssa, ssa_op->result_use, i)) { |
280 | 0 | FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n", |
281 | 0 | VAR(ssa_op->result_use), INSTR(i)); |
282 | 0 | } |
283 | 89.6k | if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) { |
284 | 0 | FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n", |
285 | 0 | VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i)); |
286 | 0 | } |
287 | 89.6k | } |
288 | 9.93M | if (ssa_op->op1_def >= 0) { |
289 | 525k | if (ssa_op->op1_def >= ssa->vars_count) { |
290 | 0 | FAIL("op1 def %d out of range\n", ssa_op->op1_def); |
291 | 0 | } |
292 | 525k | if (ssa->vars[ssa_op->op1_def].definition != i) { |
293 | 0 | FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n", |
294 | 0 | VAR(ssa_op->op1_def), INSTR(i)); |
295 | 0 | } |
296 | 525k | if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) { |
297 | 0 | FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n", |
298 | 0 | VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i)); |
299 | 0 | } |
300 | 525k | } |
301 | 9.93M | if (ssa_op->op2_def >= 0) { |
302 | 26.2k | if (ssa_op->op2_def >= ssa->vars_count) { |
303 | 0 | FAIL("op2 def %d out of range\n", ssa_op->op2_def); |
304 | 0 | } |
305 | 26.2k | if (ssa->vars[ssa_op->op2_def].definition != i) { |
306 | 0 | FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n", |
307 | 0 | VAR(ssa_op->op2_def), INSTR(i)); |
308 | 0 | } |
309 | 26.2k | if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) { |
310 | 0 | FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n", |
311 | 0 | VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i)); |
312 | 0 | } |
313 | 26.2k | } |
314 | 9.93M | if (ssa_op->result_def >= 0) { |
315 | 3.79M | if (ssa_op->result_def >= ssa->vars_count) { |
316 | 0 | FAIL("result def %d out of range\n", ssa_op->result_def); |
317 | 0 | } |
318 | 3.79M | if (ssa->vars[ssa_op->result_def].definition != i) { |
319 | 0 | FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n", |
320 | 0 | VAR(ssa_op->result_def), INSTR(i)); |
321 | 0 | } |
322 | 3.79M | if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) { |
323 | 0 | FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n", |
324 | 0 | VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i)); |
325 | 0 | } |
326 | 3.79M | } |
327 | 9.93M | } FOREACH_INSTR_NUM_END(); |
328 | | |
329 | | /* Phis */ |
330 | 2.11M | FOREACH_PHI(phi) { |
331 | 2.11M | uint32_t num_sources = NUM_PHI_SOURCES(phi); |
332 | 2.11M | for (i = 0; i < num_sources; i++) { |
333 | 1.29M | int source = phi->sources[i]; |
334 | 1.29M | if (source < 0) { |
335 | 0 | FAIL(VARFMT " negative source\n", VAR(phi->ssa_var)); |
336 | 0 | } |
337 | 1.29M | if (!is_in_phi_use_chain(ssa, source, phi)) { |
338 | 0 | FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source); |
339 | 0 | } |
340 | 1.29M | if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) { |
341 | 0 | FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var)); |
342 | 0 | } |
343 | 1.29M | if (phi->use_chains[i]) { |
344 | 202k | int j; |
345 | 387k | for (j = i + 1; j < num_sources; j++) { |
346 | 184k | if (phi->sources[j] == source && phi->use_chains[j]) { |
347 | 0 | FAIL("use chain for source " VARFMT " of phi " VARFMT |
348 | 0 | " at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j); |
349 | 0 | } |
350 | 184k | } |
351 | 202k | } |
352 | 1.29M | } |
353 | 2.11M | if (ssa->vars[phi->ssa_var].definition_phi != phi) { |
354 | 0 | FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var)); |
355 | 0 | } |
356 | 2.11M | } FOREACH_PHI_END(); |
357 | | |
358 | | /* Blocks */ |
359 | 1.36M | for (i = 0; i < cfg->blocks_count; i++) { |
360 | 1.02M | zend_basic_block *block = &cfg->blocks[i]; |
361 | 1.02M | int *predecessors = &cfg->predecessors[block->predecessor_offset]; |
362 | 1.02M | uint32_t j; |
363 | | |
364 | 1.02M | if (i != 0 && block->start < (block-1)->start + (block-1)->len) { |
365 | 0 | FAIL("Block %d start %d smaller previous end %d\n", |
366 | 0 | i, block->start, (block-1)->start + (block-1)->len); |
367 | 0 | } |
368 | 1.02M | if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) { |
369 | 0 | FAIL("Block %d end %d greater next start %d\n", |
370 | 0 | i, block->start + block->len, (block+1)->start); |
371 | 0 | } |
372 | | |
373 | 8.28M | for (j = block->start; j < block->start + block->len; j++) { |
374 | 7.26M | if (cfg->map[j] != i) { |
375 | 0 | FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i); |
376 | 0 | } |
377 | 7.26M | } |
378 | | |
379 | 1.02M | if (!(block->flags & ZEND_BB_REACHABLE)) { |
380 | 26.4k | if (ssa->blocks[i].phis) { |
381 | 0 | FAIL("Unreachable block %d has phis\n", i); |
382 | 0 | } |
383 | 26.4k | continue; |
384 | 26.4k | } |
385 | | |
386 | 1.96M | for (uint32_t s = 0; s < block->successors_count; s++) { |
387 | 970k | zend_basic_block *next_block; |
388 | 970k | if (block->successors[s] < 0) { |
389 | 0 | FAIL("Successor number %d of %d negative", s, i); |
390 | 0 | } |
391 | 970k | next_block = &cfg->blocks[block->successors[s]]; |
392 | 970k | if (!(next_block->flags & ZEND_BB_REACHABLE)) { |
393 | 0 | FAIL("Successor %d of %d not reachable\n", block->successors[s], i); |
394 | 0 | } |
395 | 970k | if (!is_in_predecessors(cfg, next_block, i)) { |
396 | 0 | FAIL("Block %d predecessors missing %d\n", block->successors[s], i); |
397 | 0 | } |
398 | 970k | } |
399 | | |
400 | 1.96M | for (j = 0; j < block->predecessors_count; j++) { |
401 | 969k | if (predecessors[j] >= 0) { |
402 | 969k | zend_basic_block *prev_block = &cfg->blocks[predecessors[j]]; |
403 | 969k | if (!(prev_block->flags & ZEND_BB_REACHABLE)) { |
404 | 0 | FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i); |
405 | 0 | } |
406 | 969k | if (!is_in_successors(prev_block, i)) { |
407 | 0 | FAIL("Block %d successors missing %d\n", predecessors[j], i); |
408 | 0 | } |
409 | 2.61M | for (uint32_t k = 0; k < block->predecessors_count; k++) { |
410 | 1.64M | if (k != j && predecessors[k] == predecessors[j]) { |
411 | 0 | FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]); |
412 | 0 | } |
413 | 1.64M | } |
414 | 969k | } |
415 | 969k | } |
416 | 999k | } |
417 | | |
418 | 339k | finish: |
419 | 339k | if (status == FAILURE) { |
420 | 0 | zend_dump_op_array(op_array, ZEND_DUMP_SSA, "at SSA integrity verification", ssa); |
421 | | ZEND_ASSERT(0 && "SSA integrity verification failed"); |
422 | 0 | } |
423 | 339k | } |