From: Dmitry Stogov Date: Thu, 5 May 2016 11:37:41 +0000 (+0300) Subject: Refactor DFA pass for better readability X-Git-Tag: php-7.1.0alpha1~195 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=542402afdb49fe4eaa44118981fbd1e1799ee302;p=php Refactor DFA pass for better readability --- diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 16dfd16932..fac62795a4 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -316,228 +316,257 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx } if (ssa->var_info) { - int i; + int op_1; + int v; int remove_nops = 0; + zend_op *opline; + zval tmp; - // 1: #1.T = OP_Y | #3.CV = OP_Y - // 2: ASSIGN #2.CV [undef,scalar] -> #3.CV, #1.T | NOP - // -- - // 2: ASSIGN #2.CV [undef,scalar] -> #3.CV, X | 3.CV = QM_ASSIGN X + for (v = op_array->last_var; v < ssa->vars_count; v++) { - for (i = 0; i < ssa->vars_count; i++) { - int op2 = ssa->vars[i].definition; + op_1 = ssa->vars[v].definition; - if (op2 < 0) { + if (op_1 < 0) { continue; } - if (op_array->opcodes[op2].opcode == ZEND_ASSIGN - && op_array->opcodes[op2].op1_type == IS_CV - && !RETURN_VALUE_USED(&op_array->opcodes[op2]) + opline = op_array->opcodes + op_1; + + /* Convert LONG constants to DOUBLE */ + if (ssa->var_info[v].use_as_double) { + if (opline->opcode == ZEND_ASSIGN + && opline->op2_type == IS_CONST + && ssa->ops[op_1].op1_def == v + && !RETURN_VALUE_USED(opline) + ) { + +// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?) + + zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant); + ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); + ZVAL_DOUBLE(&tmp, zval_get_double(zv)); + opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); + + } else if (opline->opcode == ZEND_QM_ASSIGN + && opline->op1_type == IS_CONST + ) { + +// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?) + + zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); + ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); + ZVAL_DOUBLE(&tmp, zval_get_double(zv)); + opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp); + } + + } else { + if (opline->opcode == ZEND_ADD + || opline->opcode == ZEND_SUB + || opline->opcode == ZEND_MUL + || opline->opcode == ZEND_IS_EQUAL + || opline->opcode == ZEND_IS_NOT_EQUAL + || opline->opcode == ZEND_IS_SMALLER + || 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 + ) { + +// 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); + + } 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 + ) { + +// 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); + } + } + } + + if (ssa->vars[v].var >= op_array->last_var) { + /* skip TMP and VAR */ + continue; + } + + if (opline->opcode == ZEND_ASSIGN + && ssa->ops[op_1].op1_def == v + && !RETURN_VALUE_USED(opline) ) { - int var2 = ssa->ops[op2].op1_use; + int orig_var = ssa->ops[op_1].op1_use; - if (var2 >= 0 - && !(ssa->var_info[var2].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) + 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)) ) { - if ((op_array->opcodes[op2].op2_type & (IS_TMP_VAR|IS_VAR)) - && ssa->ops[op2].op2_use >= 0 - && !(ssa->var_info[ssa->ops[op2].op2_use].type & MAY_BE_REF) - && ssa->vars[ssa->ops[op2].op2_use].definition >= 0 - && ssa->ops[ssa->vars[ssa->ops[op2].op2_use].definition].result_def == ssa->ops[op2].op2_use - && ssa->ops[ssa->vars[ssa->ops[op2].op2_use].definition].result_use < 0 - && ssa->vars[ssa->ops[op2].op2_use].use_chain == op2 - && ssa->ops[op2].op2_use_chain < 0 - && !ssa->vars[ssa->ops[op2].op2_use].phi_use_chain - && !ssa->vars[ssa->ops[op2].op2_use].sym_use_chain + int src_var = ssa->ops[op_1].op2_use; + + if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) + && 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].op2_use_chain < 0 + && !ssa->vars[src_var].phi_use_chain + && !ssa->vars[src_var].sym_use_chain /* see Zend/tests/generators/aborted_yield_during_new.phpt */ - && op_array->opcodes[ssa->vars[ssa->ops[op2].op2_use].definition].opcode != ZEND_NEW + && op_array->opcodes[ssa->vars[src_var].definition].opcode != ZEND_NEW ) { - int var1 = ssa->ops[op2].op2_use; - int op1 = ssa->vars[var1].definition; - int var3 = i; - if (zend_ssa_unlink_use_chain(ssa, op2, var2)) { + int op_2 = ssa->vars[src_var].definition; + +// op_2: #src_var.T = OP ... => #var0.CV = OP ... +// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #var0.CV, #src_var.T NOP + + if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { /* Reconstruct SSA */ - ssa->vars[var3].definition = op1; - ssa->ops[op1].result_def = var3; + ssa->vars[v].definition = op_2; + ssa->ops[op_2].result_def = v; - ssa->vars[var1].definition = -1; - ssa->vars[var1].use_chain = -1; + ssa->vars[src_var].definition = -1; + ssa->vars[src_var].use_chain = -1; - ssa->ops[op2].op1_use = -1; - ssa->ops[op2].op2_use = -1; - ssa->ops[op2].op1_def = -1; - ssa->ops[op2].op1_use_chain = -1; + ssa->ops[op_1].op1_use = -1; + ssa->ops[op_1].op2_use = -1; + ssa->ops[op_1].op1_def = -1; + ssa->ops[op_1].op1_use_chain = -1; /* Update opcodes */ - op_array->opcodes[op1].result_type = op_array->opcodes[op2].op1_type; - op_array->opcodes[op1].result.var = op_array->opcodes[op2].op1.var; - MAKE_NOP(&op_array->opcodes[op2]); + op_array->opcodes[op_2].result_type = opline->op1_type; + op_array->opcodes[op_2].result.var = opline->op1.var; + MAKE_NOP(opline); remove_nops = 1; } - } else if (op_array->opcodes[op2].op2_type == IS_CONST - || ((op_array->opcodes[op2].op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) - && ssa->ops[op2].op2_use >= 0 - && ssa->ops[op2].op2_def < 0) + } else if (opline->op2_type == IS_CONST + || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV)) + && ssa->ops[op_1].op2_use >= 0 + && ssa->ops[op_1].op2_def < 0) ) { - int var3 = i; - if (zend_ssa_unlink_use_chain(ssa, op2, var2)) { +// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #var0.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR + + if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) { /* Reconstruct SSA */ - ssa->ops[op2].result_def = var3; - ssa->ops[op2].op1_def = -1; - ssa->ops[op2].op1_use = ssa->ops[op2].op2_use; - ssa->ops[op2].op1_use_chain = ssa->ops[op2].op2_use_chain; - ssa->ops[op2].op2_use = -1; - ssa->ops[op2].op2_use_chain = -1; + ssa->ops[op_1].result_def = v; + ssa->ops[op_1].op1_def = -1; + 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; /* Update opcode */ - op_array->opcodes[op2].result_type = op_array->opcodes[op2].op1_type; - op_array->opcodes[op2].result.var = op_array->opcodes[op2].op1.var; - op_array->opcodes[op2].op1_type = op_array->opcodes[op2].op2_type; - op_array->opcodes[op2].op1.var = op_array->opcodes[op2].op2.var; - op_array->opcodes[op2].op2_type = IS_UNUSED; - op_array->opcodes[op2].op2.var = 0; - op_array->opcodes[op2].opcode = ZEND_QM_ASSIGN; + opline->result_type = opline->op1_type; + opline->result.var = opline->op1.var; + opline->op1_type = opline->op2_type; + opline->op1.var = opline->op2.var; + opline->op2_type = IS_UNUSED; + opline->op2.var = 0; + opline->opcode = ZEND_QM_ASSIGN; } } } - } else if (op_array->opcodes[op2].opcode == ZEND_ASSIGN_ADD - && op_array->opcodes[op2].extended_value == 0 - && ssa->ops[op2].op1_def == i - && op_array->opcodes[op2].op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == 1 - && ssa->ops[op2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op2].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[op2].opcode = ZEND_PRE_INC; - SET_UNUSED(op_array->opcodes[op2].op2); - - } else if (op_array->opcodes[op2].opcode == ZEND_ASSIGN_SUB - && op_array->opcodes[op2].extended_value == 0 - && ssa->ops[op2].op1_def == i - && op_array->opcodes[op2].op2_type == IS_CONST - && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == IS_LONG - && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op2].op2.constant)) == 1 - && ssa->ops[op2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op2].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[op2].opcode = ZEND_PRE_DEC; - SET_UNUSED(op_array->opcodes[op2].op2); - - } else if (op_array->opcodes[op2].opcode == ZEND_VERIFY_RETURN_TYPE - && ssa->ops[op2].op1_def == i - && ssa->ops[op2].op1_use >= 0 - && ssa->ops[op2].op1_use_chain == -1 - && ssa->vars[i].use_chain >= 0 - && (ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op2].op1_def].type & MAY_BE_ANY)) { - /* remove useless type check */ - int var1 = ssa->ops[op2].op1_use; - int ret = ssa->vars[i].use_chain; - - ssa->vars[var1].use_chain = ret; - ssa->ops[ret].op1_use = var1; - - ssa->vars[i].definition = -1; - ssa->vars[i].use_chain = -1; - - ssa->ops[op2].op1_def = -1; - ssa->ops[op2].op1_use = -1; - - MAKE_NOP(&op_array->opcodes[op2]); + } else if (opline->opcode == ZEND_ASSIGN_ADD + && opline->extended_value == 0 + && ssa->ops[op_1].op1_def == v + && opline->op2_type == IS_CONST + && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG + && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1 + && ssa->ops[op_1].op1_use >= 0 + && !(ssa->var_info[ssa->ops[op_1].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_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#var0.CV, int(1) => PRE_INC #?.CV ->#var0.CV + + opline->opcode = ZEND_PRE_INC; + SET_UNUSED(opline->op2); + + } else if (opline->opcode == ZEND_ASSIGN_SUB + && opline->extended_value == 0 + && ssa->ops[op_1].op1_def == v + && opline->op2_type == IS_CONST + && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG + && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1 + && ssa->ops[op_1].op1_use >= 0 + && !(ssa->var_info[ssa->ops[op_1].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_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #var0.CV, int(1) => PRE_DEC #?.CV ->#var0.CV + + opline->opcode = ZEND_PRE_DEC; + SET_UNUSED(opline->op2); + + } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE + && ssa->ops[op_1].op1_def == v + && ssa->ops[op_1].op1_use >= 0 + && ssa->ops[op_1].op1_use_chain == -1 + && ssa->vars[v].use_chain >= 0 + && (ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_ANY|MAY_BE_UNDEF)) == (ssa->var_info[ssa->ops[op_1].op1_def].type & MAY_BE_ANY)) { + +// op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #var0.CV [T] => NOP + + int orig_var = ssa->ops[op_1].op1_use; + int ret = ssa->vars[v].use_chain; + + ssa->vars[orig_var].use_chain = ret; + ssa->ops[ret].op1_use = orig_var; + + ssa->vars[v].definition = -1; + ssa->vars[v].use_chain = -1; + + ssa->ops[op_1].op1_def = -1; + ssa->ops[op_1].op1_use = -1; + + MAKE_NOP(opline); remove_nops = 1; - } else if (ssa->ops[op2].op1_def == i - && !RETURN_VALUE_USED(&op_array->opcodes[op2]) - && ssa->ops[op2].op1_use >= 0 - && !(ssa->var_info[ssa->ops[op2].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - && (op_array->opcodes[op2].opcode == ZEND_ASSIGN_ADD - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_SUB - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_MUL - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_DIV - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_MOD - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_SL - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_SR - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_BW_OR - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_BW_AND - || op_array->opcodes[op2].opcode == ZEND_ASSIGN_BW_XOR) - && op_array->opcodes[op2].extended_value == 0) { + } else if (ssa->ops[op_1].op1_def == v + && !RETURN_VALUE_USED(opline) + && ssa->ops[op_1].op1_use >= 0 + && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) + && (opline->opcode == ZEND_ASSIGN_ADD + || opline->opcode == ZEND_ASSIGN_SUB + || opline->opcode == ZEND_ASSIGN_MUL + || opline->opcode == ZEND_ASSIGN_DIV + || opline->opcode == ZEND_ASSIGN_MOD + || opline->opcode == ZEND_ASSIGN_SL + || opline->opcode == ZEND_ASSIGN_SR + || opline->opcode == ZEND_ASSIGN_BW_OR + || opline->opcode == ZEND_ASSIGN_BW_AND + || opline->opcode == ZEND_ASSIGN_BW_XOR) + && opline->extended_value == 0) { + +// op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #var0.CV, ? => #var0.CV = ADD #orig_var.CV, ? /* Reconstruct SSA */ - ssa->ops[op2].result_def = ssa->ops[op2].op1_def; - ssa->ops[op2].op1_def = -1; + ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def; + ssa->ops[op_1].op1_def = -1; /* Update opcode */ - op_array->opcodes[op2].opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD); - op_array->opcodes[op2].result_type = op_array->opcodes[op2].op1_type; - op_array->opcodes[op2].result.var = op_array->opcodes[op2].op1.var; + opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD); + opline->result_type = opline->op1_type; + opline->result.var = opline->op1.var; } } + if (remove_nops) { zend_ssa_remove_nops(op_array, ssa); } } - if (ssa->var_info) { - int i; - zend_op *opline; - zval tmp; - - /* Convert LONG constants to DOUBLE */ - for (i = 0; i < ssa->vars_count; i++) { - if (ssa->vars[i].definition >= 0) { - int op = ssa->vars[i].definition; - - opline = op_array->opcodes + op; - if (ssa->var_info[i].use_as_double) { - if (opline->opcode == ZEND_ASSIGN && - opline->op2_type == IS_CONST) { - zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant); - ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); - ZVAL_DOUBLE(&tmp, zval_get_double(zv)); - opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp); - } else if (opline->opcode == ZEND_QM_ASSIGN && - opline->op1_type == IS_CONST) { - zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant); - ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG); - ZVAL_DOUBLE(&tmp, zval_get_double(zv)); - opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp); - } - } else { - if (opline->opcode == ZEND_ADD || - opline->opcode == ZEND_SUB || - opline->opcode == ZEND_MUL || - opline->opcode == ZEND_IS_EQUAL || - opline->opcode == ZEND_IS_NOT_EQUAL || - opline->opcode == ZEND_IS_SMALLER || - 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) { - 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); - } 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) { - 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); - } - - } - } - } - } - } - if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) { zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa); }