From 3269e884686ada59407e14db812bfb42d59d2f1c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 17 Jan 2019 16:07:17 +0100 Subject: Implement single-pass live range calculation Instead of interleaving creation of live-ranges with the main compiler code, compute them in a separate pass over the opcodes as part of pass_two. Additionally, do not keep live ranges synchronized during optimization in opcache and instead use the same mechanism to recompute them after optimization. --- ext/opcache/Optimizer/block_pass.c | 45 +---- ext/opcache/Optimizer/compact_vars.c | 9 - ext/opcache/Optimizer/dce.c | 77 --------- ext/opcache/Optimizer/dfa_pass.c | 9 - ext/opcache/Optimizer/nop_removal.c | 6 - ext/opcache/Optimizer/optimize_temp_vars_5.c | 8 - ext/opcache/Optimizer/sccp.c | 8 - ext/opcache/Optimizer/scdf.c | 24 +-- ext/opcache/Optimizer/zend_cfg.c | 75 +++------ ext/opcache/Optimizer/zend_cfg.h | 5 +- ext/opcache/Optimizer/zend_dump.c | 28 +--- ext/opcache/Optimizer/zend_dump.h | 1 + ext/opcache/Optimizer/zend_optimizer.c | 213 +++++++----------------- ext/opcache/Optimizer/zend_optimizer_internal.h | 3 +- ext/opcache/Optimizer/zend_ssa.c | 3 - 15 files changed, 108 insertions(+), 406 deletions(-) (limited to 'ext/opcache/Optimizer') diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index b36b68dc05..5aa32bfe03 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -197,7 +197,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array ) { znode_op op1 = opline->op1; if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) { - zend_optimizer_remove_live_range(op_array, op1.var); COPY_NODE(opline->result, opline->op1); COPY_NODE(opline->op1, src->op1); VAR_SOURCE(op1) = NULL; @@ -207,7 +206,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array zval c; ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src)); if (zend_optimizer_update_op1_const(op_array, opline, &c)) { - zend_optimizer_remove_live_range(op_array, op1.var); VAR_SOURCE(op1) = NULL; literal_dtor(&ZEND_OP1_LITERAL(src)); MAKE_NOP(src); @@ -277,7 +275,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src)); if (zend_optimizer_update_op2_const(op_array, opline, &c)) { - zend_optimizer_remove_live_range(op_array, op2.var); VAR_SOURCE(op2) = NULL; literal_dtor(&ZEND_OP1_LITERAL(src)); MAKE_NOP(src); @@ -295,7 +292,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src->opcode == ZEND_CAST && src->extended_value == IS_STRING) { /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */ - zend_optimizer_remove_live_range(op_array, opline->op1.var); VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); @@ -731,7 +727,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src->opcode == ZEND_CAST && src->extended_value == IS_STRING) { /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ - zend_optimizer_remove_live_range(op_array, opline->op1.var); VAR_SOURCE(opline->op1) = NULL; COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); @@ -744,7 +739,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array src->opcode == ZEND_CAST && src->extended_value == IS_STRING) { /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */ - zend_optimizer_remove_live_range(op_array, opline->op2.var); zend_op *src = VAR_SOURCE(opline->op2); VAR_SOURCE(opline->op2) = NULL; COPY_NODE(opline->op2, src->op1); @@ -1085,43 +1079,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op free_alloca(map, use_heap); } - /* adjust loop jump targets & remove unused live range entries */ - if (op_array->last_live_range) { - int i, j; - - for (i = 0, j = 0; i < op_array->last_live_range; i++) { - if (op_array->live_range[i].var == (uint32_t)-1) { - /* this live range already removed */ - continue; - } - if (!(blocks[cfg->map[op_array->live_range[i].start]].flags & ZEND_BB_REACHABLE)) { - ZEND_ASSERT(!(blocks[cfg->map[op_array->live_range[i].end]].flags & ZEND_BB_REACHABLE)); - } else { - uint32_t start_op = blocks[cfg->map[op_array->live_range[i].start]].start; - uint32_t end_op = blocks[cfg->map[op_array->live_range[i].end]].start; - - if (start_op == end_op) { - /* skip empty live range */ - continue; - } - op_array->live_range[i].start = start_op; - op_array->live_range[i].end = end_op; - if (i != j) { - op_array->live_range[j] = op_array->live_range[i]; - } - j++; - } - } - - if (i != j) { - op_array->last_live_range = j; - if (j == 0) { - efree(op_array->live_range); - op_array->live_range = NULL; - } - } - } - /* adjust early binding list */ if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) { ZEND_ASSERT(op_array == &ctx->script->main_op_array); @@ -1943,7 +1900,7 @@ void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* Build CFG */ checkpoint = zend_arena_checkpoint(ctx->arena); - if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg) != SUCCESS) { + if (zend_build_cfg(&ctx->arena, op_array, 0, &cfg) != SUCCESS) { zend_arena_release(&ctx->arena, checkpoint); return; } diff --git a/ext/opcache/Optimizer/compact_vars.c b/ext/opcache/Optimizer/compact_vars.c index c7bda48c88..53db3c9b8d 100644 --- a/ext/opcache/Optimizer/compact_vars.c +++ b/ext/opcache/Optimizer/compact_vars.c @@ -95,15 +95,6 @@ void zend_optimizer_compact_vars(zend_op_array *op_array) { } } - /* Update TMP references in live ranges */ - if (op_array->live_range) { - for (i = 0; i < op_array->last_live_range; i++) { - op_array->live_range[i].var = - (op_array->live_range[i].var & ZEND_LIVE_MASK) | - NUM_VAR(vars_map[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK)]); - } - } - /* Update CV name table */ if (num_cvs != op_array->last_var) { if (num_cvs) { diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c index 95755d559f..a633e9ddf6 100644 --- a/ext/opcache/Optimizer/dce.c +++ b/ext/opcache/Optimizer/dce.c @@ -478,79 +478,6 @@ static inline zend_bool may_break_varargs(const zend_op_array *op_array, const z return 0; } -static void dce_live_ranges(context *ctx, zend_op_array *op_array, zend_ssa *ssa) -{ - int i = 0; - int j = 0; - zend_live_range *live_range = op_array->live_range; - - while (i < op_array->last_live_range) { - if ((live_range->var & ZEND_LIVE_MASK) != ZEND_LIVE_TMPVAR) { - /* keep */ - j++; - } else { - uint32_t var = live_range->var & ~ZEND_LIVE_MASK; - uint32_t def = live_range->start - 1; - - if ((op_array->opcodes[def].result_type == IS_UNUSED) && - (UNEXPECTED(op_array->opcodes[def].opcode == ZEND_EXT_STMT) || - UNEXPECTED(op_array->opcodes[def].opcode == ZEND_EXT_FCALL_END))) { - def--; - } - - if (op_array->opcodes[def].result_type == IS_UNUSED) { - if (op_array->opcodes[def].opcode == ZEND_DO_FCALL) { - /* constructor call */ - do { - def--; - if ((op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR)) - && op_array->opcodes[def].result.var == var) { - ZEND_ASSERT(op_array->opcodes[def].opcode == ZEND_NEW); - break; - } - } while (def > 0); - } else if (op_array->opcodes[def].opcode == ZEND_OP_DATA) { - def--; - } - } - -#if ZEND_DEBUG - ZEND_ASSERT(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR)); - ZEND_ASSERT(op_array->opcodes[def].result.var == var); - ZEND_ASSERT(ssa->ops[def].result_def >= 0); -#else - if (!(op_array->opcodes[def].result_type & (IS_TMP_VAR|IS_VAR)) - || op_array->opcodes[def].result.var != var - || ssa->ops[def].result_def < 0) { - /* TODO: Some wrong live-range? keep it. */ - j++; - live_range++; - i++; - continue; - } -#endif - - var = ssa->ops[def].result_def; - - if ((ssa->var_info[var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) - && !is_var_dead(ctx, var)) { - /* keep */ - j++; - } else if (i != j) { - op_array->live_range[j] = *live_range; - } - } - - live_range++; - i++; - } - op_array->last_live_range = j; - if (op_array->last_live_range == 0) { - efree(op_array->live_range); - op_array->live_range = NULL; - } -} - int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects) { int i; zend_ssa_phi *phi; @@ -648,10 +575,6 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor } } - if (op_array->live_range) { - dce_live_ranges(&ctx, op_array, ssa); - } - /* Eliminate dead instructions */ ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) { removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]); diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index d50747eef1..79717a64bd 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -247,12 +247,6 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op } } - /* update brk/cont array */ - for (j = 0; j < op_array->last_live_range; j++) { - op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; - op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; - } - /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; @@ -852,9 +846,6 @@ optimize_jmpnz: take_successor_1(ssa, block_num, block); goto optimize_nop; } else { - if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); - } opline->opcode = ZEND_JMP; opline->result_type = IS_UNUSED; zend_ssa_remove_result_def(ssa, ssa_op); diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index cfd2ca90cf..1019af9099 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -81,12 +81,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx zend_optimizer_shift_jump(op_array, opline, shiftlist); } - /* update brk/cont array */ - for (j = 0; j < op_array->last_live_range; j++) { - op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start]; - op_array->live_range[j].end -= shiftlist[op_array->live_range[j].end]; - } - /* update try/catch array */ for (j = 0; j < op_array->last_try_catch; j++) { op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 794bb2d993..b414527a08 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -182,14 +182,6 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c opline--; } - if (op_array->live_range) { - for (i = 0; i < op_array->last_live_range; i++) { - op_array->live_range[i].var = - NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) | - (op_array->live_range[i].var & ZEND_LIVE_MASK); - } - } - zend_arena_release(&ctx->arena, checkpoint); op_array->T = max + 1; } diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 69a3e81c6b..34e9dcd051 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -340,7 +340,6 @@ static zend_bool try_replace_op2( ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use); if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) { zend_ssa_op *next_op = ssa_op + 1; - zend_optimizer_remove_live_range_ex(ctx->scdf.op_array, opline->result.var, ssa_op - ctx->scdf.ssa->ops); zend_ssa_unlink_use_chain(ctx->scdf.ssa, next_op - ctx->scdf.ssa->ops, next_op->op2_use); next_op->op2_use = -1; next_op->op2_use_chain = -1; @@ -2128,7 +2127,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, uint32_t old_var = opline->result.var; ssa_op->result_def = -1; - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); if (opline->opcode == ZEND_DO_ICALL) { removed_ops = remove_call(ctx, opline, ssa_op) - 1; } else { @@ -2143,9 +2141,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, } return 0; } else { - if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); - } zend_ssa_remove_result_def(ssa, ssa_op); if (opline->opcode == ZEND_DO_ICALL) { removed_ops = remove_call(ctx, opline, ssa_op); @@ -2199,9 +2194,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, if (ssa_op->result_def >= 0) { if (ssa->vars[ssa_op->result_def].use_chain < 0 && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) { - if (opline->result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition); - } zend_ssa_remove_result_def(ssa, ssa_op); opline->result_type = IS_UNUSED; } else if (opline->opcode != ZEND_PRE_INC && diff --git a/ext/opcache/Optimizer/scdf.c b/ext/opcache/Optimizer/scdf.c index e9becf2d3d..77cfc9715e 100644 --- a/ext/opcache/Optimizer/scdf.c +++ b/ext/opcache/Optimizer/scdf.c @@ -184,18 +184,22 @@ void scdf_solve(scdf_ctx *scdf, const char *name) { /* If a live range starts in a reachable block and ends in an unreachable block, we should * not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var * is necessary for the correctness of temporary compaction. */ -static zend_bool kept_alive_by_live_range(scdf_ctx *scdf, uint32_t block) { +static zend_bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) { uint32_t i; const zend_op_array *op_array = scdf->op_array; const zend_cfg *cfg = &scdf->ssa->cfg; - for (i = 0; i < op_array->last_live_range; i++) { - zend_live_range *live_range = &op_array->live_range[i]; - uint32_t start_block = cfg->map[live_range->start]; - uint32_t end_block = cfg->map[live_range->end]; - - if (end_block == block && start_block != block - && zend_bitset_in(scdf->executable_blocks, start_block)) { - return 1; + const zend_basic_block *block = &cfg->blocks[block_idx]; + for (i = block->start; i < block->start + block->len; i++) { + zend_op *opline = &op_array->opcodes[i]; + if (opline->opcode == ZEND_FE_FREE || + (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH)) { + zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline); + if (def_opline) { + uint32_t def_block = cfg->map[def_opline - op_array->opcodes]; + if (zend_bitset_in(scdf->executable_blocks, def_block)) { + return 1; + } + } } } return 0; @@ -212,7 +216,7 @@ int scdf_remove_unreachable_blocks(scdf_ctx *scdf) { for (i = 0; i < ssa->cfg.blocks_count; i++) { if (!zend_bitset_in(scdf->executable_blocks, i) && (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE) - && !kept_alive_by_live_range(scdf, i)) { + && !kept_alive_by_loop_var_free(scdf, i)) { removed_ops += ssa->cfg.blocks[i].len; zend_ssa_remove_block(scdf->op_array, ssa, i); } diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c index d762d35d52..267c39c8ef 100644 --- a/ext/opcache/Optimizer/zend_cfg.c +++ b/ext/opcache/Optimizer/zend_cfg.c @@ -118,49 +118,6 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg * do { changed = 0; - /* Add live range paths */ - for (j = 0; j < op_array->last_live_range; j++) { - zend_live_range *live_range = &op_array->live_range[j]; - if (live_range->var == (uint32_t)-1) { - /* this live range already removed */ - continue; - } - b = blocks + block_map[live_range->start]; - if (b->flags & ZEND_BB_REACHABLE) { - while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) { - /* check if NOP breaks incorrect smart branch */ - if (b->len == 2 - && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ - || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ) - && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST)) - && b->start > 0 - && zend_is_smart_branch(op_array->opcodes + b->start - 1)) { - break; - } - b->start++; - b->len--; - } - if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) { - /* mark as removed (empty live range) */ - live_range->var = (uint32_t)-1; - continue; - } - b->flags |= ZEND_BB_GEN_VAR; - b = blocks + block_map[live_range->end]; - b->flags |= ZEND_BB_KILL_VAR; - if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) { - if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) { - changed = 1; - zend_mark_reachable(op_array->opcodes, cfg, b); - } else { - b->flags |= ZEND_BB_UNREACHABLE_FREE; - } - } - } else { - ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE)); - } - } - /* Add exception paths */ for (j = 0; j < op_array->last_try_catch; j++) { @@ -236,6 +193,29 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg * } } } while (changed); + + /* Mark blocks that are unreachable, but free a loop var created in a reachable block. */ + for (b = blocks; b < blocks + cfg->blocks_count; b++) { + if (b->flags & ZEND_BB_REACHABLE) { + continue; + } + + for (j = b->start; j < b->start + b->len; j++) { + zend_op *opline = &op_array->opcodes[j]; + if (opline->opcode == ZEND_FE_FREE || + (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH) + ) { + zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline); + if (def_opline) { + uint32_t def_block = block_map[def_opline - op_array->opcodes]; + if (blocks[def_block].flags & ZEND_BB_REACHABLE) { + b->flags |= ZEND_BB_UNREACHABLE_FREE; + break; + } + } + } + } + } } } /* }}} */ @@ -293,7 +273,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b zval *zv; zend_bool extra_entry_block = 0; - cfg->flags = build_flags & (ZEND_CFG_SPLIT_AT_LIVE_RANGES|ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY); + cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY); cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t)); @@ -446,13 +426,6 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b extra_entry_block = 1; } - if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) { - for (j = 0; j < op_array->last_live_range; j++) { - BB_START(op_array->live_range[j].start); - BB_START(op_array->live_range[j].end); - } - } - if (op_array->last_try_catch) { for (j = 0; j < op_array->last_try_catch; j++) { BB_START(op_array->try_catch_array[j].try_op); diff --git a/ext/opcache/Optimizer/zend_cfg.h b/ext/opcache/Optimizer/zend_cfg.h index 2f144dbe5c..fcb7657842 100644 --- a/ext/opcache/Optimizer/zend_cfg.h +++ b/ext/opcache/Optimizer/zend_cfg.h @@ -29,8 +29,6 @@ #define ZEND_BB_CATCH (1<<6) /* start of catch block */ #define ZEND_BB_FINALLY (1<<7) /* start of finally block */ #define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */ -#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */ -#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */ #define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */ #define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */ @@ -39,7 +37,7 @@ #define ZEND_BB_REACHABLE (1<<31) -#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR) +#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE) typedef struct _zend_basic_block { int *successors; /* successor block indices */ @@ -99,7 +97,6 @@ typedef struct _zend_cfg { #define ZEND_SSA_DEBUG_LIVENESS (1<<29) #define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28) #define ZEND_SSA_RC_INFERENCE (1<<27) -#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26) #define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25) #define ZEND_CFG_RECV_ENTRY (1<<24) #define ZEND_CALL_TREE (1<<23) diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index d5f6c4564b..ad4d393bd6 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -759,15 +759,12 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags if (b->flags & ZEND_BB_FINALLY_END) { fprintf(stderr, " finally_end"); } - if (b->flags & ZEND_BB_GEN_VAR) { - fprintf(stderr, " gen_var"); - } - if (b->flags & ZEND_BB_KILL_VAR) { - fprintf(stderr, " kill_var"); - } if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) && !(b->flags & ZEND_BB_REACHABLE)) { fprintf(stderr, " unreachable"); } + if (b->flags & ZEND_BB_UNREACHABLE_FREE) { + fprintf(stderr, " unreachable_free"); + } if (b->flags & ZEND_BB_LOOP_HEADER) { fprintf(stderr, " loop_header"); } @@ -1007,20 +1004,13 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons } } } - if (op_array->last_live_range) { + if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { fprintf(stderr, "LIVE RANGES:\n"); for (i = 0; i < op_array->last_live_range; i++) { - if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) { - fprintf(stderr, " %u: BB%u - BB%u ", - EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), - cfg->map[op_array->live_range[i].start], - cfg->map[op_array->live_range[i].end]); - } else { - fprintf(stderr, " %u: L%u - L%u ", - EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), - op_array->live_range[i].start, - op_array->live_range[i].end); - } + fprintf(stderr, " %u: L%u - L%u ", + EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK), + op_array->live_range[i].start, + op_array->live_range[i].end); switch (op_array->live_range[i].var & ZEND_LIVE_MASK) { case ZEND_LIVE_TMPVAR: fprintf(stderr, "(tmp/var)\n"); @@ -1070,7 +1060,7 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons zend_dump_op(op_array, NULL, opline, dump_flags, data); opline++; } - if (op_array->last_live_range) { + if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) { fprintf(stderr, "LIVE RANGES:\n"); for (i = 0; i < op_array->last_live_range; i++) { fprintf(stderr, " %u: L%u - L%u ", diff --git a/ext/opcache/Optimizer/zend_dump.h b/ext/opcache/Optimizer/zend_dump.h index 035bc51731..8786113ae5 100644 --- a/ext/opcache/Optimizer/zend_dump.h +++ b/ext/opcache/Optimizer/zend_dump.h @@ -26,6 +26,7 @@ #define ZEND_DUMP_RC_INFERENCE (1<<1) #define ZEND_DUMP_CFG (1<<2) #define ZEND_DUMP_SSA (1<<3) +#define ZEND_DUMP_LIVE_RANGES (1<<4) #define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS BEGIN_EXTERN_C() diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 42cd5eef8f..00866026f0 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -611,104 +611,6 @@ handle_static_prop: return 1; } -void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var) -{ - if (op_array->last_live_range) { - int i = 0; - int j = 0; - - do { - if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) { - if (i != j) { - op_array->live_range[j] = op_array->live_range[i]; - } - j++; - } - i++; - } while (i < op_array->last_live_range); - if (i != j) { - op_array->last_live_range = j; - if (j == 0) { - efree(op_array->live_range); - op_array->live_range = NULL; - } - } - } -} - -static uint32_t zend_determine_constructor_call(zend_op_array *op_array, uint32_t start) { - int call = 0; - while (++start < op_array->last) { - switch (op_array->opcodes[start].opcode) { - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_FCALL: - case ZEND_NEW: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_USER_CALL: - call++; - break; - case ZEND_DO_FCALL: - if (call == 0) { - return start; - } - /* break missing intentionally */ - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - call--; - break; - default: - break; - } - } - - ZEND_ASSERT(0); - return -1; -} - -void zend_optimizer_remove_live_range_ex(zend_op_array *op_array, uint32_t var, uint32_t start) -{ - uint32_t i = 0; - - switch (op_array->opcodes[start].opcode) { - case ZEND_ROPE_ADD: - case ZEND_ADD_ARRAY_ELEMENT: - return; - case ZEND_ROPE_INIT: - var |= ZEND_LIVE_ROPE; - break; - case ZEND_BEGIN_SILENCE: - var |= ZEND_LIVE_SILENCE; - break; - case ZEND_FE_RESET_R: - case ZEND_FE_RESET_RW: - var |= ZEND_LIVE_LOOP; - start++; - break; - case ZEND_NEW: - start = zend_determine_constructor_call(op_array, start); - start++; - break; - default: - start++; - } - - while (i < op_array->last_live_range) { - if (op_array->live_range[i].var == var - && op_array->live_range[i].start == start) { - op_array->last_live_range--; - if (i < op_array->last_live_range) { - memmove(&op_array->live_range[i], &op_array->live_range[i+1], (op_array->last_live_range - i) * sizeof(zend_live_range)); - } - break; - } - i++; - } -} - int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_op *opline, zend_uchar type, @@ -774,67 +676,44 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var); MAKE_NOP(m); zval_ptr_dtor_nogc(val); - zend_optimizer_remove_live_range(op_array, var); return 1; } case ZEND_SWITCH_LONG: case ZEND_SWITCH_STRING: - case ZEND_CASE: - case ZEND_FREE: { - zend_op *m, *n; - int brk = op_array->last_live_range; - zend_bool in_switch = 0; - while (brk--) { - if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) && - op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) { - in_switch = 1; - break; - } - } - - if (!in_switch) { - ZEND_ASSERT(opline->opcode == ZEND_FREE); - MAKE_NOP(opline); - zval_ptr_dtor_nogc(val); - return 1; - } - - m = opline; - n = op_array->opcodes + op_array->live_range[brk].end; - if (n->opcode == ZEND_FREE && - !(n->extended_value & ZEND_FREE_ON_RETURN)) { - n++; - } else { - n = op_array->opcodes + op_array->last; - } - - while (m < n) { - if (m->op1_type == type && - m->op1.var == var) { - if (m->opcode == ZEND_CASE - || m->opcode == ZEND_SWITCH_LONG - || m->opcode == ZEND_SWITCH_STRING) { + case ZEND_CASE: { + zend_op *end = op_array->opcodes + op_array->last; + while (opline < end) { + if (opline->op1_type == type && opline->op1.var == var) { + if (opline->opcode == ZEND_CASE + || opline->opcode == ZEND_SWITCH_LONG + || opline->opcode == ZEND_SWITCH_STRING) { zval v; - if (m->opcode == ZEND_CASE) { - m->opcode = ZEND_IS_EQUAL; + if (opline->opcode == ZEND_CASE) { + opline->opcode = ZEND_IS_EQUAL; } ZVAL_COPY(&v, val); if (Z_TYPE(v) == IS_STRING) { zend_string_hash_val(Z_STR(v)); } - m->op1.constant = zend_optimizer_add_literal(op_array, &v); - m->op1_type = IS_CONST; - } else if (m->opcode == ZEND_FREE) { - MAKE_NOP(m); + opline->op1.constant = zend_optimizer_add_literal(op_array, &v); + opline->op1_type = IS_CONST; + } else if (opline->opcode == ZEND_FREE) { + if (opline->extended_value == ZEND_FREE_SWITCH) { + /* We found the end of the switch. */ + MAKE_NOP(opline); + break; + } + + ZEND_ASSERT(opline->extended_value == ZEND_FREE_ON_RETURN); + MAKE_NOP(opline); } else { ZEND_ASSERT(0); } } - m++; + opline++; } zval_ptr_dtor_nogc(val); - zend_optimizer_remove_live_range(op_array, var); return 1; } case ZEND_VERIFY_RETURN_TYPE: { @@ -858,20 +737,12 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, default: break; } - if (zend_optimizer_update_op1_const(op_array, opline, val)) { - zend_optimizer_remove_live_range(op_array, var); - return 1; - } - return 0; + return zend_optimizer_update_op1_const(op_array, opline, val); } if (opline->op2_type == type && opline->op2.var == var) { - if (zend_optimizer_update_op2_const(op_array, opline, val)) { - zend_optimizer_remove_live_range(op_array, var); - return 1; - } - return 0; + return zend_optimizer_update_op2_const(op_array, opline, val); } opline++; } @@ -1111,6 +982,19 @@ uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) } } +zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) { + uint32_t var = free_opline->op1.var; + ZEND_ASSERT(free_opline->opcode == ZEND_FE_FREE || + (free_opline->opcode == ZEND_FREE && free_opline->extended_value == ZEND_FREE_SWITCH)); + + while (--free_opline >= op_array->opcodes) { + if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) { + return free_opline; + } + } + return NULL; +} + static void zend_optimize(zend_op_array *op_array, zend_optimizer_ctx *ctx) { @@ -1119,7 +1003,7 @@ static void zend_optimize(zend_op_array *op_array, } if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) { - zend_dump_op_array(op_array, 0, "before optimizer", NULL); + zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL); } /* pass 1 @@ -1442,6 +1326,8 @@ static void zend_optimize_op_array(zend_op_array *op_array, /* Redo pass_two() */ zend_redo_pass_two(op_array); + + zend_recalc_live_ranges(op_array, NULL); } static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx) @@ -1482,6 +1368,16 @@ static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array) } } } + +static zend_bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) { + zend_func_info *func_info = ZEND_FUNC_INFO(op_array); + zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes]; + if (ssa_op->result_def >= 0) { + uint32_t type = func_info->ssa.var_info[ssa_op->result_def].type; + return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; + } + return 1; +} #endif int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level) @@ -1594,8 +1490,10 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); if (func_info && func_info->ssa.var_info) { zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa); + zend_recalc_live_ranges(call_graph.op_arrays[i], needs_live_range); } else { zend_redo_pass_two(call_graph.op_arrays[i]); + zend_recalc_live_ranges(call_graph.op_arrays[i], NULL); } } @@ -1652,16 +1550,19 @@ int zend_optimize_script(zend_script *script, zend_long optimization_level, zend if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) && (ZEND_OPTIMIZER_PASS_7 & optimization_level)) { - zend_dump_op_array(&script->main_op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); + zend_dump_op_array(&script->main_op_array, + ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { - zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); + zend_dump_op_array(op_array, + ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_PTR(&script->class_table, ce) { ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) { if (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { - zend_dump_op_array(op_array, ZEND_DUMP_RT_CONSTANTS, "after optimizer", NULL); + zend_dump_op_array(op_array, + ZEND_DUMP_RT_CONSTANTS | ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL); } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index 6c12a0d828..8afba7c8e5 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -90,9 +90,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_uchar type, uint32_t var, zval *val); +zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline); -void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var); -void zend_optimizer_remove_live_range_ex(zend_op_array *op_array, uint32_t var, uint32_t start); void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx); void zend_optimizer_pass2(zend_op_array *op_array); void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx); diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index ad34e7f643..a3a5130385 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -1436,9 +1436,6 @@ void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ continue; } - if (op_array->opcodes[j].result_type & (IS_TMP_VAR|IS_VAR)) { - zend_optimizer_remove_live_range_ex(op_array, op_array->opcodes[j].result.var, j); - } zend_ssa_remove_defs_of_instr(ssa, &ssa->ops[j]); zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]); } -- cgit v1.2.1