]> granicus.if.org Git - php/commitdiff
Optimize out result value of ASSIGN, ASSIGN_OP and INC/DEC opcodes, if possible.
authorDmitry Stogov <dmitry@zend.com>
Tue, 15 Dec 2020 11:30:58 +0000 (14:30 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 15 Dec 2020 11:30:58 +0000 (14:30 +0300)
ext/opcache/Optimizer/dfa_pass.c

index 92a7470b9e0ad3dd5ba56392d877273a521b237c..0ac6c563d08e6bbdda2c4323c3ceeccce64cb5d5 100644 (file)
@@ -985,6 +985,77 @@ optimize_nop:
        return removed_ops;
 }
 
+static int zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ssa, int def, int cv_var)
+{
+       int result_var = ssa->ops[def].result_def;
+       int cv = EX_NUM_TO_VAR(ssa->vars[cv_var].var);
+
+       if (result_var >= 0
+        && !(ssa->var_info[cv_var].type & MAY_BE_REF)
+        && ssa->vars[cv_var].alias == NO_ALIAS
+        && ssa->vars[result_var].phi_use_chain == NULL
+        && ssa->vars[result_var].sym_use_chain == NULL) {
+               int use = ssa->vars[result_var].use_chain;
+
+               if (use >= 0
+                && zend_ssa_next_use(ssa->ops, result_var, use) < 0
+                && op_array->opcodes[use].opcode != ZEND_FREE
+                && op_array->opcodes[use].opcode != ZEND_SEND_VAL
+                && op_array->opcodes[use].opcode != ZEND_SEND_VAL_EX
+                && op_array->opcodes[use].opcode != ZEND_VERIFY_RETURN_TYPE) {
+                       if (use > def) {
+                               int i = use;
+                               const zend_op *opline = &op_array->opcodes[use];
+
+                               while (i > def) {
+                                       if ((opline->op1_type == IS_CV && opline->op1.var == cv)
+                                        || (opline->op2_type == IS_CV && opline->op2.var == cv)
+                                        || (opline->result_type == IS_CV && opline->result.var == cv)) {
+                                               return 0;
+                                       }
+                                       opline--;
+                                       i--;
+                               }
+
+                               /* Update opcodes and reconstruct SSA */
+                               ssa->vars[result_var].definition = -1;
+                               ssa->vars[result_var].use_chain = -1;
+                               ssa->ops[def].result_def = -1;
+
+                               op_array->opcodes[def].result_type = IS_UNUSED;
+                               op_array->opcodes[def].result.var = 0;
+
+                               if (ssa->ops[use].op1_use == result_var) {
+                                       ssa->ops[use].op1_use = cv_var;
+                                       ssa->ops[use].op1_use_chain = ssa->vars[cv_var].use_chain;
+                                       ssa->vars[cv_var].use_chain = use;
+
+                                       op_array->opcodes[use].op1_type = IS_CV;
+                                       op_array->opcodes[use].op1.var = cv;
+                               } else if (ssa->ops[use].op2_use == result_var) {
+                                       ssa->ops[use].op2_use = cv_var;
+                                       ssa->ops[use].op2_use_chain = ssa->vars[cv_var].use_chain;
+                                       ssa->vars[cv_var].use_chain = use;
+
+                                       op_array->opcodes[use].op2_type = IS_CV;
+                                       op_array->opcodes[use].op2.var = cv;
+                               } else if (ssa->ops[use].result_use == result_var) {
+                                       ssa->ops[use].result_use = cv_var;
+                                       ssa->ops[use].res_use_chain = ssa->vars[cv_var].use_chain;
+                                       ssa->vars[cv_var].use_chain = use;
+
+                                       op_array->opcodes[use].result_type = IS_CV;
+                                       op_array->opcodes[use].result.var = cv;
+                               }
+
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
 {
        if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
@@ -1303,6 +1374,44 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
                                continue;
                        }
 
+                       if (ssa->ops[op_1].op1_def == v
+                        && RETURN_VALUE_USED(opline)) {
+                               if (opline->opcode == ZEND_ASSIGN
+                                || opline->opcode == ZEND_ASSIGN_OP
+                                || opline->opcode == ZEND_PRE_INC
+                                || opline->opcode == ZEND_PRE_DEC) {
+                                       zend_dfa_try_to_replace_result(op_array, ssa, op_1, v);
+                               } else if (opline->opcode == ZEND_POST_INC) {
+                                       int result_var = ssa->ops[op_1].result_def;
+
+                                       if (result_var >= 0
+                                        && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
+                                               int use = ssa->vars[result_var].use_chain;
+
+                                               if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER
+                                                && ssa->ops[use].op1_use == result_var
+                                                && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
+                                                       opline->opcode = ZEND_PRE_INC;
+                                                       op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
+                                               }
+                                       }
+                               } else if (opline->opcode == ZEND_POST_DEC) {
+                                       int result_var = ssa->ops[op_1].result_def;
+
+                                       if (result_var >= 0
+                                        && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
+                                               int use = ssa->vars[result_var].use_chain;
+
+                                               if (op_array->opcodes[use].opcode == ZEND_IS_SMALLER
+                                                && ssa->ops[use].op2_use == result_var
+                                                && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
+                                                       opline->opcode = ZEND_PRE_DEC;
+                                                       op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
+                                               }
+                                       }
+                               }
+                       }
+
                        if (opline->opcode == ZEND_ASSIGN
                         && ssa->ops[op_1].op1_def == v
                         && !RETURN_VALUE_USED(opline)
@@ -1312,7 +1421,6 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
                                if (orig_var >= 0
                                 && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
                                ) {
-
                                        int src_var = ssa->ops[op_1].op2_use;
 
                                        if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))