diff options
Diffstat (limited to 'ext/opcache/Optimizer/zend_ssa.c')
| -rw-r--r-- | ext/opcache/Optimizer/zend_ssa.c | 371 |
1 files changed, 187 insertions, 184 deletions
diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 0da793918c..6f1fff961c 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -23,19 +23,47 @@ #include "zend_dump.h" #include "zend_inference.h" -static int needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */ -{ - if (from == to || ssa->cfg.blocks[to].predecessors_count != 1) { - zend_ssa_phi *p = ssa->blocks[to].phis; - while (p) { - if (p->pi < 0 && p->var == var) { - return 1; - } - p = p->next; +static zend_bool dominates(const zend_basic_block *blocks, int a, int b) { + while (blocks[b].level > blocks[a].level) { + b = blocks[b].idom; + } + return a == b; +} + +static zend_bool dominates_other_predecessors( + const zend_cfg *cfg, const zend_basic_block *block, int check, int exclude) { + int i; + for (i = 0; i < block->predecessors_count; i++) { + int predecessor = cfg->predecessors[block->predecessor_offset + i]; + if (predecessor != exclude && !dominates(cfg->blocks, check, predecessor)) { + return 0; } + } + return 1; +} + +static zend_bool needs_pi(const zend_op_array *op_array, zend_dfg *dfg, zend_ssa *ssa, int from, int to, int var) /* {{{ */ +{ + zend_basic_block *from_block, *to_block; + int other_successor; + + if (!DFG_ISSET(dfg->in, dfg->size, to, var)) { + /* Variable is not live, certainly won't benefit from pi */ return 0; } - return DFG_ISSET(dfg->in, dfg->size, to, var); + + to_block = &ssa->cfg.blocks[to]; + if (to_block->predecessors_count == 1) { + /* Always place pi if one predecessor (an if branch) */ + return 1; + } + + /* Check that the other successor of the from block does not dominate all other predecessors. + * If it does, we'd probably end up annihilating a positive+negative pi assertion. */ + from_block = &ssa->cfg.blocks[from]; + other_successor = from_block->successors[0] == to + ? from_block->successors[1] : from_block->successors[0]; + return !dominates_other_predecessors(&ssa->cfg, to_block, other_successor, from); } /* }}} */ @@ -62,6 +90,18 @@ static zend_ssa_phi *add_pi( phi->next = ssa->blocks[to].phis; ssa->blocks[to].phis = phi; + /* Block "to" now defines "var" via the pi statement, so add it to the "def" set. Note that + * this is not entirely accurate, because the pi is actually placed along the edge from->to. + * If there is a back-edge to "to" this may result in non-minimal SSA form. */ + DFG_SET(dfg->def, dfg->size, to, var); + + /* If there are multiple predecessors in the target block, we need to place a phi there. + * However this can (generally) not be expressed in terms of dominance frontiers, so place it + * explicitly. dfg->use here really is dfg->phi, we're reusing the set. */ + if (ssa->cfg.blocks[to].predecessors_count > 1) { + DFG_SET(dfg->use, dfg->size, to, var); + } + return phi; } /* }}} */ @@ -70,16 +110,17 @@ static void pi_range( zend_ssa_phi *phi, int min_var, int max_var, zend_long min, zend_long max, char underflow, char overflow, char negative) /* {{{ */ { - phi->constraint.min_var = min_var; - phi->constraint.max_var = max_var; - phi->constraint.min_ssa_var = -1; - phi->constraint.max_ssa_var = -1; - phi->constraint.range.min = min; - phi->constraint.range.max = max; - phi->constraint.range.underflow = underflow; - phi->constraint.range.overflow = overflow; - phi->constraint.negative = negative ? NEG_INIT : NEG_NONE; - phi->constraint.type_mask = (uint32_t) -1; + zend_ssa_range_constraint *constraint = &phi->constraint.range; + constraint->min_var = min_var; + constraint->max_var = max_var; + constraint->min_ssa_var = -1; + constraint->max_ssa_var = -1; + constraint->range.min = min; + constraint->range.max = max; + constraint->range.underflow = underflow; + constraint->range.overflow = overflow; + constraint->negative = negative ? NEG_INIT : NEG_NONE; + phi->has_range_constraint = 1; } /* }}} */ @@ -97,10 +138,12 @@ static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) { } static void pi_type_mask(zend_ssa_phi *phi, uint32_t type_mask) { - phi->constraint.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN; - phi->constraint.type_mask |= type_mask; + phi->has_range_constraint = 0; + phi->constraint.type.ce = NULL; + phi->constraint.type.type_mask = MAY_BE_REF|MAY_BE_RC1|MAY_BE_RCN; + phi->constraint.type.type_mask |= type_mask; if (type_mask & MAY_BE_NULL) { - phi->constraint.type_mask |= MAY_BE_UNDEF; + phi->constraint.type.type_mask |= MAY_BE_UNDEF; } } static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) { @@ -119,7 +162,7 @@ static inline uint32_t mask_for_type_check(uint32_t type) { /* We can interpret $a + 5 == 0 as $a = 0 - 5, i.e. shift the adjustment to the other operand. * This negated adjustment is what is written into the "adjustment" parameter. */ -static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) +static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_flags, zend_op *opline, uint32_t var_num, zend_long *adjustment) /* {{{ */ { zend_op *op = opline; while (op != op_array->opcodes) { @@ -164,6 +207,7 @@ static int find_adjusted_tmp_var(const zend_op_array *op_array, uint32_t build_f } return -1; } +/* }}} */ static inline zend_bool add_will_overflow(zend_long a, zend_long b) { return (b > 0 && a > ZEND_LONG_MAX - b) @@ -179,17 +223,17 @@ static inline zend_bool sub_will_overflow(zend_long a, zend_long b) { * Order of Phis is importent, Pis must be placed before Phis */ static void place_essa_pis( - zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, - zend_dfg *dfg) { + zend_arena **arena, const zend_script *script, const zend_op_array *op_array, + uint32_t build_flags, zend_ssa *ssa, zend_dfg *dfg) /* {{{ */ { zend_basic_block *blocks = ssa->cfg.blocks; int j, blocks_count = ssa->cfg.blocks_count; for (j = 0; j < blocks_count; j++) { zend_ssa_phi *pi; - zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].end; + zend_op *opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1; int bt; /* successor block number if a condition is true */ int bf; /* successor block number if a condition is false */ - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { + if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0 || blocks[j].len == 0) { continue; } /* the last instruction of basic block is conditional branch, @@ -198,12 +242,12 @@ static void place_essa_pis( switch (opline->opcode) { case ZEND_JMPZ: case ZEND_JMPZNZ: - bf = ssa->cfg.blocks[j].successors[0]; - bt = ssa->cfg.blocks[j].successors[1]; + bf = blocks[j].successors[0]; + bt = blocks[j].successors[1]; break; case ZEND_JMPNZ: - bt = ssa->cfg.blocks[j].successors[0]; - bf = ssa->cfg.blocks[j].successors[1]; + bt = blocks[j].successors[0]; + bf = blocks[j].successors[1]; break; default: continue; @@ -442,9 +486,27 @@ static void place_essa_pis( pi_not_type_mask(pi, type_mask); } } + } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_INSTANCEOF && + opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV && + (opline-1)->op2_type == IS_CONST) { + int var = EX_VAR_TO_NUM((opline-1)->op1.var); + zend_string *lcname = Z_STR_P(CRT_CONSTANT((opline-1)->op2) + 1); + zend_class_entry *ce = zend_hash_find_ptr(&script->class_table, lcname); + if (!ce) { + ce = zend_hash_find_ptr(CG(class_table), lcname); + if (!ce || ce->type != ZEND_INTERNAL_CLASS) { + continue; + } + } + + if ((pi = add_pi(arena, op_array, dfg, ssa, j, bt, var))) { + pi_type_mask(pi, MAY_BE_OBJECT); + pi->constraint.type.ce = ce; + } } } } +/* }}} */ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, int *var, int n) /* {{{ */ { @@ -453,8 +515,7 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, zend_ssa_op *ssa_ops = ssa->ops; int ssa_vars_count = ssa->vars_count; int i, j; - uint32_t k; - zend_op *opline; + zend_op *opline, *end; int *tmp = NULL; ALLOCA_FLAG(use_heap); @@ -479,12 +540,13 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, } while (phi); } - for (k = blocks[n].start; k <= blocks[n].end; k++) { - opline = op_array->opcodes + k; + opline = op_array->opcodes + blocks[n].start; + end = opline + blocks[n].len; + for (; opline < end; opline++) { + uint32_t k = opline - op_array->opcodes; if (opline->opcode != ZEND_OP_DATA) { zend_op *next = opline + 1; - if (k < blocks[n].end && - next->opcode == ZEND_OP_DATA) { + if (next < end && next->opcode == ZEND_OP_DATA) { if (next->op1_type == IS_CV) { ssa_ops[k + 1].op1_use = var[EX_VAR_TO_NUM(next->op1.var)]; //USE_SSA_VAR(next->op1.var); @@ -583,8 +645,10 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, } break; case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: case ZEND_SEND_VAR_EX: case ZEND_SEND_REF: + case ZEND_SEND_UNPACK: case ZEND_FE_RESET_RW: //TODO: ??? if (opline->op1_type == IS_CV) { @@ -664,6 +728,15 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; ssa_vars_count++; } + break; + case ZEND_VERIFY_RETURN_TYPE: + if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) { + ssa_ops[k].op1_def = ssa_vars_count; + var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; + ssa_vars_count++; + //NEW_SSA_VAR(opline->op1.var) + } + break; default: break; } @@ -688,11 +761,13 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, for (p = ssa_blocks[succ].phis; p; p = p->next) { if (p->pi == n) { /* e-SSA Pi */ - if (p->constraint.min_var >= 0) { - p->constraint.min_ssa_var = var[p->constraint.min_var]; - } - if (p->constraint.max_var >= 0) { - p->constraint.max_ssa_var = var[p->constraint.max_var]; + if (p->has_range_constraint) { + if (p->constraint.range.min_var >= 0) { + p->constraint.range.min_ssa_var = var[p->constraint.range.min_var]; + } + if (p->constraint.range.max_var >= 0) { + p->constraint.range.max_ssa_var = var[p->constraint.range.max_var]; + } } for (j = 0; j < blocks[succ].predecessors_count; j++) { p->sources[j] = var[p->var]; @@ -749,18 +824,18 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, } /* }}} */ -int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */ +int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa, uint32_t *func_flags) /* {{{ */ { zend_basic_block *blocks = ssa->cfg.blocks; zend_ssa_block *ssa_blocks; int blocks_count = ssa->cfg.blocks_count; uint32_t set_size; - zend_bitset tmp, gen, in; + zend_bitset def, in, phi; int *var = NULL; int i, j, k, changed; zend_dfg dfg; - ALLOCA_FLAG(dfg_use_heap); - ALLOCA_FLAG(var_use_heap); + ALLOCA_FLAG(dfg_use_heap) + ALLOCA_FLAG(var_use_heap) ssa->rt_constants = (build_flags & ZEND_RT_CONSTANTS); ssa_blocks = zend_arena_calloc(arena, blocks_count, sizeof(zend_ssa_block)); @@ -772,10 +847,9 @@ int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t b /* Compute Variable Liveness */ dfg.vars = op_array->last_var + op_array->T; dfg.size = set_size = zend_bitset_len(dfg.vars); - dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 5 + 1), dfg_use_heap); - memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 5 + 1)); - dfg.gen = dfg.tmp + set_size; - dfg.def = dfg.gen + set_size * blocks_count; + dfg.tmp = do_alloca((set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1), dfg_use_heap); + memset(dfg.tmp, 0, (set_size * sizeof(zend_ulong)) * (blocks_count * 4 + 1)); + dfg.def = dfg.tmp + set_size; dfg.use = dfg.def + set_size * blocks_count; dfg.in = dfg.use + set_size * blocks_count; dfg.out = dfg.in + set_size * blocks_count; @@ -789,28 +863,43 @@ int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t b zend_dump_dfg(op_array, &ssa->cfg, &dfg); } - tmp = dfg.tmp; - gen = dfg.gen; + def = dfg.def; in = dfg.in; - /* SSA construction, Step 1: Propagate "gen" sets in merge points */ + /* Reuse the "use" set, as we no longer need it */ + phi = dfg.use; + zend_bitset_clear(phi, set_size * blocks_count); + + /* Place e-SSA pis. This will add additional "def" points, so it must + * happen before def propagation. */ + place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg); + + /* SSA construction, Step 1: Propagate "def" sets in merge points */ do { changed = 0; for (j = 0; j < blocks_count; j++) { + zend_bitset def_j = def + j * set_size, phi_j = phi + j * set_size; if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } - if (j >= 0 && (blocks[j].predecessors_count > 1 || j == 0)) { - zend_bitset_copy(tmp, gen + (j * set_size), set_size); - for (k = 0; k < blocks[j].predecessors_count; k++) { - i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; - while (i != -1 && i != blocks[j].idom) { - zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size); - i = blocks[i].idom; + if (blocks[j].predecessors_count > 1) { + if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) { + /* Prevent any values from flowing into irreducible loops by + replacing all incoming values with explicit phis. The + register allocator depends on this property. */ + zend_bitset_union(phi_j, in + (j * set_size), set_size); + } else { + for (k = 0; k < blocks[j].predecessors_count; k++) { + i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; + while (i != -1 && i != blocks[j].idom) { + zend_bitset_union_with_intersection( + phi_j, phi_j, def + (i * set_size), in + (j * set_size), set_size); + i = blocks[i].idom; + } } } - if (!zend_bitset_equal(gen + (j * set_size), tmp, set_size)) { - zend_bitset_copy(gen + (j * set_size), tmp, set_size); + if (!zend_bitset_subset(phi_j, def_j, set_size)) { + zend_bitset_union(def_j, phi_j, set_size); changed = 1; } } @@ -823,128 +912,39 @@ int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t b free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; } - zend_bitset_clear(tmp, set_size); for (j = 0; j < blocks_count; j++) { if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { continue; } - if (blocks[j].predecessors_count > 1) { - zend_bitset_clear(tmp, set_size); - if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) { - /* Prevent any values from flowing into irreducible loops by - replacing all incoming values with explicit phis. The - register allocator depends on this property. */ - zend_bitset_copy(tmp, in + (j * set_size), set_size); - } else { - for (k = 0; k < blocks[j].predecessors_count; k++) { - i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; - while (i != -1 && i != blocks[j].idom) { - zend_bitset_union_with_intersection(tmp, tmp, gen + (i * set_size), in + (j * set_size), set_size); - i = blocks[i].idom; - } - } - } + if (!zend_bitset_empty(phi + j * set_size, set_size)) { + ZEND_BITSET_REVERSE_FOREACH(phi + j * set_size, set_size, i) { + zend_ssa_phi *phi = zend_arena_calloc(arena, 1, + sizeof(zend_ssa_phi) + + sizeof(int) * blocks[j].predecessors_count + + sizeof(void*) * blocks[j].predecessors_count); - if (!zend_bitset_empty(tmp, set_size)) { - i = op_array->last_var + op_array->T; - while (i > 0) { - i--; - if (zend_bitset_in(tmp, i)) { - zend_ssa_phi *phi = zend_arena_calloc(arena, 1, - sizeof(zend_ssa_phi) + - sizeof(int) * blocks[j].predecessors_count + - sizeof(void*) * blocks[j].predecessors_count); - - if (!phi) { - goto failure; - } - phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi)); - memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count); - phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count); - - phi->pi = -1; - phi->var = i; - phi->ssa_var = -1; - phi->next = ssa_blocks[j].phis; - ssa_blocks[j].phis = phi; - } - } - } - } - } + phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi)); + memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count); + phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count); - place_essa_pis(arena, op_array, build_flags, ssa, &dfg); + phi->pi = -1; + phi->var = i; + phi->ssa_var = -1; - /* SSA construction, Step ?: Phi after Pi placement based on Dominance Frontiers */ - for (j = 0; j < blocks_count; j++) { - if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) { - continue; - } - if (blocks[j].predecessors_count > 1) { - zend_bitset_clear(tmp, set_size); - if (blocks[j].flags & ZEND_BB_IRREDUCIBLE_LOOP) { - /* Prevent any values from flowing into irreducible loops by - replacing all incoming values with explicit phis. The - register allocator depends on this property. */ - zend_bitset_copy(tmp, in + (j * set_size), set_size); - } else { - for (k = 0; k < blocks[j].predecessors_count; k++) { - i = ssa->cfg.predecessors[blocks[j].predecessor_offset + k]; - while (i != -1 && i != blocks[j].idom) { - zend_ssa_phi *p = ssa_blocks[i].phis; - while (p) { - if (p) { - if (p->pi >= 0) { - if (zend_bitset_in(in + (j * set_size), p->var) && - !zend_bitset_in(gen + (i * set_size), p->var)) { - zend_bitset_incl(tmp, p->var); - } - } else { - zend_bitset_excl(tmp, p->var); - } - } - p = p->next; - } - i = blocks[i].idom; - } - } - } - - if (!zend_bitset_empty(tmp, set_size)) { - i = op_array->last_var + op_array->T; - while (i > 0) { - i--; - if (zend_bitset_in(tmp, i)) { - zend_ssa_phi **pp = &ssa_blocks[j].phis; - while (*pp) { - if ((*pp)->pi <= 0 && (*pp)->var == i) { - break; - } - pp = &(*pp)->next; - } - if (*pp == NULL) { - zend_ssa_phi *phi = zend_arena_calloc(arena, 1, - sizeof(zend_ssa_phi) + - sizeof(int) * blocks[j].predecessors_count + - sizeof(void*) * blocks[j].predecessors_count); - - if (!phi) { - goto failure; - } - phi->sources = (int*)(((char*)phi) + sizeof(zend_ssa_phi)); - memset(phi->sources, 0xff, sizeof(int) * blocks[j].predecessors_count); - phi->use_chains = (zend_ssa_phi**)(((char*)phi->sources) + sizeof(int) * ssa->cfg.blocks[j].predecessors_count); - - phi->pi = -1; - phi->var = i; - phi->ssa_var = -1; - phi->next = NULL; - *pp = phi; + /* Place phis after pis */ + { + zend_ssa_phi **pp = &ssa_blocks[j].phis; + while (*pp) { + if ((*pp)->pi < 0) { + break; } + pp = &(*pp)->next; } + phi->next = *pp; + *pp = phi; } - } + } ZEND_BITSET_FOREACH_END(); } } @@ -955,14 +955,13 @@ int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t b /* SSA construction, Step 3: Renaming */ ssa->ops = zend_arena_calloc(arena, op_array->last, sizeof(zend_ssa_op)); memset(ssa->ops, 0xff, op_array->last * sizeof(zend_ssa_op)); - memset(var, 0xff, (op_array->last_var + op_array->T) * sizeof(int)); + memset(var + op_array->last_var, 0xff, op_array->T * sizeof(int)); /* Create uninitialized SSA variables for each CV */ for (j = 0; j < op_array->last_var; j++) { var[j] = j; } ssa->vars_count = op_array->last_var; if (zend_ssa_rename(op_array, build_flags, ssa, var, 0) != SUCCESS) { -failure: free_alloca(var, var_use_heap); free_alloca(dfg.tmp, dfg_use_heap); return FAILURE; @@ -1044,13 +1043,16 @@ int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_ ssa_vars[phi->sources[0]].phi_use_chain = phi; } } - /* min and max variables can't be used together */ - if (phi->constraint.min_ssa_var >= 0) { - phi->sym_use_chain = ssa_vars[phi->constraint.min_ssa_var].sym_use_chain; - ssa_vars[phi->constraint.min_ssa_var].sym_use_chain = phi; - } else if (phi->constraint.max_ssa_var >= 0) { - phi->sym_use_chain = ssa_vars[phi->constraint.max_ssa_var].sym_use_chain; - ssa_vars[phi->constraint.max_ssa_var].sym_use_chain = phi; + if (phi->has_range_constraint) { + /* min and max variables can't be used together */ + zend_ssa_range_constraint *constraint = &phi->constraint.range; + if (constraint->min_ssa_var >= 0) { + phi->sym_use_chain = ssa_vars[constraint->min_ssa_var].sym_use_chain; + ssa_vars[constraint->min_ssa_var].sym_use_chain = phi; + } else if (constraint->max_ssa_var >= 0) { + phi->sym_use_chain = ssa_vars[constraint->max_ssa_var].sym_use_chain; + ssa_vars[constraint->max_ssa_var].sym_use_chain = phi; + } } } else { int j; @@ -1076,7 +1078,7 @@ int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_ } /* }}} */ -int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) +int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) /* {{{ */ { if (ssa->vars[var].use_chain == op) { ssa->vars[var].use_chain = zend_ssa_next_use(ssa->ops, var, op); @@ -1115,6 +1117,7 @@ int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var) return 0; } } +/* }}} */ /* * Local variables: |
