summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2020-10-07 15:23:17 +0300
committerDmitry Stogov <dmitry@zend.com>2020-10-07 15:23:17 +0300
commit3024d9c56d834cba9299971396119a160ec92af8 (patch)
tree30d1a0d25248be2132e2df6aa213fd6bedd89e0e
parent92d0a101508f535b98636509cb8695823aeeb926 (diff)
downloadphp-git-3024d9c56d834cba9299971396119a160ec92af8.tar.gz
Optimize out MAY_BE_LONG +/- 0 and MAY_BE_DOUBLE +/- 0.0
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c179
1 files changed, 163 insertions, 16 deletions
diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c
index 0605d5f299..765b023f07 100644
--- a/ext/opcache/Optimizer/dfa_pass.c
+++ b/ext/opcache/Optimizer/dfa_pass.c
@@ -1091,29 +1091,68 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
|| opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
) {
- if (opline->op1_type == IS_CONST
- && opline->op2_type != IS_CONST
- && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
- && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
- ) {
+ if (opline->op1_type == IS_CONST && opline->op2_type != IS_CONST) {
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+
+ if ((OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(zv) == IS_LONG) {
// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
- zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
+ zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+ }
+ if (opline->opcode == ZEND_ADD) {
+ zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
+
+ if (((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
+ && Z_TYPE_INFO_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) == 0)
+ || ((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(zv) == IS_DOUBLE
+ && Z_DVAL_P(zv) == 0.0)) {
+
+// op_1: #v.? = ADD 0, #?.? [double,long] => #v.? = QM_ASSIGN #?.?
+
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op1_type = opline->op2_type;
+ opline->op1.var = opline->op2.var;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.num = 0;
+ ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
+ ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
+ ssa->ops[op_1].op2_use = -1;
+ ssa->ops[op_1].op2_use_chain = -1;
+ }
+ }
+ } else if (opline->op1_type != IS_CONST && opline->op2_type == IS_CONST) {
+ zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
- } else if (opline->op1_type != IS_CONST
- && opline->op2_type == IS_CONST
- && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
- && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
- ) {
+ if ((OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG) {
// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
- zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
- ZVAL_DOUBLE(&tmp, zval_get_double(zv));
- opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ ZVAL_DOUBLE(&tmp, zval_get_double(zv));
+ opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
+ zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
+ }
+ if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB) {
+ if (((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
+ && Z_TYPE_INFO_P(zv) == IS_LONG
+ && Z_LVAL_P(zv) == 0)
+ || ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
+ && Z_TYPE_INFO_P(zv) == IS_DOUBLE
+ && Z_DVAL_P(zv) == 0.0)) {
+
+// op_1: #v.? = ADD #?.? [double,long], 0 => #v.? = QM_ASSIGN #?.?
+
+ opline->opcode = ZEND_QM_ASSIGN;
+ opline->op2_type = IS_UNUSED;
+ opline->op2.num = 0;
+ }
+ }
}
} else if (opline->opcode == ZEND_CONCAT) {
if (!(OP1_INFO() & MAY_BE_OBJECT)
@@ -1151,6 +1190,114 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
}
}
+ if (opline->opcode == ZEND_QM_ASSIGN
+ && ssa->ops[op_1].result_def == v
+ && opline->op1_type & (IS_TMP_VAR|IS_VAR)
+ && !(ssa->var_info[v].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
+ ) {
+
+ int src_var = ssa->ops[op_1].op1_use;
+
+ if (src_var >= 0
+ && !(ssa->var_info[src_var].type & MAY_BE_REF)
+ && ssa->vars[src_var].definition >= 0
+ && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
+ && ssa->ops[ssa->vars[src_var].definition].result_use < 0
+ && ssa->vars[src_var].use_chain == op_1
+ && ssa->ops[op_1].op1_use_chain < 0
+ && !ssa->vars[src_var].phi_use_chain
+ && !ssa->vars[src_var].sym_use_chain
+ && opline_supports_assign_contraction(
+ ssa, &op_array->opcodes[ssa->vars[src_var].definition],
+ src_var, opline->result.var)
+ ) {
+
+ int orig_var = ssa->ops[op_1].result_use;
+ int op_2 = ssa->vars[src_var].definition;
+
+// op_2: #src_var.T = OP ... => #v.CV = OP ...
+// op_1: QM_ASSIGN #src_var.T #orig_var.CV [undef,scalar] -> #v.CV, NOP
+
+ if (orig_var < 0 || zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
+ /* Reconstruct SSA */
+ ssa->vars[v].definition = op_2;
+ ssa->ops[op_2].result_def = v;
+
+ ssa->vars[src_var].definition = -1;
+ ssa->vars[src_var].use_chain = -1;
+
+ ssa->ops[op_1].op1_use = -1;
+ ssa->ops[op_1].op1_def = -1;
+ ssa->ops[op_1].op1_use_chain = -1;
+ ssa->ops[op_1].result_use = -1;
+ ssa->ops[op_1].result_def = -1;
+ ssa->ops[op_1].res_use_chain = -1;
+
+ /* Update opcodes */
+ op_array->opcodes[op_2].result_type = opline->result_type;
+ op_array->opcodes[op_2].result.var = opline->result.var;
+
+ MAKE_NOP(opline);
+ remove_nops = 1;
+
+ if (op_array->opcodes[op_2].opcode == ZEND_SUB
+ && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
+ && ssa->ops[op_2].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+
+ } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
+ && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op2_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
+ && ssa->ops[op_2].op1_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+
+ } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
+ && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
+ && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
+ && op_array->opcodes[op_2].op1_type == IS_CONST
+ && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
+ && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
+ && ssa->ops[op_2].op2_use >= 0
+ && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
+
+ op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
+ op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
+ op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
+ SET_UNUSED(op_array->opcodes[op_2].op2);
+ SET_UNUSED(op_array->opcodes[op_2].result);
+
+ ssa->ops[op_2].result_def = -1;
+ ssa->ops[op_2].op1_def = v;
+ ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
+ ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
+ ssa->ops[op_2].op2_use = -1;
+ ssa->ops[op_2].op2_use_chain = -1;
+ }
+ }
+ }
+ }
+
if (ssa->vars[v].var >= op_array->last_var) {
/* skip TMP and VAR */
continue;