From: Dmitry Stogov Date: Thu, 27 Aug 2020 12:29:11 +0000 (+0300) Subject: Update operands range information accoring to the taken branches and use this indorma... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e701146070e3287dc4519f543b2228ca54f4f029;p=php Update operands range information accoring to the taken branches and use this indormation to eliminate useless overflow checks. --- diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 5d1b2414a8..5254a39518 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -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; } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 419f4f1459..e549ba7ece 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -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;