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
213219ZEND_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+
259492ZEND_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