diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 960e82e842f3a..12f82f696ee69 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -77,11 +77,12 @@ #define CHECK_SCC_VAR(var2) \ do { \ if (!ssa->vars[var2].no_val) { \ - if (dfs[var2] < 0) { \ - zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \ + if (ssa->vars[var2].scc < 0) { \ + zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \ } \ - if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \ - root[var] = root[var2]; \ + if (ssa->vars[var2].scc < ssa->vars[var].scc) { \ + ssa->vars[var].scc = ssa->vars[var2].scc; \ + is_root = 0; \ } \ } \ } while (0) @@ -172,15 +173,17 @@ static inline bool sub_will_overflow(zend_long a, zend_long b) { } #endif -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) /* {{{ */ +#if 0 +/* Recursive Pearce's SCC algorithm implementation */ +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */ { + int is_root = 1; #ifdef SYM_RANGE zend_ssa_phi *p; #endif - dfs[var] = *index; + ssa->vars[var].scc = *index; (*index)++; - root[var] = var; FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); @@ -193,17 +196,20 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, } #endif - if (root[var] == var) { - ssa->vars[var].scc = ssa->sccs; + if (is_root) { + ssa->sccs--; while (stack->len > 0) { int var2 = zend_worklist_stack_peek(stack); - if (dfs[var2] <= dfs[var]) { + if (ssa->vars[var2].scc < ssa->vars[var].scc) { break; } zend_worklist_stack_pop(stack); ssa->vars[var2].scc = ssa->sccs; + (*index)--; } - ssa->sccs++; + ssa->vars[var].scc = ssa->sccs; + ssa->vars[var].scc_entry = 1; + (*index)--; } else { zend_worklist_stack_push(stack, var); } @@ -212,48 +218,275 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { - int index = 0, *dfs, *root; + int index = 0; zend_worklist_stack stack; int j; - ALLOCA_FLAG(dfs_use_heap) - ALLOCA_FLAG(root_use_heap) ALLOCA_FLAG(stack_use_heap) - dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap); - memset(dfs, -1, sizeof(int) * ssa->vars_count); - root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap); ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - /* Find SCCs using Tarjan's algorithm. */ + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && dfs[j] < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack); + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack); + } + } + + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } } } + ssa->sccs = ssa->vars_count - ssa->sccs; - /* Revert SCC order. This results in a topological order. */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1); + int var = j; + FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); + } + } + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); +} +/* }}} */ + +#else +/* Iterative Pearce's SCC algorithm implementation */ + +typedef struct _zend_scc_iterator { + int state; + int last; + union { + int use; + zend_ssa_phi *phi; + }; +} zend_scc_iterator; + +static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */ +{ + zend_ssa_phi *phi; + int use, var2; + + switch (iterator->state) { + case 0: goto state_0; + case 1: use = iterator->use; goto state_1; + case 2: use = iterator->use; goto state_2; + case 3: use = iterator->use; goto state_3; + case 4: use = iterator->use; goto state_4; + case 5: use = iterator->use; goto state_5; + case 6: use = iterator->use; goto state_6; + case 7: use = iterator->use; goto state_7; + case 8: use = iterator->use; goto state_8; + case 9: phi = iterator->phi; goto state_9; +#ifdef SYM_RANGE + case 10: phi = iterator->phi; goto state_10; +#endif + case 11: goto state_11; + } + +state_0: + use = ssa->vars[var].use_chain; + while (use >= 0) { + iterator->use = use; + var2 = ssa->ops[use].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 1; + return var2; + } +state_1: + var2 = ssa->ops[use].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 2; + return var2; } +state_2: + var2 = ssa->ops[use].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 3; + return var2; + } +state_3: + if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use-1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 4; + return var2; + } +state_4: + var2 = ssa->ops[use-1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 5; + return var2; + } +state_5: + var2 = ssa->ops[use-1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } else if ((uint32_t)use+1 < op_array->last && + op_array->opcodes[use+1].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use+1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 6; + return var2; + } +state_6: + var2 = ssa->ops[use+1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 7; + return var2; + } +state_7: + var2 = ssa->ops[use+1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } +state_8: + use = zend_ssa_next_use(ssa->ops, var, use); } + phi = ssa->vars[var].phi_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 9; + iterator->phi = phi; + return var2; + } +state_9: + phi = zend_ssa_next_use_phi(ssa, var, phi); + } + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + phi = ssa->vars[var].sym_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 10; + iterator->phi = phi; + return var2; + } +state_10: + phi = phi->sym_use_chain; + } +#endif + + iterator->state = 11; +state_11: + return -1; +} +/* }}} */ + +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) /* {{{ */ +{ +restart: + zend_worklist_stack_push(vstack, var); + iterators[var].state = 0; + iterators[var].last = -1; + ssa->vars[var].scc_entry = 1; + ssa->vars[var].scc = *index; + (*index)++; + + while (vstack->len > 0) { + var = zend_worklist_stack_peek(vstack); + while (1) { + int var2; + + if (iterators[var].last >= 0) { + /* finish edge */ + var2 = iterators[var].last; + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + ssa->vars[var].scc = ssa->vars[var2].scc; + ssa->vars[var].scc_entry = 0; + } + } + var2 = zend_scc_next(op_array, ssa, var, iterators + var); + iterators[var].last = var2; + if (var2 < 0) break; + /* begin edge */ + if (ssa->vars[var2].scc < 0) { + var = var2; + goto restart; + } + } + + /* finish visiting */ + zend_worklist_stack_pop(vstack); + if (ssa->vars[var].scc_entry) { + ssa->sccs--; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + (*index)--; + } + ssa->vars[var].scc = ssa->sccs; + (*index)--; + } else { + zend_worklist_stack_push(stack, var); + } + } +} +/* }}} */ + +ZEND_API void zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ +{ + int index = 0; + zend_worklist_stack stack, vstack; + zend_scc_iterator *iterators; + int j; + ALLOCA_FLAG(stack_use_heap) + ALLOCA_FLAG(vstack_use_heap) + ALLOCA_FLAG(iterators_use_heap) + + iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); + + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators); + } + } + + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].scc >= 0) { int var = j; - if (root[j] == j) { - ssa->vars[j].scc_entry = 1; - } FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); } } ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); - free_alloca(root, root_use_heap); - free_alloca(dfs, dfs_use_heap); + ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap); + free_alloca(iterators, iterators_use_heap); } /* }}} */ +#endif + ZEND_API void zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { zend_ssa_var *ssa_vars = ssa->vars;