From: Dmitry Stogov Date: Tue, 15 Dec 2020 11:30:58 +0000 (+0300) Subject: Optimize out result value of ASSIGN, ASSIGN_OP and INC/DEC opcodes, if possible. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=de78786566e6094cd6517d99a57ad5b97f95a18b;p=php Optimize out result value of ASSIGN, ASSIGN_OP and INC/DEC opcodes, if possible. --- diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index 92a7470b9e..0ac6c563d0 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -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))