diff options
author | Dmitry Stogov <dmitry@zend.com> | 2020-10-05 22:33:45 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2020-10-05 22:33:45 +0300 |
commit | 31f54586b5464c2232d2e628a816b85b663eb877 (patch) | |
tree | 5f263d1d2eb5f1c42f66c5e630e3b71ba9f3cb6a | |
parent | 90b80c82787f42b5ded3e313b93f55f865643cf6 (diff) | |
download | php-git-31f54586b5464c2232d2e628a816b85b663eb877.tar.gz |
Use range inference to eliminate useless comparisons
-rw-r--r-- | ext/opcache/jit/zend_jit.c | 9 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_trace.c | 48 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_x86.dasc | 156 |
3 files changed, 188 insertions, 25 deletions
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 7c9a8bb7cf..ad1f835bd5 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -192,6 +192,7 @@ static zend_bool zend_long_is_power_of_two(zend_long x) #define OP_RANGE(ssa_op, opN) \ (((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \ + ssa->var_info && \ (ssa_op)->opN##_use >= 0 && \ ssa->var_info[(ssa_op)->opN##_use].has_range) ? \ &ssa->var_info[(ssa_op)->opN##_use].range : NULL) @@ -2796,8 +2797,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op target_label = target_label2 = (uint32_t)-1; } if (!zend_jit_cmp(&dasm_state, opline, - OP1_INFO(), OP1_REG_ADDR(), - OP2_INFO(), OP2_REG_ADDR(), + OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(), + OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(), res_addr, zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, target_label, target_label2, @@ -2825,8 +2826,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op target_label = target_label2 = (uint32_t)-1; } if (!zend_jit_identical(&dasm_state, opline, - OP1_INFO(), OP1_REG_ADDR(), - OP2_INFO(), OP2_REG_ADDR(), + OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(), + OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(), RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, target_label, target_label2, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 751e492a4f..11f66b9b06 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4299,18 +4299,26 @@ 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; + if (!zend_jit_cmp(&dasm_state, opline, + op1_info, OP1_RANGE(), OP1_REG_ADDR(), + op2_info, OP2_RANGE(), OP2_REG_ADDR(), + RES_REG_ADDR(), + zend_may_throw(opline, ssa_op, op_array, ssa), + smart_branch_opcode, -1, -1, exit_addr)) { + goto jit_failure; + } zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true); } else { smart_branch_opcode = 0; exit_addr = NULL; - } - if (!zend_jit_cmp(&dasm_state, opline, - op1_info, OP1_REG_ADDR(), - op2_info, OP2_REG_ADDR(), - RES_REG_ADDR(), - zend_may_throw(opline, ssa_op, op_array, ssa), - smart_branch_opcode, -1, -1, exit_addr)) { - goto jit_failure; + if (!zend_jit_cmp(&dasm_state, opline, + op1_info, OP1_RANGE(), OP1_REG_ADDR(), + op2_info, OP2_RANGE(), OP2_REG_ADDR(), + RES_REG_ADDR(), + zend_may_throw(opline, ssa_op, op_array, ssa), + smart_branch_opcode, -1, -1, exit_addr)) { + goto jit_failure; + } } goto done; case ZEND_IS_IDENTICAL: @@ -4337,18 +4345,26 @@ 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; + if (!zend_jit_identical(&dasm_state, opline, + op1_info, OP1_RANGE(), OP1_REG_ADDR(), + op2_info, OP2_RANGE(), OP2_REG_ADDR(), + RES_REG_ADDR(), + zend_may_throw(opline, ssa_op, op_array, ssa), + smart_branch_opcode, -1, -1, exit_addr)) { + goto jit_failure; + } zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true); } else { smart_branch_opcode = 0; exit_addr = NULL; - } - if (!zend_jit_identical(&dasm_state, opline, - op1_info, OP1_REG_ADDR(), - op2_info, OP2_REG_ADDR(), - RES_REG_ADDR(), - zend_may_throw(opline, ssa_op, op_array, ssa), - smart_branch_opcode, -1, -1, exit_addr)) { - goto jit_failure; + if (!zend_jit_identical(&dasm_state, opline, + op1_info, OP1_RANGE(), OP1_REG_ADDR(), + op2_info, OP2_RANGE(), OP2_REG_ADDR(), + RES_REG_ADDR(), + zend_may_throw(opline, ssa_op, op_array, ssa), + smart_branch_opcode, -1, -1, exit_addr)) { + goto jit_failure; + } } goto done; case ZEND_DEFINED: diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 5e097e27e7..d930ea0585 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -6572,9 +6572,129 @@ static int zend_jit_assign_op(dasm_State **Dst, const zend_op *opline, uint32_t return result; } -static int zend_jit_cmp_long_long(dasm_State **Dst, const zend_op *opline, zend_jit_addr op1_addr, zend_jit_addr op2_addr, zend_jit_addr res_addr, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) +static int zend_jit_is_constant_cmp_long_long(const zend_op *opline, + zend_ssa_range *op1_range, + zend_jit_addr op1_addr, + zend_ssa_range *op2_range, + zend_jit_addr op2_addr, + zend_bool *result) +{ + zend_long op1_min; + zend_long op1_max; + zend_long op2_min; + zend_long op2_max; + + if (op1_range) { + op1_min = op1_range->min; + op1_max = op1_range->max; + } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) { + ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG); + op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr)); + } else { + return 0; + } + + if (op2_range) { + op2_min = op2_range->min; + op2_max = op2_range->max; + } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { + ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG); + op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr)); + } else { + return 0; + } + + switch (opline->opcode) { + case ZEND_IS_EQUAL: + case ZEND_IS_IDENTICAL: + case ZEND_CASE: + case ZEND_CASE_STRICT: + if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) { + *result = 1; + return 1; + } else if (op1_max < op2_min || op1_min > op2_max) { + *result = 0; + return 1; + } + return 0; + case ZEND_IS_NOT_EQUAL: + case ZEND_IS_NOT_IDENTICAL: + if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) { + *result = 0; + return 1; + } else if (op1_max < op2_min || op1_min > op2_max) { + *result = 1; + return 1; + } + return 0; + case ZEND_IS_SMALLER: + if (op1_max < op2_min) { + *result = 1; + return 1; + } else if (op1_min >= op2_max) { + *result = 0; + return 1; + } + return 0; + case ZEND_IS_SMALLER_OR_EQUAL: + if (op1_max <= op2_min) { + *result = 1; + return 1; + } else if (op1_min > op2_max) { + *result = 0; + return 1; + } + return 0; + default: + ZEND_UNREACHABLE(); + } + return 0; +} + +static int zend_jit_cmp_long_long(dasm_State **Dst, + const zend_op *opline, + zend_ssa_range *op1_range, + zend_jit_addr op1_addr, + zend_ssa_range *op2_range, + zend_jit_addr op2_addr, + zend_jit_addr res_addr, + zend_uchar smart_branch_opcode, + uint32_t target_label, + uint32_t target_label2, + const void *exit_addr) { zend_bool swap = 0; + zend_bool result; + + if (zend_jit_is_constant_cmp_long_long(opline, op1_range, op1_addr, op2_range, op2_addr, &result)) { + if (!smart_branch_opcode || + smart_branch_opcode == ZEND_JMPZ_EX || + smart_branch_opcode == ZEND_JMPNZ_EX) { + | SET_ZVAL_TYPE_INFO res_addr, (result ? IS_TRUE : IS_FALSE) + } + if (smart_branch_opcode && !exit_addr) { + if (smart_branch_opcode == ZEND_JMPZ || + smart_branch_opcode == ZEND_JMPZ_EX) { + if (!result) { + | jmp => target_label + } + } else if (smart_branch_opcode == ZEND_JMPNZ || + smart_branch_opcode == ZEND_JMPNZ_EX) { + if (result) { + | jmp => target_label + } + } else if (smart_branch_opcode == ZEND_JMPZNZ) { + if (!result) { + | jmp => target_label + } else { + | jmp => target_label2 + } + } else { + ZEND_UNREACHABLE(); + } + } + return 1; + } if (Z_MODE(op1_addr) == IS_REG) { if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { @@ -7350,7 +7470,20 @@ static int zend_jit_cmp_slow(dasm_State **Dst, const zend_op *opline, zend_jit_a return 1; } -static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr res_addr, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) +static int zend_jit_cmp(dasm_State **Dst, + const zend_op *opline, + uint32_t op1_info, + zend_ssa_range *op1_range, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_ssa_range *op2_range, + zend_jit_addr op2_addr, + zend_jit_addr res_addr, + int may_throw, + zend_uchar smart_branch_opcode, + uint32_t target_label, + uint32_t target_label2, + const void *exit_addr) { zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var); zend_bool has_slow; @@ -7386,7 +7519,7 @@ static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_in | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9 } } - if (!zend_jit_cmp_long_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { + if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { return 0; } if (op1_info & MAY_BE_DOUBLE) { @@ -7575,7 +7708,20 @@ static int zend_jit_cmp(dasm_State **Dst, const zend_op *opline, uint32_t op1_in return 1; } -static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, zend_jit_addr op2_addr, zend_jit_addr res_addr, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) +static int zend_jit_identical(dasm_State **Dst, + const zend_op *opline, + uint32_t op1_info, + zend_ssa_range *op1_range, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_ssa_range *op2_range, + zend_jit_addr op2_addr, + zend_jit_addr res_addr, + int may_throw, + zend_uchar smart_branch_opcode, + uint32_t target_label, + uint32_t target_label2, + const void *exit_addr) { uint32_t identical_label = (uint32_t)-1; uint32_t not_identical_label = (uint32_t)-1; @@ -7608,7 +7754,7 @@ static int zend_jit_identical(dasm_State **Dst, const zend_op *opline, uint32_t if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && (op2_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) { - if (!zend_jit_cmp_long_long(Dst, opline, op1_addr, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { + if (!zend_jit_cmp_long_long(Dst, opline, op1_range, op1_addr, op2_range, op2_addr, res_addr, smart_branch_opcode, target_label, target_label2, exit_addr)) { return 0; } return 1; |