From: Nikita Popov Date: Sat, 9 Jan 2016 17:42:07 +0000 (+0100) Subject: Add support for Pi type constraints X-Git-Tag: php-7.2.0alpha1~620^2~102 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5662d73528f7be103c8812b97dc239957c1f4626;p=php Add support for Pi type constraints Supports TYPE_CHECK and IS_IDENTICAL for now. --- diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index fddc5a9741..46952c1159 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -305,8 +305,14 @@ void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int s } } -static void zend_dump_pi_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_pi_range *r) +static void zend_dump_pi_constraint(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_pi_constraint *r) { + if (r->type_mask != (uint32_t) -1) { + fprintf(stderr, " TYPE"); + zend_dump_type_info(r->type_mask, NULL, 0); + return; + } + if (r->range.underflow && r->range.overflow) { return; } @@ -791,7 +797,7 @@ static void zend_dump_block_header(const zend_cfg *cfg, const zend_op_array *op_ fprintf(stderr, " = Pi("); zend_dump_ssa_var(op_array, ssa, p->sources[0], 0, p->var); fprintf(stderr, " &"); - zend_dump_pi_range(op_array, ssa, &p->constraint); + zend_dump_pi_constraint(op_array, ssa, &p->constraint); fprintf(stderr, ")\n"); } p = p->next; diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 2ff9727254..beec91924c 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -475,7 +475,7 @@ 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) { + if (p->pi >= 0 && p->constraint.type_mask == (uint32_t) -1) { if (p->constraint.negative) { if (ssa->var_info[p->sources[0]].has_range) { tmp->underflow = ssa->var_info[p->sources[0]].range.underflow; @@ -1814,6 +1814,7 @@ 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) { @@ -3658,6 +3659,9 @@ int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script zend_ssa_phi *p = ssa_vars[j].definition_phi; if (p->pi >= 0) { tmp = get_ssa_var_info(ssa, p->sources[0]); + if (p->constraint.type_mask != (uint32_t) -1) { + tmp &= p->constraint.type_mask; + } 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); diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index 059550b63d..16d22dd258 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -21,6 +21,7 @@ #include "zend_dfg.h" #include "zend_ssa.h" #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) /* {{{ */ { @@ -78,6 +79,7 @@ static void pi_range( 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; } /* }}} */ @@ -94,6 +96,27 @@ static inline void pi_range_max(zend_ssa_phi *phi, int var, zend_long val) { pi_range(phi, -1, var, ZEND_LONG_MIN, val, 1, 0, 0); } +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; + if (type_mask & MAY_BE_NULL) { + phi->constraint.type_mask |= MAY_BE_UNDEF; + } +} +static inline void pi_not_type_mask(zend_ssa_phi *phi, uint32_t type_mask) { + uint32_t relevant = MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + pi_type_mask(phi, ~type_mask & relevant); +} +static inline uint32_t mask_for_type_check(uint32_t type) { + if (type == _IS_BOOL) { + return MAY_BE_TRUE|MAY_BE_FALSE; + } else if (type == IS_ARRAY) { + return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + } else { + return 1 << 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) @@ -805,6 +828,59 @@ int zend_build_ssa(zend_arena **arena, const zend_op_array *op_array, uint32_t b if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bt, var))) { pi_range_not_equals(pi, -1, 0); } + } else if (opline->op1_type == IS_TMP_VAR && (opline-1)->opcode == ZEND_TYPE_CHECK && + opline->op1.var == (opline-1)->result.var && (opline-1)->op1_type == IS_CV) { + int var = EX_VAR_TO_NUM((opline-1)->op1.var); + uint32_t type = (opline-1)->extended_value; + if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bt, var))) { + pi_type_mask(pi, mask_for_type_check(type)); + } + if (type != IS_OBJECT && type != IS_RESOURCE) { + /* is_object() and is_resource() may return false, even though the value is + * an object/resource. */ + if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bf, var))) { + pi_not_type_mask(pi, mask_for_type_check(type)); + } + } + } else if (opline->op1_type == IS_TMP_VAR && + ((opline-1)->opcode == ZEND_IS_IDENTICAL + || (opline-1)->opcode == ZEND_IS_NOT_IDENTICAL) && + opline->op1.var == (opline-1)->result.var) { + int var; + zval *val; + uint32_t type_mask; + if ((opline-1)->op1_type == IS_CV && (opline-1)->op2_type == IS_CONST) { + var = EX_VAR_TO_NUM((opline-1)->op1.var); + val = CRT_CONSTANT((opline-1)->op2); + } else if ((opline-1)->op1_type == IS_CONST && (opline-1)->op2_type == IS_CV) { + var = EX_VAR_TO_NUM((opline-1)->op2.var); + val = CRT_CONSTANT((opline-1)->op1); + } else { + continue; + } + + /* We're interested in === null/true/false comparisons here, because they eliminate + * a type in the false-branch. Other === VAL comparisons are unlikely to be useful. */ + if (Z_TYPE_P(val) != IS_NULL && Z_TYPE_P(val) != IS_TRUE && Z_TYPE_P(val) != IS_FALSE) { + continue; + } + + type_mask = _const_op_type(val); + if ((opline-1)->opcode == ZEND_IS_IDENTICAL) { + if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bt, var))) { + pi_type_mask(pi, type_mask); + } + if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bf, var))) { + pi_not_type_mask(pi, type_mask); + } + } else { + if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bf, var))) { + pi_type_mask(pi, type_mask); + } + if ((pi = add_pi(arena, op_array, &dfg, ssa, j, bt, var))) { + pi_not_type_mask(pi, type_mask); + } + } } } diff --git a/ext/opcache/Optimizer/zend_ssa.h b/ext/opcache/Optimizer/zend_ssa.h index 0e1bc84dbc..00d6875dfd 100644 --- a/ext/opcache/Optimizer/zend_ssa.h +++ b/ext/opcache/Optimizer/zend_ssa.h @@ -38,21 +38,22 @@ typedef enum _zend_ssa_negative_lat { } zend_ssa_negative_lat; /* Special kind of SSA Phi function used in eSSA */ -typedef struct _zend_ssa_pi_range { +typedef struct _zend_ssa_pi_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; /* ((man_var>0) ? MAX(ssa_var) : 0) + range.man */ zend_ssa_negative_lat negative; -} zend_ssa_pi_range; + uint32_t type_mask; /* If -1 this is a range constraint */ +} zend_ssa_pi_constraint; /* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */ typedef struct _zend_ssa_phi zend_ssa_phi; struct _zend_ssa_phi { zend_ssa_phi *next; /* next Phi in the same BB */ int pi; /* if >= 0 this is actually a e-SSA Pi */ - zend_ssa_pi_range constraint; /* e-SSA Pi constraint */ + zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */ int var; /* Original CV, VAR or TMP variable index */ int ssa_var; /* SSA variable index */ int block; /* current BB index */