diff options
-rw-r--r-- | Zend/zend_compile.c | 241 | ||||
-rw-r--r-- | Zend/zend_compile.h | 5 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 196 | ||||
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 45 | ||||
-rw-r--r-- | ext/opcache/Optimizer/compact_vars.c | 9 | ||||
-rw-r--r-- | ext/opcache/Optimizer/dce.c | 77 | ||||
-rw-r--r-- | ext/opcache/Optimizer/dfa_pass.c | 9 | ||||
-rw-r--r-- | ext/opcache/Optimizer/nop_removal.c | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/optimize_temp_vars_5.c | 8 | ||||
-rw-r--r-- | ext/opcache/Optimizer/sccp.c | 8 | ||||
-rw-r--r-- | ext/opcache/Optimizer/scdf.c | 24 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_cfg.c | 75 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_cfg.h | 5 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_dump.c | 28 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_dump.h | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 213 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer_internal.h | 3 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_ssa.c | 3 |
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]); } |