]> granicus.if.org Git - php/commitdiff
Add support for Pi type constraints
authorNikita Popov <nikic@php.net>
Sat, 9 Jan 2016 17:42:07 +0000 (18:42 +0100)
committerNikita Popov <nikic@php.net>
Tue, 19 Jan 2016 21:09:29 +0000 (22:09 +0100)
Supports TYPE_CHECK and IS_IDENTICAL for now.

ext/opcache/Optimizer/zend_dump.c
ext/opcache/Optimizer/zend_inference.c
ext/opcache/Optimizer/zend_ssa.c
ext/opcache/Optimizer/zend_ssa.h

index fddc5a974175857023a8f7c3aa9e1d106e8a520f..46952c1159f03b76bf37f3b1c0bfc6a8d469a728 100644 (file)
@@ -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;
index 2ff97272546e57261059b12846f38c125dd2256d..beec91924c2720cf51d8611e0025bdb9cb361e2b 100644 (file)
@@ -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);
index 059550b63d8b0c9f4b849fa1b283debeb4e83944..16d22dd258efdd421695cf44efc14e4dd823a0bf 100644 (file)
@@ -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);
+                               }
+                       }
                }
        }
 
index 0e1bc84dbc369023e67d2fe2c783dae50b4857d4..00d6875dfd2a947af5018cc84226c5ae5df5c447 100644 (file)
@@ -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 */