diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 91a142a9f863..816090aae6b7 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -74,12 +74,39 @@ } \ } while (0) +#define CHECK_SCC_VAR_OLD(var2) \ + do { \ + if (!ssa->vars[var2].no_val) { \ + if (dfs[var2] < 0) { \ + zend_ssa_check_scc_var_old(op_array, ssa, var2, index, dfs, root, stack); \ + } \ + if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \ + root[var] = root[var2]; \ + } \ + } \ + } while (0) + #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); \ + /* Need to come back from "recursion" */ \ + zend_worklist_stack_push(&var_stack, var); \ + zend_worklist_stack_push(&var_stack, use); \ + ZEND_WORKLIST_STACK_PUSH_PTR(&var_stack, p); \ + ZEND_WORKLIST_STACK_PUSH_PTR(&var_stack, sym_p); \ + /* "Recurse" on next var */ \ + var = var2; \ + dfs[var] = *index; \ + (*index)++; \ + root[var] = var; \ + use = ssa->vars[var].use_chain; \ + p = ssa->vars[var].phi_use_chain; \ + sym_p = ssa->vars[var].sym_use_chain; \ + goto recurse; \ } \ + /* Because the use, p, sym_p variables will remain the same from the last iteration, + * this will correctly execute after coming back from the "recursion". */ \ if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \ root[var] = root[var2]; \ } \ @@ -146,21 +173,25 @@ } while (0) -#define FOR_EACH_VAR_USAGE(_var, MACRO) \ +#define FOR_EACH_VAR_USAGE_EX(_var, MACRO) \ do { \ - zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ - int use = ssa->vars[_var].use_chain; \ while (use >= 0) { \ FOR_EACH_DEFINED_VAR(use, MACRO); \ use = zend_ssa_next_use(ssa->ops, _var, use); \ } \ - p = ssa->vars[_var].phi_use_chain; \ while (p) { \ MACRO(p->ssa_var); \ p = zend_ssa_next_use_phi(ssa, _var, p); \ } \ } while (0) +#define FOR_EACH_VAR_USAGE(_var, MACRO) \ + do { \ + int use = ssa->vars[_var].use_chain; \ + zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ + FOR_EACH_VAR_USAGE_EX(_var, MACRO); \ + } while (0) + static inline bool add_will_overflow(zend_long a, zend_long b) { return (b > 0 && a > ZEND_LONG_MAX - b) || (b < 0 && a < ZEND_LONG_MIN - b); @@ -172,23 +203,23 @@ 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) /* {{{ */ +static void zend_ssa_check_scc_var_old(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */ { #ifdef SYM_RANGE zend_ssa_phi *p; #endif - +// fprintf(stderr, "(old) enter %d\n", var); dfs[var] = *index; (*index)++; root[var] = var; - FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); + FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR_OLD); #ifdef SYM_RANGE /* Process symbolic control-flow constraints */ p = ssa->vars[var].sym_use_chain; while (p) { - CHECK_SCC_VAR(p->ssa_var); + CHECK_SCC_VAR_OLD(p->ssa_var); p = p->sym_use_chain; } #endif @@ -204,12 +235,83 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, ssa->vars[var2].scc = ssa->sccs; } ssa->sccs++; + //printf("(old) assign %d to %d\n", ssa->sccs, var); } else { zend_worklist_stack_push(stack, var); + //printf("(old) push %d (root[var]=%d, var=%d)\n", var, root[var], var); } } /* }}} */ +#if UINTPTR_MAX > UINT32_MAX +# define ZEND_WORKLIST_STACK_PUSH_PTR(stack, ptr) do { \ + zend_worklist_stack_push(&var_stack, (uintptr_t) ptr); \ + zend_worklist_stack_push(&var_stack, ((uintptr_t) (ptr)) >> 32); \ + } while (0) +# define ZEND_WORKLIST_STACK_POP_PTR(stack, res) do { \ + uintptr_t a = (unsigned int) zend_worklist_stack_pop(&var_stack); \ + uintptr_t b = (unsigned int) zend_worklist_stack_pop(&var_stack); \ + res = (void *) ((a << 32) | b); \ + } while (0) +#else +# define ZEND_WORKLIST_STACK_PUSH_PTR(stack, ptr) zend_worklist_stack_push(&var_stack, (intptr_t) (ptr)) +# define ZEND_WORKLIST_STACK_POP_PTR(stack, res) res = (void *) zend_worklist_stack_pop(&var_stack); +#endif + +static void zend_ssa_check_scc_var_new(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */ +{ + zend_worklist_stack var_stack; + ALLOCA_FLAG(var_stack_use_heap); + /* Manual recursion stack that tracks to which variable the recursion should "return" to. + * This cannot be larger than the variable count because every time a variable is visited it is marked as such. */ + ZEND_WORKLIST_STACK_ALLOCA(&var_stack, ssa->vars_count * (2 + sizeof(zend_ssa_phi *) / sizeof(int) * 2), var_stack_use_heap); + + dfs[var] = *index; + (*index)++; + root[var] = var; + + zend_worklist_stack_push(&var_stack, var); + zend_worklist_stack_push(&var_stack, ssa->vars[var].use_chain); + ZEND_WORKLIST_STACK_PUSH_PTR(&var_stack, ssa->vars[var].phi_use_chain); + ZEND_WORKLIST_STACK_PUSH_PTR(&var_stack, ssa->vars[var].sym_use_chain); + do { + const zend_ssa_phi *p, *sym_p; + ZEND_WORKLIST_STACK_POP_PTR(&var_stack, sym_p); + ZEND_WORKLIST_STACK_POP_PTR(&var_stack, p); + int use = zend_worklist_stack_pop(&var_stack); + var = zend_worklist_stack_pop(&var_stack); + +recurse:; + FOR_EACH_VAR_USAGE_EX(var, CHECK_SCC_VAR); + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + while (sym_p) { + CHECK_SCC_VAR(sym_p->ssa_var); + sym_p = sym_p->sym_use_chain; + } +#endif + + if (root[var] == var) { + ssa->vars[var].scc = ssa->sccs; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (dfs[var2] <= dfs[var]) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + } + ssa->sccs++; + } else { + zend_worklist_stack_push(stack, var); + } + } while (var_stack.len > 0); + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&var_stack, var_stack_use_heap); +} +/* }}} */ + ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { int index = 0, *dfs, *root; @@ -227,10 +329,35 @@ ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* /* Find SCCs using Tarjan's algorithm. */ 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); + zend_ssa_check_scc_var_old(op_array, ssa, j, &index, dfs, root, &stack); } } + int old_sccs = ssa->sccs; + stack.len = 0; + ssa->sccs = 0; + index = 0; + memset(dfs, -1, sizeof(int)*ssa->vars_count); + int *sccs1 = malloc(sizeof(int)*ssa->vars_count); + for (int i = 0; i < ssa->vars_count; i++) { + sccs1[i]=ssa->vars[i].scc; + ssa->vars[i].scc = -1; + } + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && dfs[j] < 0) { + zend_ssa_check_scc_var_new(op_array, ssa, j, &index, dfs, root, &stack); + } + } + if (old_sccs != ssa->sccs) { + fflush(stdout); + fprintf(stderr,"old=%d new=%d\n", old_sccs, ssa->sccs); + ZEND_ASSERT(old_sccs == ssa->sccs); + } + for (int i = 0; i < ssa->vars_count; i++) { + ZEND_ASSERT(sccs1[i]==ssa->vars[i].scc); + } + free(sccs1); + /* Revert SCC order. This results in a topological order. */ for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].scc >= 0) {