]> granicus.if.org Git - php/commitdiff
Update operands range information accoring to the taken branches and use this indorma...
authorDmitry Stogov <dmitry@zend.com>
Thu, 27 Aug 2020 12:29:11 +0000 (15:29 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 27 Aug 2020 12:29:11 +0000 (15:29 +0300)
ext/opcache/jit/zend_jit.c
ext/opcache/jit/zend_jit_trace.c

index 5d1b2414a84dae79a64580c82c6f3041934dc49e..5254a395187c00e650bd6a994e75a999fe582815 100644 (file)
@@ -111,7 +111,6 @@ static const void *zend_jit_func_trace_counter_handler = NULL;
 static const void *zend_jit_ret_trace_counter_handler = NULL;
 static const void *zend_jit_loop_trace_counter_handler = NULL;
 
-static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa);
 static void ZEND_FASTCALL zend_runtime_jit(void);
 
 static int zend_jit_trace_op_len(const zend_op *opline);
@@ -487,9 +486,10 @@ static void *dasm_link_and_encode(dasm_State             **dasm_state,
        return entry;
 }
 
-static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
+static int zend_may_overflow(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
 {
        int res;
+       zend_long op1_min, op1_max, op2_min, op2_max;
 
        if (!ssa->ops || !ssa->var_info) {
                return 1;
@@ -498,24 +498,38 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                case ZEND_PRE_INC:
                case ZEND_POST_INC:
                        res = ssa_op->op1_def;
-                       return (res < 0 ||
-                               !ssa->var_info[res].has_range ||
-                               ssa->var_info[res].range.overflow);
+                       if (res < 0
+                        || !ssa->var_info[res].has_range
+                        || ssa->var_info[res].range.overflow) {
+                               if (!OP1_HAS_RANGE()) {
+                                       return 1;
+                               }
+                               op1_max = OP1_MAX_RANGE();
+                               if (op1_max == ZEND_LONG_MAX) {
+                                       return 1;
+                               }
+                       }
+                       return 0;
                case ZEND_PRE_DEC:
                case ZEND_POST_DEC:
                        res = ssa_op->op1_def;
-                       return (res < 0 ||
-                               !ssa->var_info[res].has_range ||
-                               ssa->var_info[res].range.underflow);
+                       if (res < 0
+                        || !ssa->var_info[res].has_range
+                        || ssa->var_info[res].range.underflow) {
+                               if (!OP1_HAS_RANGE()) {
+                                       return 1;
+                               }
+                               op1_min = OP1_MIN_RANGE();
+                               if (op1_min == ZEND_LONG_MIN) {
+                                       return 1;
+                               }
+                       }
+                       return 0;
                case ZEND_ADD:
                        res = ssa_op->result_def;
-                       if (res < 0 ||
-                           !ssa->var_info[res].has_range) {
-                               return 1;
-                       }
-                       if (ssa->var_info[res].range.underflow) {
-                               zend_long op1_min, op2_min;
-
+                       if (res < 0
+                        || !ssa->var_info[res].has_range
+                        || ssa->var_info[res].range.underflow) {
                                if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                        return 1;
                                }
@@ -525,9 +539,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                                        return 1;
                                }
                        }
-                       if (ssa->var_info[res].range.overflow) {
-                               zend_long op1_max, op2_max;
-
+                       if (res < 0
+                        || !ssa->var_info[res].has_range
+                        || ssa->var_info[res].range.overflow) {
                                if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                        return 1;
                                }
@@ -540,13 +554,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                        return 0;
                case ZEND_SUB:
                        res = ssa_op->result_def;
-                       if (res < 0 ||
-                           !ssa->var_info[res].has_range) {
-                               return 1;
-                       }
-                       if (ssa->var_info[res].range.underflow) {
-                               zend_long op1_min, op2_max;
-
+                       if (res < 0
+                        || !ssa->var_info[res].has_range
+                        || ssa->var_info[res].range.underflow) {
                                if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                        return 1;
                                }
@@ -556,9 +566,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                                        return 1;
                                }
                        }
-                       if (ssa->var_info[res].range.overflow) {
-                               zend_long op1_max, op2_min;
-
+                       if (res < 0
+                        || !ssa->var_info[res].has_range
+                        || ssa->var_info[res].range.overflow) {
                                if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                        return 1;
                                }
@@ -578,13 +588,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                case ZEND_ASSIGN_OP:
                        if (opline->extended_value == ZEND_ADD) {
                                res = ssa_op->op1_def;
-                               if (res < 0 ||
-                                   !ssa->var_info[res].has_range) {
-                                       return 1;
-                               }
-                               if (ssa->var_info[res].range.underflow) {
-                                       zend_long op1_min, op2_min;
-
+                               if (res < 0
+                                || !ssa->var_info[res].has_range
+                                || ssa->var_info[res].range.underflow) {
                                        if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                                return 1;
                                        }
@@ -594,9 +600,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                                                return 1;
                                        }
                                }
-                               if (ssa->var_info[res].range.overflow) {
-                                       zend_long op1_max, op2_max;
-
+                               if (res < 0
+                                || !ssa->var_info[res].has_range
+                                || ssa->var_info[res].range.overflow) {
                                        if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                                return 1;
                                        }
@@ -609,13 +615,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                                return 0;
                        } else if (opline->extended_value == ZEND_SUB) {
                                res = ssa_op->op1_def;
-                               if (res < 0 ||
-                                   !ssa->var_info[res].has_range) {
-                                       return 1;
-                               }
-                               if (ssa->var_info[res].range.underflow) {
-                                       zend_long op1_min, op2_max;
-
+                               if (res < 0
+                                || !ssa->var_info[res].has_range
+                                || ssa->var_info[res].range.underflow) {
                                        if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                                return 1;
                                        }
@@ -625,9 +627,9 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
                                                return 1;
                                        }
                                }
-                               if (ssa->var_info[res].range.overflow) {
-                                       zend_long op1_max, op2_min;
-
+                               if (res < 0
+                                || !ssa->var_info[res].has_range
+                                || ssa->var_info[res].range.overflow) {
                                        if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
                                                return 1;
                                        }
@@ -650,11 +652,6 @@ static int zend_may_overflow_ex(const zend_op *opline, const zend_ssa_op *ssa_op
        }
 }
 
-static int zend_may_overflow(const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa)
-{
-       return zend_may_overflow_ex(opline, &ssa->ops[opline - op_array->opcodes], op_array, ssa);
-}
-
 static int zend_jit_build_cfg(const zend_op_array *op_array, zend_cfg *cfg)
 {
        uint32_t flags;
@@ -2253,7 +2250,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                                op1_def_info, OP1_DEF_REG_ADDR(),
                                                                res_use_info, res_info,
                                                                res_addr,
-                                                               (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -2343,7 +2340,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                                op2_info, OP2_REG_ADDR(),
                                                                res_use_info, res_info, res_addr,
                                                                send_result,
-                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -2423,7 +2420,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                if (!zend_jit_assign_op(&dasm_state, opline,
                                                                op1_info, op1_def_info, OP1_RANGE(),
                                                                op2_info, OP2_RANGE(),
-                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
index 419f4f1459cda18bf75a2408c0d8e4bb9cb56643..e549ba7ece6751b9dcab004e55188e774bf37076 100644 (file)
@@ -2906,6 +2906,133 @@ static int zend_jit_trace_deoptimization(dasm_State             **Dst,
        return 1;
 }
 
+static void zend_jit_trace_set_var_range(zend_ssa_var_info *info, zend_long min, zend_long max)
+{
+       info->has_range = 1;
+       info->range.min = min;
+       info->range.max = max;
+       info->range.underflow = 0;
+       info->range.overflow = 0;
+}
+
+static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, zend_bool exit_if_true)
+{
+       zend_long op1_min, op1_max, op2_min, op2_max;
+
+       if ((OP1_INFO() & MAY_BE_ANY) != MAY_BE_LONG
+        || (OP1_INFO() & MAY_BE_ANY) != MAY_BE_LONG) {
+               return;
+       }
+
+       op1_min = OP1_MIN_RANGE();
+       op1_max = OP1_MAX_RANGE();
+       op2_min = OP2_MIN_RANGE();
+       op2_max = OP2_MAX_RANGE();
+
+       switch (opline->opcode) {
+               case ZEND_IS_EQUAL:
+               case ZEND_CASE:
+               case ZEND_IS_IDENTICAL:
+               case ZEND_CASE_STRICT:
+                       if (!exit_if_true) {
+                               /* op1 == op2 */
+                               if (ssa_op->op1_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op1_use],
+                                               MAX(op1_min, op2_min),
+                                               MIN(op1_max, op2_max));
+                               }
+                               if (ssa_op->op2_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op2_use],
+                                               MAX(op2_min, op1_min),
+                                               MIN(op2_max, op1_max));
+                               }
+                       }
+                       break;
+               case ZEND_IS_NOT_EQUAL:
+               case ZEND_IS_NOT_IDENTICAL:
+                       if (exit_if_true) {
+                               /* op1 == op2 */
+                               if (ssa_op->op1_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op1_use],
+                                               MAX(op1_min, op2_min),
+                                               MIN(op1_max, op2_max));
+                               }
+                               if (ssa_op->op2_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op2_use],
+                                               MAX(op2_min, op1_min),
+                                               MIN(op2_max, op1_max));
+                               }
+                       }
+                       break;
+               case ZEND_IS_SMALLER_OR_EQUAL:
+                       if (!exit_if_true) {
+                               /* op1 <= op2 */
+                               if (ssa_op->op1_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op1_use],
+                                               op1_min,
+                                               MIN(op1_max, op2_max));
+                               }
+                               if (ssa_op->op2_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op2_use],
+                                               MAX(op2_min, op1_min),
+                                               op2_max);
+                               }
+                       } else {
+                               /* op1 > op2 */
+                               if (ssa_op->op1_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op1_use],
+                                               op2_min != ZEND_LONG_MAX ? MAX(op1_min, op2_min + 1) : op1_min,
+                                               op1_max);
+                               }
+                               if (ssa_op->op2_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op2_use],
+                                               op2_min,
+                                               op2_max != ZEND_LONG_MIN ?MIN(op2_max, op1_max - 1) : op1_max);
+                               }
+                       }
+                       break;
+               case ZEND_IS_SMALLER:
+                       if (!exit_if_true) {
+                               /* op1 < op2 */
+                               if (ssa_op->op1_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op1_use],
+                                               op1_min,
+                                               op2_max != ZEND_LONG_MIN ? MIN(op1_max, op2_max - 1) : op1_max);
+                               }
+                               if (ssa_op->op2_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op2_use],
+                                               op1_min != ZEND_LONG_MAX ? MAX(op2_min, op1_min + 1) : op2_min,
+                                               op2_max);
+                               }
+                       } else {
+                               /* op1 >= op2 */
+                               if (ssa_op->op1_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op1_use],
+                                               MAX(op1_min, op2_min),
+                                               op1_max);
+                               }
+                               if (ssa_op->op2_use >= 0) {
+                                       zend_jit_trace_set_var_range(
+                                               &ssa->var_info[ssa_op->op2_use],
+                                               op2_min,
+                                               MIN(op2_max, op1_max));
+                               }
+                       }
+                       break;
+       }
+}
+
 static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num)
 {
        const void *handler = NULL;
@@ -3235,7 +3362,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                                op1_def_info, OP1_DEF_REG_ADDR(),
                                                                res_use_info, res_info,
                                                                res_addr,
-                                                               (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+                                                               (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -3380,7 +3507,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                                op2_info, OP2_REG_ADDR(),
                                                                res_use_info, res_info, res_addr,
                                                                send_result,
-                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -3477,7 +3604,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                if (!zend_jit_assign_op(&dasm_state, opline,
                                                                op1_info, op1_def_info, OP1_RANGE(),
                                                                op2_info, OP2_RANGE(),
-                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow_ex(opline, ssa_op, op_array, ssa),
+                                                               (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa),
                                                                zend_may_throw(opline, ssa_op, op_array, ssa))) {
                                                        goto jit_failure;
                                                }
@@ -3775,6 +3902,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                                goto jit_failure;
                                                        }
                                                        smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
+                                                       zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
                                                } else {
                                                        smart_branch_opcode = 0;
                                                        exit_addr = NULL;
@@ -3812,6 +3940,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                                exit_if_true = !exit_if_true;
                                                        }
                                                        smart_branch_opcode = exit_if_true ? ZEND_JMPNZ : ZEND_JMPZ;
+                                                       zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true);
                                                } else {
                                                        smart_branch_opcode = 0;
                                                        exit_addr = NULL;