From 3024d9c56d834cba9299971396119a160ec92af8 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 7 Oct 2020 15:23:17 +0300 Subject: [PATCH] Optimize out MAY_BE_LONG +/- 0 and MAY_BE_DOUBLE +/- 0.0 --- ext/opcache/Optimizer/dfa_pass.c | 179 ++++++++++++++++++++++++++++--- 1 file 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; -- 2.40.0