From cc45324dc34d0f118796e0df6b56a212d86298b9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 18 May 2023 15:56:12 +0200 Subject: [PATCH 1/5] import --- Zend/Optimizer/zend_inference.c | 94 ++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 8 deletions(-) diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 91a142a9f863..138c799bf6b5 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -38,6 +38,7 @@ /* Whether to handle symbolic range constraints */ #define SYM_RANGE +#undef SYM_RANGE /* Whether to handle negative range constraints */ /* Negative range inference is buggy, so disabled for now */ @@ -74,11 +75,31 @@ } \ } 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); \ + /* "Recurse" on next var */ \ + var = var2; \ + /* Recursion case */ \ + dfs[var] = *index; \ + (*index)++; \ + root[var] = var; \ + goto recurse; \ } \ if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \ root[var] = root[var2]; \ @@ -148,13 +169,12 @@ #define FOR_EACH_VAR_USAGE(_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; \ + zend_ssa_phi *p = ssa->vars[_var].phi_use_chain; \ while (p) { \ MACRO(p->ssa_var); \ p = zend_ssa_next_use_phi(ssa, _var, p); \ @@ -172,23 +192,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 +224,51 @@ 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); } } /* }}} */ +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 */ + ZEND_WORKLIST_STACK_ALLOCA(&var_stack, ssa->vars_count, var_stack_use_heap); + + zend_worklist_stack_push(&var_stack, var); + + do { + var = zend_worklist_stack_pop(&var_stack); + +recurse:; + // TODO: support sym range + + FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); // TODO: prevent always starting from the start again + + 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,9 +286,28 @@ 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); + for (int i = 0; i < ssa->vars_count; i++) { + 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,"%d %d\n", old_sccs, ssa->sccs); + ZEND_ASSERT(old_sccs == ssa->sccs); + } /* Revert SCC order. This results in a topological order. */ for (j = 0; j < ssa->vars_count; j++) { From 4826fbed5789bd6c0746ddb8fb7883035e4e6d99 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 18 May 2023 16:23:12 +0200 Subject: [PATCH 2/5] correctness stuff --- Zend/Optimizer/zend_inference.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 138c799bf6b5..f192241467f2 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -95,7 +95,6 @@ zend_worklist_stack_push(&var_stack, var); \ /* "Recurse" on next var */ \ var = var2; \ - /* Recursion case */ \ dfs[var] = *index; \ (*index)++; \ root[var] = var; \ @@ -236,11 +235,16 @@ static void zend_ssa_check_scc_var_new(const zend_op_array *op_array, zend_ssa * { zend_worklist_stack var_stack; ALLOCA_FLAG(var_stack_use_heap); - /* Manual recursion stack that tracks to which variable the recursion should "return" to */ + /* 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, var_stack_use_heap); zend_worklist_stack_push(&var_stack, var); + dfs[var] = *index; + (*index)++; + root[var] = var; + do { var = zend_worklist_stack_pop(&var_stack); @@ -295,7 +299,9 @@ ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* 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++) { @@ -308,6 +314,10 @@ ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* fprintf(stderr,"%d %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++) { From 0213c4472d0ae06f78650a5202010f4604a6d7e7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 18 May 2023 17:29:25 +0200 Subject: [PATCH 3/5] wip --- Zend/Optimizer/zend_inference.c | 59 +++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index f192241467f2..672ce9dd735e 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -38,7 +38,6 @@ /* Whether to handle symbolic range constraints */ #define SYM_RANGE -#undef SYM_RANGE /* Whether to handle negative range constraints */ /* Negative range inference is buggy, so disabled for now */ @@ -93,13 +92,21 @@ if (dfs[var2] < 0) { \ /* 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]; \ } \ @@ -166,20 +173,25 @@ } while (0) -#define FOR_EACH_VAR_USAGE(_var, MACRO) \ +#define FOR_EACH_VAR_USAGE_EX(_var, MACRO) \ do { \ - 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); \ } \ - zend_ssa_phi *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); @@ -231,27 +243,54 @@ static void zend_ssa_check_scc_var_old(const zend_op_array *op_array, zend_ssa * } /* }}} */ +#if UINTPTR_MAX > 0xFFFFFFFF +# define ZEND_WORKLIST_STACK_PUSH_PTR(stack, ptr) do { \ + zend_worklist_stack_push(&var_stack, (intptr_t) (ptr)); \ + zend_worklist_stack_push(&var_stack, (intptr_t) (ptr) >> 32); \ + } while (0) +# define ZEND_WORKLIST_STACK_POP_PTR(stack, res) do { \ + intptr_t a = zend_worklist_stack_pop(&var_stack); \ + intptr_t b = 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, var_stack_use_heap); - - zend_worklist_stack_push(&var_stack, var); + 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:; - // TODO: support sym range + FOR_EACH_VAR_USAGE_EX(var, CHECK_SCC_VAR); - FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); // TODO: prevent always starting from the start again +#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; @@ -311,7 +350,7 @@ ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* } if (old_sccs != ssa->sccs) { fflush(stdout); - fprintf(stderr,"%d %d\n", old_sccs, ssa->sccs); + 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++) { From 89af18c3b6210ac319c5ad4d3cc5fcb0830564f0 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 18 May 2023 18:20:45 +0200 Subject: [PATCH 4/5] Windows fix? --- Zend/Optimizer/zend_inference.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 672ce9dd735e..4a35de242246 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -243,14 +243,14 @@ static void zend_ssa_check_scc_var_old(const zend_op_array *op_array, zend_ssa * } /* }}} */ -#if UINTPTR_MAX > 0xFFFFFFFF +#if UINTPTR_MAX > UINT32_MAX # define ZEND_WORKLIST_STACK_PUSH_PTR(stack, ptr) do { \ - zend_worklist_stack_push(&var_stack, (intptr_t) (ptr)); \ - zend_worklist_stack_push(&var_stack, (intptr_t) (ptr) >> 32); \ + 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 { \ - intptr_t a = zend_worklist_stack_pop(&var_stack); \ - intptr_t b = zend_worklist_stack_pop(&var_stack); \ + uintptr_t a = zend_worklist_stack_pop(&var_stack); \ + uintptr_t b = zend_worklist_stack_pop(&var_stack); \ res = (void *) ((a << 32) | b); \ } while (0) #else From e31d4cc4f58a1a0c76201c48212c8ddf1ec62df2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 19 May 2023 00:23:36 +0200 Subject: [PATCH 5/5] fix --- Zend/Optimizer/zend_inference.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 4a35de242246..816090aae6b7 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -245,12 +245,12 @@ static void zend_ssa_check_scc_var_old(const zend_op_array *op_array, zend_ssa * #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); \ + 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 = zend_worklist_stack_pop(&var_stack); \ - uintptr_t b = zend_worklist_stack_pop(&var_stack); \ + 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