summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/zend_compile.c241
-rw-r--r--Zend/zend_compile.h5
-rw-r--r--Zend/zend_opcode.c196
-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
18 files changed, 301 insertions, 655 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 7210cb4bab..99349e13d3 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -56,10 +56,7 @@ typedef struct _zend_loop_var {
zend_uchar opcode;
zend_uchar var_type;
uint32_t var_num;
- union {
- uint32_t try_catch_offset;
- uint32_t live_range_offset;
- } u;
+ uint32_t try_catch_offset;
} zend_loop_var;
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
@@ -625,33 +622,6 @@ void zend_stop_lexing(void)
LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit);
}
-static uint32_t zend_start_live_range(uint32_t start) /* {{{ */
-{
- zend_op_array *op_array = CG(active_op_array);
- zend_live_range *range;
-
- op_array->last_live_range++;
- op_array->live_range = erealloc(op_array->live_range, sizeof(zend_live_range) * op_array->last_live_range);
- range = op_array->live_range + op_array->last_live_range - 1;
- range->start = start;
- return op_array->last_live_range - 1;
-}
-/* }}} */
-
-static void zend_end_live_range(uint32_t offset, uint32_t end, uint32_t kind, uint32_t var) /* {{{ */
-{
- zend_op_array *op_array = CG(active_op_array);
- zend_live_range *range = op_array->live_range + offset;
-
- if (range->start == end && offset == (uint32_t)op_array->last_live_range - 1) {
- op_array->last_live_range--;
- } else {
- range->end = end;
- range->var = (var * sizeof(zval)) | kind;
- }
-}
-/* }}} */
-
static inline void zend_begin_loop(
zend_uchar free_opcode, const znode *loop_var, zend_bool is_switch) /* {{{ */
{
@@ -670,7 +640,6 @@ static inline void zend_begin_loop(
info.opcode = free_opcode;
info.var_type = loop_var->op_type;
info.var_num = loop_var->u.op.var;
- info.u.live_range_offset = zend_start_live_range(start);
brk_cont_element->start = start;
} else {
info.opcode = ZEND_NOP;
@@ -692,13 +661,6 @@ static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */
brk_cont_element->brk = end;
CG(context).current_brk_cont = brk_cont_element->parent;
- if (brk_cont_element->start != -1) {
- zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
- zend_end_live_range(loop_var->u.live_range_offset, end,
- loop_var->opcode == ZEND_FE_FREE ? ZEND_LIVE_LOOP : ZEND_LIVE_TMPVAR,
- var_node->u.op.var);
- }
-
zend_stack_del_top(&CG(loop_var_stack));
}
/* }}} */
@@ -1809,172 +1771,6 @@ static inline void zend_make_tmp_result(znode *result, zend_op *opline) /* {{{ *
}
/* }}} */
-static void zend_find_live_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
-{
- zend_op *def = opline;
-
- while (def != CG(active_op_array)->opcodes) {
- def--;
- if (def->result_type == type && def->result.var == var) {
- if (def->opcode == ZEND_ADD_ARRAY_ELEMENT ||
- def->opcode == ZEND_ROPE_ADD) {
- /* not a real definition */
- continue;
- } else if (def->opcode == ZEND_JMPZ_EX ||
- def->opcode == ZEND_JMPNZ_EX ||
- def->opcode == ZEND_BOOL ||
- def->opcode == ZEND_BOOL_NOT) {
- /* result IS_BOOL, it doesn't have to be destroyed */
- break;
- } else if (def->opcode == ZEND_DECLARE_ANON_CLASS ||
- def->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS) {
- /* classes don't have to be destroyed */
- break;
- } else if (def->opcode == ZEND_FAST_CALL) {
- /* fast_calls don't have to be destroyed */
- break;
- } else if (def->opcode == ZEND_NEW) {
- /* Objects created via ZEND_NEW are only fully initialized
- * after the DO_FCALL (constructor call) */
- int level = 0;
- while (def + 1 != opline) {
- def++;
- if (def->opcode == ZEND_DO_FCALL) {
- if (level == 0) {
- break;
- }
- level--;
- } else {
- switch(def->opcode) {
- case ZEND_INIT_FCALL:
- case ZEND_INIT_FCALL_BY_NAME:
- case ZEND_INIT_NS_FCALL_BY_NAME:
- case ZEND_INIT_DYNAMIC_CALL:
- case ZEND_INIT_USER_CALL:
- case ZEND_INIT_METHOD_CALL:
- case ZEND_INIT_STATIC_METHOD_CALL:
- case ZEND_NEW:
- level++;
- break;
- case ZEND_DO_ICALL:
- case ZEND_DO_UCALL:
- case ZEND_DO_FCALL_BY_NAME:
- level--;
- break;
- }
- }
- }
- if (def + 1 == opline) {
- break;
- }
- }
-
- zend_end_live_range(
- zend_start_live_range(
- def + 1 - CG(active_op_array)->opcodes),
- opline - CG(active_op_array)->opcodes,
- ZEND_LIVE_TMPVAR, var);
- break;
- }
- }
-}
-/* }}} */
-
-static zend_always_inline int zend_is_def_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
-{
- while (1) {
- if (opline->result_type == type && opline->result.var == var) {
- return opline->opcode != ZEND_ADD_ARRAY_ELEMENT &&
- opline->opcode != ZEND_ROPE_ADD;
- } else if (opline->opcode == ZEND_OP_DATA) {
- return (opline-1)->result_type == type &&
- (opline-1)->result.var == var;
- } else if (opline->opcode == ZEND_END_SILENCE ||
- opline->opcode == ZEND_NOP ||
- opline->opcode == ZEND_EXT_NOP ||
- opline->opcode == ZEND_EXT_STMT ||
- opline->opcode == ZEND_EXT_FCALL_BEGIN ||
- opline->opcode == ZEND_EXT_FCALL_END ||
- opline->opcode == ZEND_TICKS) {
- opline--;
- } else {
- return 0;
- }
- }
-}
-/* }}} */
-
-static void zend_check_live_ranges_op1(zend_op *opline) /* {{{ */
-{
- if (!zend_is_def_range(opline - 1, opline->op1_type, opline->op1.var)) {
-
- if (opline->opcode == ZEND_OP_DATA) {
- if (!zend_is_def_range(opline - 2, opline->op1_type, opline->op1.var)) {
- zend_find_live_range(opline - 1, opline->op1_type, opline->op1.var);
- }
- } else if (opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
- opline->opcode == ZEND_NEW ||
- opline->opcode == ZEND_FETCH_CLASS_CONSTANT) {
- /* classes don't have to be destroyed */
- } else if (opline->opcode == ZEND_FAST_RET) {
- /* fast_calls don't have to be destroyed */
- } else if (opline->opcode == ZEND_CASE ||
- opline->opcode == ZEND_SWITCH_LONG ||
- opline->opcode == ZEND_SWITCH_STRING ||
- opline->opcode == ZEND_FE_FETCH_R ||
- opline->opcode == ZEND_FE_FETCH_RW ||
- opline->opcode == ZEND_FE_FREE ||
- opline->opcode == ZEND_ROPE_ADD ||
- opline->opcode == ZEND_ROPE_END ||
- opline->opcode == ZEND_END_SILENCE ||
- opline->opcode == ZEND_FETCH_LIST_R ||
- opline->opcode == ZEND_FETCH_LIST_W ||
- opline->opcode == ZEND_VERIFY_RETURN_TYPE ||
- opline->opcode == ZEND_BIND_LEXICAL) {
- /* these opcodes are handled separately */
- } else {
- zend_find_live_range(opline, opline->op1_type, opline->op1.var);
- }
- }
-}
-/* }}} */
-
-static void zend_check_live_ranges_op2(zend_op *opline) /* {{{ */
-{
- if (!zend_is_def_range(opline - 1, opline->op2_type, opline->op2.var)) {
-
- if (opline->opcode == ZEND_OP_DATA) {
- if (!zend_is_def_range(opline - 2, opline->op2_type, opline->op2.var)) {
- zend_find_live_range(opline-1, opline->op2_type, opline->op2.var);
- }
- } else if (opline->opcode == ZEND_FETCH_STATIC_PROP_R ||
- opline->opcode == ZEND_FETCH_STATIC_PROP_W ||
- opline->opcode == ZEND_FETCH_STATIC_PROP_RW ||
- opline->opcode == ZEND_FETCH_STATIC_PROP_IS ||
- opline->opcode == ZEND_FETCH_STATIC_PROP_FUNC_ARG ||
- opline->opcode == ZEND_FETCH_STATIC_PROP_UNSET ||
- opline->opcode == ZEND_UNSET_STATIC_PROP ||
- opline->opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP ||
- opline->opcode == ZEND_INSTANCEOF) {
- /* classes don't have to be destroyed */
- } else {
- zend_find_live_range(opline, opline->op2_type, opline->op2.var);
- }
- }
-}
-/* }}} */
-
-static void zend_check_live_ranges(zend_op *opline) /* {{{ */
-{
- if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
- zend_check_live_ranges_op1(opline);
- }
- if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
- zend_check_live_ranges_op2(opline);
- }
-}
-/* }}} */
-
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */
{
zend_op *opline = get_next_op();
@@ -1982,16 +1778,10 @@ static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode
if (op1 != NULL) {
SET_NODE(opline->op1, op1);
- if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
- zend_check_live_ranges_op1(opline);
- }
}
if (op2 != NULL) {
SET_NODE(opline->op2, op2);
- if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
- zend_check_live_ranges_op2(opline);
- }
}
if (result) {
@@ -2008,16 +1798,10 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z
if (op1 != NULL) {
SET_NODE(opline->op1, op1);
- if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
- zend_check_live_ranges_op1(opline);
- }
}
if (op2 != NULL) {
SET_NODE(opline->op2, op2);
- if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
- zend_check_live_ranges_op2(opline);
- }
}
if (result) {
@@ -2166,7 +1950,6 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
for (i = offset; i < count; ++i) {
opline = get_next_op();
memcpy(opline, &oplines[i], sizeof(zend_op));
- zend_check_live_ranges(opline);
}
CG(delayed_oplines_stack).top = offset;
return opline;
@@ -4099,7 +3882,6 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}
SET_NODE(opline->op2, &method_node);
}
- zend_check_live_ranges(opline);
/* Check if we already know which method we're calling */
if (opline->op2_type == IS_CONST) {
@@ -4321,7 +4103,7 @@ static int zend_handle_loops_and_finally_ex(zend_long depth, znode *return_value
if (return_value) {
SET_NODE(opline->op2, return_value);
}
- opline->op1.num = loop_var->u.try_catch_offset;
+ opline->op1.num = loop_var->try_catch_offset;
} else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
zend_op *opline = get_next_op();
opline->opcode = ZEND_DISCARD_EXCEPTION;
@@ -5043,10 +4825,8 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_end_loop(get_next_op_number(), &expr_node);
if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
- /* don't use emit_op() to prevent automatic live-range construction */
- opline = get_next_op();
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, &expr_node);
+ opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
+ opline->extended_value = ZEND_FREE_SWITCH;
} else if (expr_node.op_type == IS_CONST) {
zval_ptr_dtor_nogc(&expr_node.u.constant);
}
@@ -5096,7 +4876,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
fast_call.opcode = ZEND_FAST_CALL;
fast_call.var_type = IS_TMP_VAR;
fast_call.var_num = CG(context).fast_call_var;
- fast_call.u.try_catch_offset = try_catch_offset;
+ fast_call.try_catch_offset = try_catch_offset;
zend_stack_push(&CG(loop_var_stack), &fast_call);
}
@@ -7631,9 +7411,7 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode silence_node;
- uint32_t range;
- range = zend_start_live_range(get_next_op_number());
zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL);
if (expr_ast->kind == ZEND_AST_VAR) {
@@ -7644,11 +7422,6 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(result, expr_ast);
}
- /* Store BEGIN_SILENCE/END_SILENCE pair to restore previous
- * EG(error_reporting) value on exception */
- zend_end_live_range(range, get_next_op_number(),
- ZEND_LIVE_SILENCE, silence_node.u.op.var);
-
zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
}
/* }}} */
@@ -7940,7 +7713,6 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
GET_NODE(result, opline->result);
} else {
uint32_t var;
- uint32_t range = zend_start_live_range(rope_init_lineno);
init_opline->extended_value = j;
opline->opcode = ZEND_ROPE_END;
@@ -7955,9 +7727,6 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
i--;
}
- zend_end_live_range(range, opline - CG(active_op_array)->opcodes,
- ZEND_LIVE_ROPE, var);
-
/* Update all the previous opcodes to use the same variable */
while (opline != init_opline) {
opline--;
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 07c8a1768f..0d7019c988 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -806,6 +806,10 @@ static zend_always_inline const char *zend_get_unmangled_property_name(const zen
#define ZEND_FUNCTION_DTOR zend_function_dtor
#define ZEND_CLASS_DTOR destroy_zend_class
+typedef zend_bool (*zend_needs_live_range_cb)(zend_op_array *op_array, zend_op *opline);
+ZEND_API void zend_recalc_live_ranges(
+ zend_op_array *op_array, zend_needs_live_range_cb needs_live_range);
+
ZEND_API int pass_two(zend_op_array *op_array);
ZEND_API zend_bool zend_is_compiling(void);
ZEND_API char *zend_make_compiled_string_description(const char *name);
@@ -909,6 +913,7 @@ void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_LAST_CATCH (1<<0)
#define ZEND_FREE_ON_RETURN (1<<0)
+#define ZEND_FREE_SWITCH (1<<1)
#define ZEND_SEND_BY_VAL 0
#define ZEND_SEND_BY_REF 1
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index ec624a5adb..dac934115e 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -547,6 +547,123 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze
return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont;
}
+static void emit_live_range(
+ zend_op_array *op_array, uint32_t var_num, uint32_t start, uint32_t end,
+ zend_needs_live_range_cb needs_live_range) {
+ zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline;
+ zend_op *use_opline = &op_array->opcodes[end];
+ zend_live_range *range;
+ uint32_t kind = ZEND_LIVE_TMPVAR;
+
+ switch (def_opline->opcode) {
+ /* These should never be the first def. */
+ case ZEND_ADD_ARRAY_ELEMENT:
+ case ZEND_ROPE_ADD:
+ ZEND_ASSERT(0);
+ return;
+ /* Result is boolean, it doesn't have to be destroyed. */
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_BOOL:
+ case ZEND_BOOL_NOT:
+ /* Classes don't have to be destroyed. */
+ case ZEND_FETCH_CLASS:
+ case ZEND_DECLARE_ANON_CLASS:
+ case ZEND_DECLARE_ANON_INHERITED_CLASS:
+ /* FAST_CALLs don't have to be destroyed. */
+ case ZEND_FAST_CALL:
+ return;
+ case ZEND_BEGIN_SILENCE:
+ kind = ZEND_LIVE_SILENCE;
+ break;
+ case ZEND_ROPE_INIT:
+ kind = ZEND_LIVE_ROPE;
+ /* ROPE live ranges include the generating opcode. */
+ def_opline--;
+ break;
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ kind = ZEND_LIVE_LOOP;
+ break;
+ /* Objects created via ZEND_NEW are only fully initialized
+ * after the DO_FCALL (constructor call). */
+ case ZEND_NEW: {
+ int level = 0;
+ while (def_opline + 1 < use_opline) {
+ def_opline++;
+ if (def_opline->opcode == ZEND_DO_FCALL) {
+ if (level == 0) {
+ break;
+ }
+ level--;
+ } else {
+ switch (def_opline->opcode) {
+ case ZEND_INIT_FCALL:
+ case ZEND_INIT_FCALL_BY_NAME:
+ case ZEND_INIT_NS_FCALL_BY_NAME:
+ case ZEND_INIT_DYNAMIC_CALL:
+ case ZEND_INIT_USER_CALL:
+ case ZEND_INIT_METHOD_CALL:
+ case ZEND_INIT_STATIC_METHOD_CALL:
+ case ZEND_NEW:
+ level++;
+ break;
+ case ZEND_DO_ICALL:
+ case ZEND_DO_UCALL:
+ case ZEND_DO_FCALL_BY_NAME:
+ level--;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ start = def_opline + 1 - op_array->opcodes;
+ if (start == end) {
+ /* Trivial live-range, no need to store it. */
+ return;
+ }
+
+ /* Check hook to determine whether a live range is necessary, e.g. based on type info. */
+ if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
+ return;
+ }
+
+ op_array->last_live_range++;
+ op_array->live_range = erealloc(op_array->live_range,
+ sizeof(zend_live_range) * op_array->last_live_range);
+
+ ZEND_ASSERT(start < end);
+ range = &op_array->live_range[op_array->last_live_range - 1];
+ range->var = (uint32_t) (intptr_t) ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var_num);
+ range->var |= kind;
+ range->start = start;
+ range->end = end;
+}
+
+static zend_bool is_fake_def(zend_op *opline) {
+ /* These opcodes only modify the result, not create it. */
+ return opline->opcode == ZEND_ROPE_ADD
+ || opline->opcode == ZEND_ADD_ARRAY_ELEMENT;
+}
+
+static zend_bool keeps_op1_alive(zend_op *opline) {
+ /* These opcodes don't consume their OP1 operand,
+ * it is later freed by something else. */
+ return opline->opcode == ZEND_CASE
+ || opline->opcode == ZEND_SWITCH_LONG
+ || opline->opcode == ZEND_SWITCH_STRING
+ || opline->opcode == ZEND_FE_FETCH_R
+ || opline->opcode == ZEND_FE_FETCH_RW
+ || opline->opcode == ZEND_FETCH_LIST_R
+ || opline->opcode == ZEND_FETCH_LIST_W
+ || opline->opcode == ZEND_VERIFY_RETURN_TYPE
+ || opline->opcode == ZEND_BIND_LEXICAL
+ || opline->opcode == ZEND_ROPE_ADD;
+}
+
/* Live ranges must be sorted by increasing start opline */
static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) {
return a->start - b->start;
@@ -556,9 +673,71 @@ static void swap_live_range(zend_live_range *a, zend_live_range *b) {
*a = *b;
*b = tmp;
}
-static void zend_sort_live_ranges(zend_op_array *op_array) {
- zend_sort(op_array->live_range, op_array->last_live_range, sizeof(zend_live_range),
- (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
+
+static void zend_calc_live_ranges(
+ zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
+ zend_op *opline = &op_array->opcodes[op_array->last - 1];
+ zend_op *begin = op_array->opcodes;
+ ALLOCA_FLAG(use_heap)
+ uint32_t var_offset = op_array->last_var;
+ uint32_t *last_use = do_alloca(sizeof(uint32_t) * op_array->T, use_heap);
+ memset(last_use, -1, sizeof(uint32_t) * op_array->T);
+
+ ZEND_ASSERT(!op_array->live_range);
+ for (; opline >= begin; opline--) {
+ uint32_t opnum = opline - begin;
+
+ if (opline->opcode == ZEND_OP_DATA) {
+ /* OP_DATA is really part of the previous opcode. */
+ opnum--;
+ }
+
+ if ((opline->result_type & (IS_TMP_VAR|IS_VAR)) && !is_fake_def(opline)) {
+ uint32_t var_num = EX_VAR_TO_NUM(opline->result.var) - var_offset;
+ /* Defs without uses can occur for two reasons: Either because the result is
+ * genuinely unused (e.g. omitted FREE opcode for an unused boolean result), or
+ * because there are multiple defining opcodes (e.g. JMPZ_EX and QM_ASSIGN), in
+ * which case the last one starts the live range. As such, we can simply ignore
+ * missing uses here. */
+ if (last_use[var_num] != (uint32_t) -1) {
+ emit_live_range(op_array, var_num, opnum, last_use[var_num], needs_live_range);
+ last_use[var_num] = (uint32_t) -1;
+ }
+ }
+
+ if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) && !keeps_op1_alive(opline)) {
+ uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var) - var_offset;
+ if (last_use[var_num] == (uint32_t) -1) {
+ last_use[var_num] = opnum;
+ }
+ }
+ if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
+ uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var) - var_offset;
+ if (last_use[var_num] == (uint32_t) -1) {
+ last_use[var_num] = opnum;
+ }
+ }
+ }
+
+ if (op_array->live_range) {
+ zend_sort(op_array->live_range, op_array->last_live_range, sizeof(zend_live_range),
+ (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
+ }
+
+ free_alloca(last_use, use_heap);
+}
+
+ZEND_API void zend_recalc_live_ranges(
+ zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
+ /* We assume that we never create live-ranges where there were none before. */
+ if (!op_array->live_range) {
+ return;
+ }
+
+ efree(op_array->live_range);
+ op_array->live_range = NULL;
+ op_array->last_live_range = 0;
+ zend_calc_live_ranges(op_array, needs_live_range);
}
ZEND_API int pass_two(zend_op_array *op_array)
@@ -726,16 +905,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
opline++;
}
- if (op_array->live_range) {
- int i;
-
- zend_sort_live_ranges(op_array);
- for (i = 0; i < op_array->last_live_range; i++) {
- op_array->live_range[i].var =
- (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + (op_array->live_range[i].var / sizeof(zval))) |
- (op_array->live_range[i].var & ZEND_LIVE_MASK);
- }
- }
+ zend_calc_live_ranges(op_array, NULL);
return 0;
}
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]);
}