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