summaryrefslogtreecommitdiff
path: root/ext/opcache/Optimizer
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-01-17 16:07:17 +0100
committerNikita Popov <nikita.ppv@gmail.com>2019-01-21 11:47:27 +0100
commit3269e884686ada59407e14db812bfb42d59d2f1c (patch)
tree340b3f09b122a3e1c676f02f0f729bae40c450fd /ext/opcache/Optimizer
parent276d3a7d159964d225c8e7ff75debdd9c359eee2 (diff)
downloadphp-git-3269e884686ada59407e14db812bfb42d59d2f1c.tar.gz
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.
Diffstat (limited to 'ext/opcache/Optimizer')
-rw-r--r--ext/opcache/Optimizer/block_pass.c45
-rw-r--r--ext/opcache/Optimizer/compact_vars.c9
-rw-r--r--ext/opcache/Optimizer/dce.c77
-rw-r--r--ext/opcache/Optimizer/dfa_pass.c9
-rw-r--r--ext/opcache/Optimizer/nop_removal.c6
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c8
-rw-r--r--ext/opcache/Optimizer/sccp.c8
-rw-r--r--ext/opcache/Optimizer/scdf.c24
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c75
-rw-r--r--ext/opcache/Optimizer/zend_cfg.h5
-rw-r--r--ext/opcache/Optimizer/zend_dump.c28
-rw-r--r--ext/opcache/Optimizer/zend_dump.h1
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c213
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h3
-rw-r--r--ext/opcache/Optimizer/zend_ssa.c3
15 files changed, 108 insertions, 406 deletions
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]);
}