Skip to content

Commit

Permalink
Merge branch 'master' of git.php.net:/php-src
Browse files Browse the repository at this point in the history
laruence committed Jun 14, 2016
2 parents 6166c26 + 0468895 commit 564cc38
Showing 5 changed files with 156 additions and 99 deletions.
2 changes: 1 addition & 1 deletion ext/opcache/Optimizer/dfa_pass.c
Original file line number Diff line number Diff line change
@@ -82,7 +82,7 @@ int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx,
if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
}
if (zend_build_ssa(&ctx->arena, op_array, build_flags, ssa, flags) != SUCCESS) {
if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa, flags) != SUCCESS) {
return FAILURE;
}

18 changes: 11 additions & 7 deletions ext/opcache/Optimizer/zend_dump.c
Original file line number Diff line number Diff line change
@@ -332,14 +332,14 @@ static void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa
}
}

static void zend_dump_pi_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_pi_constraint *r, uint32_t dump_flags)
static void zend_dump_type_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_type_constraint *constraint, uint32_t dump_flags)
{
if (r->type_mask != (uint32_t) -1) {
fprintf(stderr, " TYPE");
zend_dump_type_info(r->type_mask, NULL, 0, dump_flags);
return;
}
fprintf(stderr, " TYPE");
zend_dump_type_info(constraint->type_mask, constraint->ce, 1, dump_flags);
}

static void zend_dump_range_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_range_constraint *r, uint32_t dump_flags)
{
if (r->range.underflow && r->range.overflow) {
return;
}
@@ -773,7 +773,11 @@ static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_
fprintf(stderr, " = Pi<BB%d>(", p->pi);
zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var, dump_flags);
fprintf(stderr, " &");
zend_dump_pi_constraint(op_array, ssa, &p->constraint, dump_flags);
if (p->has_range_constraint) {
zend_dump_range_constraint(op_array, ssa, &p->constraint.range, dump_flags);
} else {
zend_dump_type_constraint(op_array, ssa, &p->constraint.type, dump_flags);
}
fprintf(stderr, ")\n");
}
p = p->next;
133 changes: 75 additions & 58 deletions ext/opcache/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
@@ -486,8 +486,9 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
tmp->min = ZEND_LONG_MAX;
tmp->max = ZEND_LONG_MIN;
tmp->overflow = 0;
if (p->pi >= 0 && p->constraint.type_mask == (uint32_t) -1) {
if (p->constraint.negative) {
if (p->pi >= 0 && p->has_range_constraint) {
zend_ssa_range_constraint *constraint = &p->constraint.range;
if (constraint->negative) {
if (ssa->var_info[p->sources[0]].has_range) {
tmp->underflow = ssa->var_info[p->sources[0]].range.underflow;
tmp->min = ssa->var_info[p->sources[0]].range.min;
@@ -500,8 +501,8 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
tmp->overflow = 1;
}
#ifdef NEG_RANGE
if (p->constraint.min_ssa_var < 0 &&
p->constraint.min_ssa_var < 0 &&
if (constraint->min_ssa_var < 0 &&
constraint->min_ssa_var < 0 &&
ssa->var_info[p->ssa_var].has_range) {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "%s() #%d [%ld..%ld] -> [%ld..%ld]?\n",
@@ -512,19 +513,19 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
tmp->min,
tmp->max);
#endif
if (p->constraint.negative == NEG_USE_LT &&
tmp->max >= p->constraint.range.min) {
if (constraint->negative == NEG_USE_LT &&
tmp->max >= constraint->range.min) {
tmp->overflow = 0;
tmp->max = p->constraint.range.min - 1;
tmp->max = constraint->range.min - 1;
#ifdef LOG_NEG_RANGE
fprintf(stderr, " => [%ld..%ld]\n",
tmp->min,
tmp->max);
#endif
} else if (p->constraint.negative == NEG_USE_GT &&
tmp->min <= p->constraint.range.max) {
} else if (constraint->negative == NEG_USE_GT &&
tmp->min <= constraint->range.max) {
tmp->underflow = 0;
tmp->min = p->constraint.range.max + 1;
tmp->min = constraint->range.max + 1;
#ifdef LOG_NEG_RANGE
fprintf(stderr, " => [%ld..%ld]\n",
tmp->min,
@@ -539,44 +540,44 @@ int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int
tmp->min = ssa->var_info[p->sources[0]].range.min;
tmp->max = ssa->var_info[p->sources[0]].range.max;
tmp->overflow = ssa->var_info[p->sources[0]].range.overflow;
if (p->constraint.min_ssa_var < 0) {
tmp->underflow = p->constraint.range.underflow && tmp->underflow;
tmp->min = MAX(p->constraint.range.min, tmp->min);
if (constraint->min_ssa_var < 0) {
tmp->underflow = constraint->range.underflow && tmp->underflow;
tmp->min = MAX(constraint->range.min, tmp->min);
#ifdef SYM_RANGE
} else if (narrowing && ssa->var_info[p->constraint.min_ssa_var].has_range) {
tmp->underflow = ssa->var_info[p->constraint.min_ssa_var].range.underflow && tmp->underflow;
tmp->min = MAX(ssa->var_info[p->constraint.min_ssa_var].range.min + p->constraint.range.min, tmp->min);
} else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow && tmp->underflow;
tmp->min = MAX(ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min, tmp->min);
#endif
}
if (p->constraint.max_ssa_var < 0) {
tmp->max = MIN(p->constraint.range.max, tmp->max);
tmp->overflow = p->constraint.range.overflow && tmp->overflow;
if (constraint->max_ssa_var < 0) {
tmp->max = MIN(constraint->range.max, tmp->max);
tmp->overflow = constraint->range.overflow && tmp->overflow;
#ifdef SYM_RANGE
} else if (narrowing && ssa->var_info[p->constraint.max_ssa_var].has_range) {
tmp->max = MIN(ssa->var_info[p->constraint.max_ssa_var].range.max + p->constraint.range.max, tmp->max);
tmp->overflow = ssa->var_info[p->constraint.max_ssa_var].range.overflow && tmp->overflow;
} else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
tmp->max = MIN(ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max, tmp->max);
tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow && tmp->overflow;
#endif
}
} else if (narrowing) {
if (p->constraint.min_ssa_var < 0) {
tmp->underflow = p->constraint.range.underflow;
tmp->min = p->constraint.range.min;
if (constraint->min_ssa_var < 0) {
tmp->underflow = constraint->range.underflow;
tmp->min = constraint->range.min;
#ifdef SYM_RANGE
} else if (narrowing && ssa->var_info[p->constraint.min_ssa_var].has_range) {
tmp->underflow = ssa->var_info[p->constraint.min_ssa_var].range.underflow;
tmp->min = ssa->var_info[p->constraint.min_ssa_var].range.min + p->constraint.range.min;
} else if (narrowing && ssa->var_info[constraint->min_ssa_var].has_range) {
tmp->underflow = ssa->var_info[constraint->min_ssa_var].range.underflow;
tmp->min = ssa->var_info[constraint->min_ssa_var].range.min + constraint->range.min;
#endif
} else {
tmp->underflow = 1;
tmp->min = ZEND_LONG_MIN;
}
if (p->constraint.max_ssa_var < 0) {
tmp->max = p->constraint.range.max;
tmp->overflow = p->constraint.range.overflow;
if (constraint->max_ssa_var < 0) {
tmp->max = constraint->range.max;
tmp->overflow = constraint->range.overflow;
#ifdef SYM_RANGE
} else if (narrowing && ssa->var_info[p->constraint.max_ssa_var].has_range) {
tmp->max = ssa->var_info[p->constraint.max_ssa_var].range.max + p->constraint.range.max;
tmp->overflow = ssa->var_info[p->constraint.max_ssa_var].range.overflow;
} else if (narrowing && ssa->var_info[constraint->max_ssa_var].has_range) {
tmp->max = ssa->var_info[constraint->max_ssa_var].range.max + constraint->range.max;
tmp->overflow = ssa->var_info[constraint->max_ssa_var].range.overflow;
#endif
} else {
tmp->max = ZEND_LONG_MAX;
@@ -1825,55 +1826,57 @@ static void zend_infer_ranges_warmup(const zend_op_array *op_array, zend_ssa *ss
ssa->var_info[j].has_range &&
ssa->vars[j].definition_phi &&
ssa->vars[j].definition_phi->pi >= 0 &&
ssa->vars[j].definition_phi->constraint.type_mask == (uint32_t) -1 &&
ssa->vars[j].definition_phi->constraint.negative &&
ssa->vars[j].definition_phi->constraint.min_ssa_var < 0 &&
ssa->vars[j].definition_phi->constraint.min_ssa_var < 0) {
ssa->vars[j].definition_phi->has_range_constraint &&
ssa->vars[j].definition_phi->constraint.range.negative &&
ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0 &&
ssa->vars[j].definition_phi->constraint.range.min_ssa_var < 0) {
zend_ssa_range_constraint *constraint =
&ssa->vars[j].definition_phi->constraint.range;
if (tmp.min == ssa->var_info[j].range.min &&
tmp.max == ssa->var_info[j].range.max) {
if (ssa->vars[j].definition_phi->constraint.negative == NEG_INIT) {
if (constraint->negative == NEG_INIT) {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "#%d INVARIANT\n", j);
#endif
ssa->vars[j].definition_phi->constraint.negative = NEG_INVARIANT;
constraint->negative = NEG_INVARIANT;
}
} else if (tmp.min == ssa->var_info[j].range.min &&
tmp.max == ssa->var_info[j].range.max + 1 &&
tmp.max < ssa->vars[j].definition_phi->constraint.range.min) {
if (ssa->vars[j].definition_phi->constraint.negative == NEG_INIT ||
ssa->vars[j].definition_phi->constraint.negative == NEG_INVARIANT) {
tmp.max < constraint->range.min) {
if (constraint->negative == NEG_INIT ||
constraint->negative == NEG_INVARIANT) {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "#%d LT\n", j);
#endif
ssa->vars[j].definition_phi->constraint.negative = NEG_USE_LT;
constraint->negative = NEG_USE_LT;
//???NEG
} else if (ssa->vars[j].definition_phi->constraint.negative == NEG_USE_GT) {
} else if (constraint->negative == NEG_USE_GT) {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "#%d UNKNOWN\n", j);
#endif
ssa->vars[j].definition_phi->constraint.negative = NEG_UNKNOWN;
constraint->negative = NEG_UNKNOWN;
}
} else if (tmp.max == ssa->var_info[j].range.max &&
tmp.min == ssa->var_info[j].range.min - 1 &&
tmp.min > ssa->vars[j].definition_phi->constraint.range.max) {
if (ssa->vars[j].definition_phi->constraint.negative == NEG_INIT ||
ssa->vars[j].definition_phi->constraint.negative == NEG_INVARIANT) {
tmp.min > constraint->range.max) {
if (constraint->negative == NEG_INIT ||
constraint->negative == NEG_INVARIANT) {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "#%d GT\n", j);
#endif
ssa->vars[j].definition_phi->constraint.negative = NEG_USE_GT;
constraint->negative = NEG_USE_GT;
//???NEG
} else if (ssa->vars[j].definition_phi->constraint.negative == NEG_USE_LT) {
} else if (constraint->negative == NEG_USE_LT) {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "#%d UNKNOWN\n", j);
#endif
ssa->vars[j].definition_phi->constraint.negative = NEG_UNKNOWN;
constraint->negative = NEG_UNKNOWN;
}
} else {
#ifdef LOG_NEG_RANGE
fprintf(stderr, "#%d UNKNOWN\n", j);
#endif
ssa->vars[j].definition_phi->constraint.negative = NEG_UNKNOWN;
constraint->negative = NEG_UNKNOWN;
}
}
#endif
@@ -3424,14 +3427,28 @@ int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script
if (ssa_vars[j].definition_phi) {
zend_ssa_phi *p = ssa_vars[j].definition_phi;
if (p->pi >= 0) {
zend_class_entry *ce = ssa_var_info[p->sources[0]].ce;
int is_instanceof = ssa_var_info[p->sources[0]].is_instanceof;
tmp = get_ssa_var_info(ssa, p->sources[0]);
if (p->constraint.type_mask != (uint32_t) -1) {
tmp &= p->constraint.type_mask;

if (!p->has_range_constraint) {
zend_ssa_type_constraint *constraint = &p->constraint.type;
tmp &= constraint->type_mask;
if ((tmp & MAY_BE_OBJECT) && constraint->ce && ce != constraint->ce) {
if (!ce) {
ce = constraint->ce;
is_instanceof = 1;
} else if (is_instanceof && instanceof_function(constraint->ce, ce)) {
ce = constraint->ce;
} else {
/* Ignore the constraint (either ce instanceof constraint->ce or
* they are unrelated, as far as we can statically determine) */
}
}
}

UPDATE_SSA_TYPE(tmp, j);
if (ssa_var_info[p->sources[0]].ce) {
UPDATE_SSA_OBJ_TYPE(ssa_var_info[p->sources[0]].ce, ssa_var_info[p->sources[0]].is_instanceof, j);
}
UPDATE_SSA_OBJ_TYPE(ce, is_instanceof, j);
} else {
int first = 1;
int is_instanceof = 0;
83 changes: 54 additions & 29 deletions ext/opcache/Optimizer/zend_ssa.c
Original file line number Diff line number Diff line change
@@ -110,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;
}
/* }}} */

@@ -137,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) {
@@ -220,8 +223,8 @@ 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++) {
@@ -483,6 +486,23 @@ 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;
}
}
}
}
@@ -741,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];
@@ -802,7 +824,7 @@ 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;
@@ -850,7 +872,7 @@ int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t b

/* Place e-SSA pis. This will add additional "def" points, so it must
* happen before def propagation. */
place_essa_pis(arena, op_array, build_flags, ssa, &dfg);
place_essa_pis(arena, script, op_array, build_flags, ssa, &dfg);

/* SSA construction, Step 1: Propagate "def" sets in merge points */
do {
@@ -1021,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;
19 changes: 15 additions & 4 deletions ext/opcache/Optimizer/zend_ssa.h
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
#ifndef ZEND_SSA_H
#define ZEND_SSA_H

#include "zend_optimizer.h"
#include "zend_cfg.h"

typedef struct _zend_ssa_range {
@@ -38,14 +39,23 @@ typedef enum _zend_ssa_negative_lat {
} zend_ssa_negative_lat;

/* Special kind of SSA Phi function used in eSSA */
typedef struct _zend_ssa_pi_constraint {
typedef struct _zend_ssa_range_constraint {
zend_ssa_range range; /* simple range constraint */
int min_var;
int max_var;
int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
zend_ssa_negative_lat negative;
uint32_t type_mask; /* If -1 this is a range constraint */
} zend_ssa_range_constraint;

typedef struct _zend_ssa_type_constraint {
uint32_t type_mask; /* Type mask to intersect with */
zend_class_entry *ce; /* Class entry for instanceof constraints */
} zend_ssa_type_constraint;

typedef union _zend_ssa_pi_constraint {
zend_ssa_range_constraint range;
zend_ssa_type_constraint type;
} zend_ssa_pi_constraint;

/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
@@ -57,7 +67,8 @@ struct _zend_ssa_phi {
int var; /* Original CV, VAR or TMP variable index */
int ssa_var; /* SSA variable index */
int block; /* current BB index */
int visited; /* flag to avoid recursive processing */
int visited : 1; /* flag to avoid recursive processing */
int has_range_constraint : 1;
zend_ssa_phi **use_chains;
zend_ssa_phi *sym_use_chain;
int *sources; /* Array of SSA IDs that produce this var.
@@ -116,7 +127,7 @@ typedef struct _zend_ssa {

BEGIN_EXTERN_C()

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);
int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);

0 comments on commit 564cc38

Please sign in to comment.