From 23bbff2b0556fe9c0b6ca8b02076b6dea6e43678 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 24 Dec 2020 16:58:54 +0300 Subject: [PATCH] Eliminate redundand comparison insructions --- ext/opcache/jit/zend_jit.c | 4 +- ext/opcache/jit/zend_jit_trace.c | 106 ++++++++++++++++++++++++++++-- ext/opcache/jit/zend_jit_x86.dasc | 21 ++++-- 3 files changed, 117 insertions(+), 14 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index eb80d5240b..a983a818a9 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2842,7 +2842,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op res_addr, zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, target_label, target_label2, - NULL)) { + NULL, 0)) { goto jit_failure; } goto done; @@ -2871,7 +2871,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, target_label, target_label2, - NULL)) { + NULL, 0)) { goto jit_failure; } goto done; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index ad4ce03a04..0b4edb1092 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -3431,6 +3431,89 @@ static void zend_jit_trace_update_condition_ranges(const zend_op *opline, const } } +static zend_bool zend_jit_may_skip_comparison(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_ssa *ssa, const zend_op **ssa_opcodes) +{ + zend_uchar prev_opcode; + + if (opline->op1_type == IS_CONST + && Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_LONG + && Z_LVAL_P(RT_CONSTANT(opline, opline->op1)) == 0) { + if (ssa_op->op2_use >= 0) { + if ((ssa_op-1)->op1_def == ssa_op->op2_use) { + prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode; + if (prev_opcode == ZEND_PRE_INC + || prev_opcode == ZEND_PRE_DEC + || prev_opcode == ZEND_POST_INC + || prev_opcode == ZEND_POST_DEC) { + return 1; + } + } else if ((ssa_op-1)->result_def == ssa_op->op2_use) { + prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode; + if (prev_opcode == ZEND_ADD + || prev_opcode == ZEND_SUB) { + return 1; + } + } + } + } else if (opline->op2_type == IS_CONST + && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG + && Z_LVAL_P(RT_CONSTANT(opline, opline->op2)) == 0) { + if (ssa_op->op1_use >= 0) { + if ((ssa_op-1)->op1_def == ssa_op->op1_use) { + prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode; + if (prev_opcode == ZEND_PRE_INC + || prev_opcode == ZEND_PRE_DEC + || prev_opcode == ZEND_POST_INC + || prev_opcode == ZEND_POST_DEC) { + return 1; + } + } else if ((ssa_op-1)->result_def == ssa_op->op1_use) { + prev_opcode = ssa_opcodes[(ssa_op - ssa->ops) - 1]->opcode; + if (prev_opcode == ZEND_ADD + || prev_opcode == ZEND_SUB) { + return 1; + } + } + } + } else { + const zend_ssa_op *prev_ssa_op = ssa_op - 1; + prev_opcode = ssa_opcodes[prev_ssa_op - ssa->ops]->opcode; + + if ((prev_opcode == ZEND_JMPZ || prev_opcode == ZEND_JMPNZ) + && prev_ssa_op != ssa->ops + && prev_ssa_op->op1_use >= 0 + && prev_ssa_op->op1_use == (prev_ssa_op-1)->result_def) { + prev_ssa_op--; + prev_opcode = ssa_opcodes[prev_ssa_op - ssa->ops]->opcode; + } + + if (ssa_op->op1_use == prev_ssa_op->op1_use + && ssa_op->op2_use == prev_ssa_op->op2_use) { + if (prev_opcode == ZEND_IS_EQUAL + || prev_opcode == ZEND_IS_NOT_EQUAL + || prev_opcode == ZEND_IS_SMALLER + || prev_opcode == ZEND_IS_SMALLER_OR_EQUAL + || prev_opcode == ZEND_CASE + || prev_opcode == ZEND_IS_IDENTICAL + || prev_opcode == ZEND_IS_NOT_IDENTICAL + || prev_opcode == ZEND_CASE_STRICT) { + if (ssa_op->op1_use < 0) { + if (opline->op1.constant != ssa_opcodes[prev_ssa_op - ssa->ops]->op1.constant) { + return 0; + } + } + if (ssa_op->op2_use < 0) { + if (opline->op2.constant != ssa_opcodes[prev_ssa_op - ssa->ops]->op2.constant) { + return 0; + } + } + return 1; + } + } + } + return 0; +} + static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num) { const void *handler = NULL; @@ -3451,6 +3534,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par const void *exit_addr; uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_data_info; zend_bool send_result = 0; + zend_bool skip_comparison; zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; zend_class_entry *ce; zend_bool ce_is_instanceof; @@ -4675,8 +4759,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_IS_SMALLER_OR_EQUAL: case ZEND_CASE: op1_info = OP1_INFO(); - CHECK_OP1_TRACE_TYPE(); op2_info = OP2_INFO(); + skip_comparison = + ssa_op != ssa->ops && + (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && + (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && + zend_jit_may_skip_comparison(opline, ssa_op, ssa, ssa_opcodes); + CHECK_OP1_TRACE_TYPE(); CHECK_OP2_TRACE_TYPE(); if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { zend_bool exit_if_true = 0; @@ -4697,7 +4786,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par 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)) { + smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) { goto jit_failure; } zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true); @@ -4709,7 +4798,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par 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)) { + smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) { goto jit_failure; } } @@ -4718,8 +4807,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_IS_NOT_IDENTICAL: case ZEND_CASE_STRICT: op1_info = OP1_INFO(); - CHECK_OP1_TRACE_TYPE(); op2_info = OP2_INFO(); + skip_comparison = + ssa_op != ssa->ops && + (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && + (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && + zend_jit_may_skip_comparison(opline, ssa_op, ssa, ssa_opcodes); + CHECK_OP1_TRACE_TYPE(); CHECK_OP2_TRACE_TYPE(); if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { zend_bool exit_if_true = 0; @@ -4743,7 +4837,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par 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)) { + smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) { goto jit_failure; } zend_jit_trace_update_condition_ranges(opline, ssa_op, op_array, ssa, exit_if_true); @@ -4755,7 +4849,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par 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)) { + smart_branch_opcode, -1, -1, exit_addr, skip_comparison)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 404e402ec7..6723cb8f7e 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -6745,7 +6745,8 @@ static int zend_jit_cmp_long_long(dasm_State **Dst, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, - const void *exit_addr) + const void *exit_addr, + zend_bool skip_comparison) { zend_bool swap = 0; zend_bool result; @@ -6780,7 +6781,13 @@ static int zend_jit_cmp_long_long(dasm_State **Dst, return 1; } - if (Z_MODE(op1_addr) == IS_REG) { + if (skip_comparison) { + if (Z_MODE(op1_addr) != IS_REG && + (Z_MODE(op2_addr) == IS_REG || + (Z_MODE(op1_addr) == IS_CONST_ZVAL && Z_MODE(op2_addr) != IS_CONST_ZVAL))) { + swap = 1; + } + } else if (Z_MODE(op1_addr) == IS_REG) { if (Z_MODE(op2_addr) == IS_CONST_ZVAL && Z_LVAL_P(Z_ZV(op2_addr)) == 0) { | test Ra(Z_REG(op1_addr)), Ra(Z_REG(op1_addr)) } else { @@ -7567,7 +7574,8 @@ static int zend_jit_cmp(dasm_State **Dst, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, - const void *exit_addr) + const void *exit_addr, + zend_bool skip_comparison) { zend_bool same_ops = (opline->op1_type == opline->op2_type) && (opline->op1.var == opline->op2.var); zend_bool has_slow; @@ -7603,7 +7611,7 @@ static int zend_jit_cmp(dasm_State **Dst, | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >9 } } - 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)) { + 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, skip_comparison)) { return 0; } if (op1_info & MAY_BE_DOUBLE) { @@ -7805,7 +7813,8 @@ static int zend_jit_identical(dasm_State **Dst, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, - const void *exit_addr) + const void *exit_addr, + zend_bool skip_comparison) { uint32_t identical_label = (uint32_t)-1; uint32_t not_identical_label = (uint32_t)-1; @@ -7838,7 +7847,7 @@ static int zend_jit_identical(dasm_State **Dst, 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_range, op1_addr, op2_range, 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, skip_comparison)) { return 0; } return 1; -- 2.40.0