summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2020-12-24 16:58:54 +0300
committerDmitry Stogov <dmitry@zend.com>2020-12-24 16:58:54 +0300
commit23bbff2b0556fe9c0b6ca8b02076b6dea6e43678 (patch)
tree24696a19b933b9bd2f529ffb8ced702d5eeaed1d
parentab4c5976d7a07644930ac49abd4552008a1e5918 (diff)
downloadphp-git-23bbff2b0556fe9c0b6ca8b02076b6dea6e43678.tar.gz
Eliminate redundand comparison insructions
-rw-r--r--ext/opcache/jit/zend_jit.c4
-rw-r--r--ext/opcache/jit/zend_jit_trace.c106
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc21
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;