]> granicus.if.org Git - php/commitdiff
Eliminate redundand comparison insructions
authorDmitry Stogov <dmitry@zend.com>
Thu, 24 Dec 2020 13:58:54 +0000 (16:58 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 24 Dec 2020 13:58:54 +0000 (16:58 +0300)
ext/opcache/jit/zend_jit.c
ext/opcache/jit/zend_jit_trace.c
ext/opcache/jit/zend_jit_x86.dasc

index eb80d5240bce6c0fe400ff2da4ec867d0231dcf7..a983a818a9ee0a2b652a7ae6e798ee622c449310 100644 (file)
@@ -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;
index ad4ce03a048e4f8a9df63a86db03c776e5b9963d..0b4edb10925cda105fa7f1779d7c99c7103c0d54 100644 (file)
@@ -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;
                                                        }
                                                }
index 404e402ec7e6697b675320c9765f6f9d461e8178..6723cb8f7e320e06b009d5fb35af3742f0bcb4af 100644 (file)
@@ -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;