Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 137 additions & 10 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]; \
} \
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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) {
Expand Down