From 624f76df48db42f616bdfd02e9b26515a97c68e2 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 13 Jul 2017 12:30:27 +0300 Subject: [PATCH] Set RETURN_VALUE_UNUSED instead of additional FREE opcode, if possible. Keep alive dead instructions that have to free two temporary variables. --- ext/opcache/Optimizer/dce.c | 84 +++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 7 deletions(-) diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c index af2bc7a5e20..f86176efed6 100644 --- a/ext/opcache/Optimizer/dce.c +++ b/ext/opcache/Optimizer/dce.c @@ -276,6 +276,68 @@ static inline zend_bool is_var_dead(context *ctx, int var_num) { } } +// Sometimes we can mark the var as EXT_UNUSED +static zend_bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) { + if (use_chain >= 0) { + return 0; + } + zend_ssa_var *var = &ctx->ssa->vars[free_var]; + int def = var->definition; + + if (def >= 0) { + zend_ssa_op *def_op = &ctx->ssa->ops[def]; + + if (def_op->result_def == free_var + && var->phi_use_chain == NULL + && var->use_chain == (opline - ctx->op_array->opcodes)) { + zend_op *def_opline = &ctx->op_array->opcodes[def]; + + switch (def_opline->opcode) { + case ZEND_ASSIGN: + case ZEND_ASSIGN_REF: + case ZEND_ASSIGN_DIM: + case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_ASSIGN_POW: + case ZEND_PRE_INC: + case ZEND_POST_INC: + case ZEND_PRE_DEC: + case ZEND_POST_DEC: + case ZEND_PRE_INC_OBJ: + case ZEND_POST_INC_OBJ: + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_DEC_OBJ: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_DO_FCALL: + case ZEND_INCLUDE_OR_EVAL: + case ZEND_YIELD: + case ZEND_YIELD_FROM: + case ZEND_ASSERT_CHECK: + def_opline->result_type = IS_UNUSED; + def_opline->result.var = 0; + def_op->result_def = -1; + var->definition = -1; + return 1; + default: + break; + } + } + } + return 0; +} + /* Returns whether the instruction has been DCEd */ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) { zend_ssa *ssa = ctx->ssa; @@ -291,20 +353,28 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) { return 0; } - // TODO Two free vars? if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op1_use)) { - free_var = ssa_op->op1_use; - free_var_type = opline->op1_type; - } else if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) { - free_var = ssa_op->op2_use; - free_var_type = opline->op2_type; + if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) { + free_var = ssa_op->op1_use; + free_var_type = opline->op1_type; + } + } + if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) { + if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) { + if (free_var >= 0) { + // TODO: We can't free two vars. Keep instruction alive. + zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes); + return 0; + } + free_var = ssa_op->op2_use; + free_var_type = opline->op2_type; + } } zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op); zend_ssa_remove_instr(ctx->ssa, opline, ssa_op); if (free_var >= 0) { - // TODO Sometimes we can mark the var as EXT_UNUSED opline->opcode = ZEND_FREE; opline->op1.var = (uintptr_t) ZEND_CALL_VAR_NUM(NULL, ssa->vars[free_var].var); opline->op1_type = free_var_type;