Skip to content

Commit 52bb39e

Browse files
committed
Backport implementation of iterative Pearce's SCC finding algoritm (#12528)
Fixes GH-11795
1 parent b3b46a4 commit 52bb39e

File tree

1 file changed

+261
-28
lines changed

1 file changed

+261
-28
lines changed

Diff for: Zend/Optimizer/zend_inference.c

+261-28
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,12 @@
7777
#define CHECK_SCC_VAR(var2) \
7878
do { \
7979
if (!ssa->vars[var2].no_val) { \
80-
if (dfs[var2] < 0) { \
81-
zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \
80+
if (ssa->vars[var2].scc < 0) { \
81+
zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \
8282
} \
83-
if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \
84-
root[var] = root[var2]; \
83+
if (ssa->vars[var2].scc < ssa->vars[var].scc) { \
84+
ssa->vars[var].scc = ssa->vars[var2].scc; \
85+
is_root = 0; \
8586
} \
8687
} \
8788
} while (0)
@@ -172,15 +173,17 @@ static inline bool sub_will_overflow(zend_long a, zend_long b) {
172173
}
173174
#endif
174175

175-
static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */
176+
#if 0
177+
/* Recursive Pearce's SCC algorithm implementation */
178+
static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */
176179
{
180+
int is_root = 1;
177181
#ifdef SYM_RANGE
178182
zend_ssa_phi *p;
179183
#endif
180184

181-
dfs[var] = *index;
185+
ssa->vars[var].scc = *index;
182186
(*index)++;
183-
root[var] = var;
184187

185188
FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR);
186189

@@ -193,17 +196,20 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa,
193196
}
194197
#endif
195198

196-
if (root[var] == var) {
197-
ssa->vars[var].scc = ssa->sccs;
199+
if (is_root) {
200+
ssa->sccs--;
198201
while (stack->len > 0) {
199202
int var2 = zend_worklist_stack_peek(stack);
200-
if (dfs[var2] <= dfs[var]) {
203+
if (ssa->vars[var2].scc < ssa->vars[var].scc) {
201204
break;
202205
}
203206
zend_worklist_stack_pop(stack);
204207
ssa->vars[var2].scc = ssa->sccs;
208+
(*index)--;
205209
}
206-
ssa->sccs++;
210+
ssa->vars[var].scc = ssa->sccs;
211+
ssa->vars[var].scc_entry = 1;
212+
(*index)--;
207213
} else {
208214
zend_worklist_stack_push(stack, var);
209215
}
@@ -212,50 +218,277 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa,
212218

213219
ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
214220
{
215-
int index = 0, *dfs, *root;
221+
int index = 0;
216222
zend_worklist_stack stack;
217223
int j;
218-
ALLOCA_FLAG(dfs_use_heap)
219-
ALLOCA_FLAG(root_use_heap)
220224
ALLOCA_FLAG(stack_use_heap)
221225

222-
dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap);
223-
memset(dfs, -1, sizeof(int) * ssa->vars_count);
224-
root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap);
225226
ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
226227

227-
/* Find SCCs using Tarjan's algorithm. */
228+
/* Find SCCs using Pearce's algorithm. */
229+
ssa->sccs = ssa->vars_count;
228230
for (j = 0; j < ssa->vars_count; j++) {
229-
if (!ssa->vars[j].no_val && dfs[j] < 0) {
230-
zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack);
231+
if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) {
232+
zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack);
233+
}
234+
}
235+
236+
if (ssa->sccs) {
237+
/* Shift SCC indexes. */
238+
for (j = 0; j < ssa->vars_count; j++) {
239+
if (ssa->vars[j].scc >= 0) {
240+
ssa->vars[j].scc -= ssa->sccs;
241+
}
231242
}
232243
}
244+
ssa->sccs = ssa->vars_count - ssa->sccs;
233245

234-
/* Revert SCC order. This results in a topological order. */
235246
for (j = 0; j < ssa->vars_count; j++) {
236247
if (ssa->vars[j].scc >= 0) {
237-
ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1);
248+
int var = j;
249+
FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
238250
}
239251
}
240252

253+
ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
254+
return SUCCESS;
255+
}
256+
/* }}} */
257+
258+
#else
259+
/* Iterative Pearce's SCC algorithm implementation */
260+
261+
typedef struct _zend_scc_iterator {
262+
int state;
263+
int last;
264+
union {
265+
int use;
266+
zend_ssa_phi *phi;
267+
};
268+
} zend_scc_iterator;
269+
270+
static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */
271+
{
272+
zend_ssa_phi *phi;
273+
int use, var2;
274+
275+
switch (iterator->state) {
276+
case 0: goto state_0;
277+
case 1: use = iterator->use; goto state_1;
278+
case 2: use = iterator->use; goto state_2;
279+
case 3: use = iterator->use; goto state_3;
280+
case 4: use = iterator->use; goto state_4;
281+
case 5: use = iterator->use; goto state_5;
282+
case 6: use = iterator->use; goto state_6;
283+
case 7: use = iterator->use; goto state_7;
284+
case 8: use = iterator->use; goto state_8;
285+
case 9: phi = iterator->phi; goto state_9;
286+
#ifdef SYM_RANGE
287+
case 10: phi = iterator->phi; goto state_10;
288+
#endif
289+
case 11: goto state_11;
290+
}
291+
292+
state_0:
293+
use = ssa->vars[var].use_chain;
294+
while (use >= 0) {
295+
iterator->use = use;
296+
var2 = ssa->ops[use].op1_def;
297+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
298+
iterator->state = 1;
299+
return var2;
300+
}
301+
state_1:
302+
var2 = ssa->ops[use].op2_def;
303+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
304+
iterator->state = 2;
305+
return var2;
306+
}
307+
state_2:
308+
var2 = ssa->ops[use].result_def;
309+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
310+
iterator->state = 3;
311+
return var2;
312+
}
313+
state_3:
314+
if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
315+
var2 = ssa->ops[use-1].op1_def;
316+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
317+
iterator->state = 4;
318+
return var2;
319+
}
320+
state_4:
321+
var2 = ssa->ops[use-1].op2_def;
322+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
323+
iterator->state = 5;
324+
return var2;
325+
}
326+
state_5:
327+
var2 = ssa->ops[use-1].result_def;
328+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
329+
iterator->state = 8;
330+
return var2;
331+
}
332+
} else if ((uint32_t)use+1 < op_array->last &&
333+
op_array->opcodes[use+1].opcode == ZEND_OP_DATA) {
334+
var2 = ssa->ops[use+1].op1_def;
335+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
336+
iterator->state = 6;
337+
return var2;
338+
}
339+
state_6:
340+
var2 = ssa->ops[use+1].op2_def;
341+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
342+
iterator->state = 7;
343+
return var2;
344+
}
345+
state_7:
346+
var2 = ssa->ops[use+1].result_def;
347+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
348+
iterator->state = 8;
349+
return var2;
350+
}
351+
}
352+
state_8:
353+
use = zend_ssa_next_use(ssa->ops, var, use);
354+
}
355+
356+
phi = ssa->vars[var].phi_use_chain;
357+
while (phi) {
358+
var2 = phi->ssa_var;
359+
if (!ssa->vars[var2].no_val) {
360+
iterator->state = 9;
361+
iterator->phi = phi;
362+
return var2;
363+
}
364+
state_9:
365+
phi = zend_ssa_next_use_phi(ssa, var, phi);
366+
}
367+
368+
#ifdef SYM_RANGE
369+
/* Process symbolic control-flow constraints */
370+
phi = ssa->vars[var].sym_use_chain;
371+
while (phi) {
372+
var2 = phi->ssa_var;
373+
if (!ssa->vars[var2].no_val) {
374+
iterator->state = 10;
375+
iterator->phi = phi;
376+
return var2;
377+
}
378+
state_10:
379+
phi = phi->sym_use_chain;
380+
}
381+
#endif
382+
383+
iterator->state = 11;
384+
state_11:
385+
return -1;
386+
}
387+
/* }}} */
388+
389+
static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack, zend_worklist_stack *vstack, zend_scc_iterator *iterators) /* {{{ */
390+
{
391+
restart:
392+
zend_worklist_stack_push(vstack, var);
393+
iterators[var].state = 0;
394+
iterators[var].last = -1;
395+
ssa->vars[var].scc_entry = 1;
396+
ssa->vars[var].scc = *index;
397+
(*index)++;
398+
399+
while (vstack->len > 0) {
400+
var = zend_worklist_stack_peek(vstack);
401+
while (1) {
402+
int var2;
403+
404+
if (iterators[var].last >= 0) {
405+
/* finish edge */
406+
var2 = iterators[var].last;
407+
if (ssa->vars[var2].scc < ssa->vars[var].scc) {
408+
ssa->vars[var].scc = ssa->vars[var2].scc;
409+
ssa->vars[var].scc_entry = 0;
410+
}
411+
}
412+
var2 = zend_scc_next(op_array, ssa, var, iterators + var);
413+
iterators[var].last = var2;
414+
if (var2 < 0) break;
415+
/* begin edge */
416+
if (ssa->vars[var2].scc < 0) {
417+
var = var2;
418+
goto restart;
419+
}
420+
}
421+
422+
/* finish visiting */
423+
zend_worklist_stack_pop(vstack);
424+
if (ssa->vars[var].scc_entry) {
425+
ssa->sccs--;
426+
while (stack->len > 0) {
427+
int var2 = zend_worklist_stack_peek(stack);
428+
if (ssa->vars[var2].scc < ssa->vars[var].scc) {
429+
break;
430+
}
431+
zend_worklist_stack_pop(stack);
432+
ssa->vars[var2].scc = ssa->sccs;
433+
(*index)--;
434+
}
435+
ssa->vars[var].scc = ssa->sccs;
436+
(*index)--;
437+
} else {
438+
zend_worklist_stack_push(stack, var);
439+
}
440+
}
441+
}
442+
/* }}} */
443+
444+
ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
445+
{
446+
int index = 0;
447+
zend_worklist_stack stack, vstack;
448+
zend_scc_iterator *iterators;
449+
int j;
450+
ALLOCA_FLAG(stack_use_heap)
451+
ALLOCA_FLAG(vstack_use_heap)
452+
ALLOCA_FLAG(iterators_use_heap)
453+
454+
iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap);
455+
ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap);
456+
ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
457+
458+
/* Find SCCs using Pearce's algorithm. */
459+
ssa->sccs = ssa->vars_count;
460+
for (j = 0; j < ssa->vars_count; j++) {
461+
if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) {
462+
zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators);
463+
}
464+
}
465+
466+
if (ssa->sccs) {
467+
/* Shift SCC indexes. */
468+
for (j = 0; j < ssa->vars_count; j++) {
469+
if (ssa->vars[j].scc >= 0) {
470+
ssa->vars[j].scc -= ssa->sccs;
471+
}
472+
}
473+
}
474+
ssa->sccs = ssa->vars_count - ssa->sccs;
475+
241476
for (j = 0; j < ssa->vars_count; j++) {
242477
if (ssa->vars[j].scc >= 0) {
243478
int var = j;
244-
if (root[j] == j) {
245-
ssa->vars[j].scc_entry = 1;
246-
}
247479
FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
248480
}
249481
}
250482

251483
ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
252-
free_alloca(root, root_use_heap);
253-
free_alloca(dfs, dfs_use_heap);
254-
484+
ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap);
485+
free_alloca(iterators, iterators_use_heap);
255486
return SUCCESS;
256487
}
257488
/* }}} */
258489

490+
#endif
491+
259492
ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
260493
{
261494
zend_ssa_var *ssa_vars = ssa->vars;

0 commit comments

Comments
 (0)