Skip to content

Commit bd185c3

Browse files
authored
Implement iterative Pearce's SCC finding algoritm (#12528)
* Switch to Pearce's SCC algorithm * Implement iterative Pearce's SCC algorithm * Wrap "goto" with "#ifdef SYM_RANGE"
1 parent 7a594af commit bd185c3

File tree

1 file changed

+260
-27
lines changed

1 file changed

+260
-27
lines changed

Zend/Optimizer/zend_inference.c

+260-27
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,48 +218,275 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa,
212218

213219
ZEND_API void 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);
250+
}
251+
}
252+
253+
ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
254+
}
255+
/* }}} */
256+
257+
#else
258+
/* Iterative Pearce's SCC algorithm implementation */
259+
260+
typedef struct _zend_scc_iterator {
261+
int state;
262+
int last;
263+
union {
264+
int use;
265+
zend_ssa_phi *phi;
266+
};
267+
} zend_scc_iterator;
268+
269+
static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */
270+
{
271+
zend_ssa_phi *phi;
272+
int use, var2;
273+
274+
switch (iterator->state) {
275+
case 0: goto state_0;
276+
case 1: use = iterator->use; goto state_1;
277+
case 2: use = iterator->use; goto state_2;
278+
case 3: use = iterator->use; goto state_3;
279+
case 4: use = iterator->use; goto state_4;
280+
case 5: use = iterator->use; goto state_5;
281+
case 6: use = iterator->use; goto state_6;
282+
case 7: use = iterator->use; goto state_7;
283+
case 8: use = iterator->use; goto state_8;
284+
case 9: phi = iterator->phi; goto state_9;
285+
#ifdef SYM_RANGE
286+
case 10: phi = iterator->phi; goto state_10;
287+
#endif
288+
case 11: goto state_11;
289+
}
290+
291+
state_0:
292+
use = ssa->vars[var].use_chain;
293+
while (use >= 0) {
294+
iterator->use = use;
295+
var2 = ssa->ops[use].op1_def;
296+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
297+
iterator->state = 1;
298+
return var2;
299+
}
300+
state_1:
301+
var2 = ssa->ops[use].op2_def;
302+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
303+
iterator->state = 2;
304+
return var2;
238305
}
306+
state_2:
307+
var2 = ssa->ops[use].result_def;
308+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
309+
iterator->state = 3;
310+
return var2;
311+
}
312+
state_3:
313+
if (op_array->opcodes[use].opcode == ZEND_OP_DATA) {
314+
var2 = ssa->ops[use-1].op1_def;
315+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
316+
iterator->state = 4;
317+
return var2;
318+
}
319+
state_4:
320+
var2 = ssa->ops[use-1].op2_def;
321+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
322+
iterator->state = 5;
323+
return var2;
324+
}
325+
state_5:
326+
var2 = ssa->ops[use-1].result_def;
327+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
328+
iterator->state = 8;
329+
return var2;
330+
}
331+
} else if ((uint32_t)use+1 < op_array->last &&
332+
op_array->opcodes[use+1].opcode == ZEND_OP_DATA) {
333+
var2 = ssa->ops[use+1].op1_def;
334+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
335+
iterator->state = 6;
336+
return var2;
337+
}
338+
state_6:
339+
var2 = ssa->ops[use+1].op2_def;
340+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
341+
iterator->state = 7;
342+
return var2;
343+
}
344+
state_7:
345+
var2 = ssa->ops[use+1].result_def;
346+
if (var2 >= 0 && !ssa->vars[var2].no_val) {
347+
iterator->state = 8;
348+
return var2;
349+
}
350+
}
351+
state_8:
352+
use = zend_ssa_next_use(ssa->ops, var, use);
239353
}
240354

355+
phi = ssa->vars[var].phi_use_chain;
356+
while (phi) {
357+
var2 = phi->ssa_var;
358+
if (!ssa->vars[var2].no_val) {
359+
iterator->state = 9;
360+
iterator->phi = phi;
361+
return var2;
362+
}
363+
state_9:
364+
phi = zend_ssa_next_use_phi(ssa, var, phi);
365+
}
366+
367+
#ifdef SYM_RANGE
368+
/* Process symbolic control-flow constraints */
369+
phi = ssa->vars[var].sym_use_chain;
370+
while (phi) {
371+
var2 = phi->ssa_var;
372+
if (!ssa->vars[var2].no_val) {
373+
iterator->state = 10;
374+
iterator->phi = phi;
375+
return var2;
376+
}
377+
state_10:
378+
phi = phi->sym_use_chain;
379+
}
380+
#endif
381+
382+
iterator->state = 11;
383+
state_11:
384+
return -1;
385+
}
386+
/* }}} */
387+
388+
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) /* {{{ */
389+
{
390+
restart:
391+
zend_worklist_stack_push(vstack, var);
392+
iterators[var].state = 0;
393+
iterators[var].last = -1;
394+
ssa->vars[var].scc_entry = 1;
395+
ssa->vars[var].scc = *index;
396+
(*index)++;
397+
398+
while (vstack->len > 0) {
399+
var = zend_worklist_stack_peek(vstack);
400+
while (1) {
401+
int var2;
402+
403+
if (iterators[var].last >= 0) {
404+
/* finish edge */
405+
var2 = iterators[var].last;
406+
if (ssa->vars[var2].scc < ssa->vars[var].scc) {
407+
ssa->vars[var].scc = ssa->vars[var2].scc;
408+
ssa->vars[var].scc_entry = 0;
409+
}
410+
}
411+
var2 = zend_scc_next(op_array, ssa, var, iterators + var);
412+
iterators[var].last = var2;
413+
if (var2 < 0) break;
414+
/* begin edge */
415+
if (ssa->vars[var2].scc < 0) {
416+
var = var2;
417+
goto restart;
418+
}
419+
}
420+
421+
/* finish visiting */
422+
zend_worklist_stack_pop(vstack);
423+
if (ssa->vars[var].scc_entry) {
424+
ssa->sccs--;
425+
while (stack->len > 0) {
426+
int var2 = zend_worklist_stack_peek(stack);
427+
if (ssa->vars[var2].scc < ssa->vars[var].scc) {
428+
break;
429+
}
430+
zend_worklist_stack_pop(stack);
431+
ssa->vars[var2].scc = ssa->sccs;
432+
(*index)--;
433+
}
434+
ssa->vars[var].scc = ssa->sccs;
435+
(*index)--;
436+
} else {
437+
zend_worklist_stack_push(stack, var);
438+
}
439+
}
440+
}
441+
/* }}} */
442+
443+
ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
444+
{
445+
int index = 0;
446+
zend_worklist_stack stack, vstack;
447+
zend_scc_iterator *iterators;
448+
int j;
449+
ALLOCA_FLAG(stack_use_heap)
450+
ALLOCA_FLAG(vstack_use_heap)
451+
ALLOCA_FLAG(iterators_use_heap)
452+
453+
iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap);
454+
ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap);
455+
ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap);
456+
457+
/* Find SCCs using Pearce's algorithm. */
458+
ssa->sccs = ssa->vars_count;
459+
for (j = 0; j < ssa->vars_count; j++) {
460+
if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) {
461+
zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators);
462+
}
463+
}
464+
465+
if (ssa->sccs) {
466+
/* Shift SCC indexes. */
467+
for (j = 0; j < ssa->vars_count; j++) {
468+
if (ssa->vars[j].scc >= 0) {
469+
ssa->vars[j].scc -= ssa->sccs;
470+
}
471+
}
472+
}
473+
ssa->sccs = ssa->vars_count - ssa->sccs;
474+
241475
for (j = 0; j < ssa->vars_count; j++) {
242476
if (ssa->vars[j].scc >= 0) {
243477
int var = j;
244-
if (root[j] == j) {
245-
ssa->vars[j].scc_entry = 1;
246-
}
247478
FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY);
248479
}
249480
}
250481

251482
ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap);
252-
free_alloca(root, root_use_heap);
253-
free_alloca(dfs, dfs_use_heap);
483+
ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap);
484+
free_alloca(iterators, iterators_use_heap);
254485
}
255486
/* }}} */
256487

488+
#endif
489+
257490
ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
258491
{
259492
zend_ssa_var *ssa_vars = ssa->vars;

0 commit comments

Comments
 (0)