mirror of
https://github.com/php/php-src.git
synced 2025-08-17 22:48:57 +02:00
Implement single-pass live range calculation
Instead of interleaving creation of live-ranges with the main compiler code, compute them in a separate pass over the opcodes as part of pass_two. Additionally, do not keep live ranges synchronized during optimization in opcache and instead use the same mechanism to recompute them after optimization.
This commit is contained in:
parent
276d3a7d15
commit
3269e88468
18 changed files with 301 additions and 655 deletions
|
@ -56,10 +56,7 @@ typedef struct _zend_loop_var {
|
||||||
zend_uchar opcode;
|
zend_uchar opcode;
|
||||||
zend_uchar var_type;
|
zend_uchar var_type;
|
||||||
uint32_t var_num;
|
uint32_t var_num;
|
||||||
union {
|
|
||||||
uint32_t try_catch_offset;
|
uint32_t try_catch_offset;
|
||||||
uint32_t live_range_offset;
|
|
||||||
} u;
|
|
||||||
} zend_loop_var;
|
} zend_loop_var;
|
||||||
|
|
||||||
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
|
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);
|
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(
|
static inline void zend_begin_loop(
|
||||||
zend_uchar free_opcode, const znode *loop_var, zend_bool is_switch) /* {{{ */
|
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.opcode = free_opcode;
|
||||||
info.var_type = loop_var->op_type;
|
info.var_type = loop_var->op_type;
|
||||||
info.var_num = loop_var->u.op.var;
|
info.var_num = loop_var->u.op.var;
|
||||||
info.u.live_range_offset = zend_start_live_range(start);
|
|
||||||
brk_cont_element->start = start;
|
brk_cont_element->start = start;
|
||||||
} else {
|
} else {
|
||||||
info.opcode = ZEND_NOP;
|
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;
|
brk_cont_element->brk = end;
|
||||||
CG(context).current_brk_cont = brk_cont_element->parent;
|
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));
|
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) /* {{{ */
|
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */
|
||||||
{
|
{
|
||||||
zend_op *opline = get_next_op();
|
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) {
|
if (op1 != NULL) {
|
||||||
SET_NODE(opline->op1, op1);
|
SET_NODE(opline->op1, op1);
|
||||||
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
|
|
||||||
zend_check_live_ranges_op1(opline);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op2 != NULL) {
|
if (op2 != NULL) {
|
||||||
SET_NODE(opline->op2, op2);
|
SET_NODE(opline->op2, op2);
|
||||||
if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
|
|
||||||
zend_check_live_ranges_op2(opline);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -2008,16 +1798,10 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z
|
||||||
|
|
||||||
if (op1 != NULL) {
|
if (op1 != NULL) {
|
||||||
SET_NODE(opline->op1, op1);
|
SET_NODE(opline->op1, op1);
|
||||||
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
|
|
||||||
zend_check_live_ranges_op1(opline);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op2 != NULL) {
|
if (op2 != NULL) {
|
||||||
SET_NODE(opline->op2, op2);
|
SET_NODE(opline->op2, op2);
|
||||||
if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
|
|
||||||
zend_check_live_ranges_op2(opline);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -2166,7 +1950,6 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
|
||||||
for (i = offset; i < count; ++i) {
|
for (i = offset; i < count; ++i) {
|
||||||
opline = get_next_op();
|
opline = get_next_op();
|
||||||
memcpy(opline, &oplines[i], sizeof(zend_op));
|
memcpy(opline, &oplines[i], sizeof(zend_op));
|
||||||
zend_check_live_ranges(opline);
|
|
||||||
}
|
}
|
||||||
CG(delayed_oplines_stack).top = offset;
|
CG(delayed_oplines_stack).top = offset;
|
||||||
return opline;
|
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);
|
SET_NODE(opline->op2, &method_node);
|
||||||
}
|
}
|
||||||
zend_check_live_ranges(opline);
|
|
||||||
|
|
||||||
/* Check if we already know which method we're calling */
|
/* Check if we already know which method we're calling */
|
||||||
if (opline->op2_type == IS_CONST) {
|
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) {
|
if (return_value) {
|
||||||
SET_NODE(opline->op2, 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) {
|
} else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
|
||||||
zend_op *opline = get_next_op();
|
zend_op *opline = get_next_op();
|
||||||
opline->opcode = ZEND_DISCARD_EXCEPTION;
|
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);
|
zend_end_loop(get_next_op_number(), &expr_node);
|
||||||
|
|
||||||
if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
|
if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
|
||||||
/* don't use emit_op() to prevent automatic live-range construction */
|
opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
|
||||||
opline = get_next_op();
|
opline->extended_value = ZEND_FREE_SWITCH;
|
||||||
opline->opcode = ZEND_FREE;
|
|
||||||
SET_NODE(opline->op1, &expr_node);
|
|
||||||
} else if (expr_node.op_type == IS_CONST) {
|
} else if (expr_node.op_type == IS_CONST) {
|
||||||
zval_ptr_dtor_nogc(&expr_node.u.constant);
|
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.opcode = ZEND_FAST_CALL;
|
||||||
fast_call.var_type = IS_TMP_VAR;
|
fast_call.var_type = IS_TMP_VAR;
|
||||||
fast_call.var_num = CG(context).fast_call_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);
|
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];
|
zend_ast *expr_ast = ast->child[0];
|
||||||
znode silence_node;
|
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);
|
zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL);
|
||||||
|
|
||||||
if (expr_ast->kind == ZEND_AST_VAR) {
|
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);
|
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);
|
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);
|
GET_NODE(result, opline->result);
|
||||||
} else {
|
} else {
|
||||||
uint32_t var;
|
uint32_t var;
|
||||||
uint32_t range = zend_start_live_range(rope_init_lineno);
|
|
||||||
|
|
||||||
init_opline->extended_value = j;
|
init_opline->extended_value = j;
|
||||||
opline->opcode = ZEND_ROPE_END;
|
opline->opcode = ZEND_ROPE_END;
|
||||||
|
@ -7955,9 +7727,6 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
|
||||||
i--;
|
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 */
|
/* Update all the previous opcodes to use the same variable */
|
||||||
while (opline != init_opline) {
|
while (opline != init_opline) {
|
||||||
opline--;
|
opline--;
|
||||||
|
|
|
@ -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_FUNCTION_DTOR zend_function_dtor
|
||||||
#define ZEND_CLASS_DTOR destroy_zend_class
|
#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 int pass_two(zend_op_array *op_array);
|
||||||
ZEND_API zend_bool zend_is_compiling(void);
|
ZEND_API zend_bool zend_is_compiling(void);
|
||||||
ZEND_API char *zend_make_compiled_string_description(const char *name);
|
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_LAST_CATCH (1<<0)
|
||||||
|
|
||||||
#define ZEND_FREE_ON_RETURN (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_VAL 0
|
||||||
#define ZEND_SEND_BY_REF 1
|
#define ZEND_SEND_BY_REF 1
|
||||||
|
|
|
@ -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;
|
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 */
|
/* Live ranges must be sorted by increasing start opline */
|
||||||
static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) {
|
static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) {
|
||||||
return a->start - b->start;
|
return a->start - b->start;
|
||||||
|
@ -556,9 +673,71 @@ static void swap_live_range(zend_live_range *a, zend_live_range *b) {
|
||||||
*a = *b;
|
*a = *b;
|
||||||
*b = tmp;
|
*b = tmp;
|
||||||
}
|
}
|
||||||
static void zend_sort_live_ranges(zend_op_array *op_array) {
|
|
||||||
|
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),
|
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);
|
(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)
|
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++;
|
opline++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op_array->live_range) {
|
zend_calc_live_ranges(op_array, NULL);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,7 +197,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
|
||||||
) {
|
) {
|
||||||
znode_op op1 = opline->op1;
|
znode_op op1 = opline->op1;
|
||||||
if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
|
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->result, opline->op1);
|
||||||
COPY_NODE(opline->op1, src->op1);
|
COPY_NODE(opline->op1, src->op1);
|
||||||
VAR_SOURCE(op1) = NULL;
|
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 c;
|
||||||
ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
|
ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
|
||||||
if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
|
if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
|
||||||
zend_optimizer_remove_live_range(op_array, op1.var);
|
|
||||||
VAR_SOURCE(op1) = NULL;
|
VAR_SOURCE(op1) = NULL;
|
||||||
literal_dtor(&ZEND_OP1_LITERAL(src));
|
literal_dtor(&ZEND_OP1_LITERAL(src));
|
||||||
MAKE_NOP(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));
|
ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
|
||||||
if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
|
if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
|
||||||
zend_optimizer_remove_live_range(op_array, op2.var);
|
|
||||||
VAR_SOURCE(op2) = NULL;
|
VAR_SOURCE(op2) = NULL;
|
||||||
literal_dtor(&ZEND_OP1_LITERAL(src));
|
literal_dtor(&ZEND_OP1_LITERAL(src));
|
||||||
MAKE_NOP(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->opcode == ZEND_CAST &&
|
||||||
src->extended_value == IS_STRING) {
|
src->extended_value == IS_STRING) {
|
||||||
/* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
|
/* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
|
||||||
zend_optimizer_remove_live_range(op_array, opline->op1.var);
|
|
||||||
VAR_SOURCE(opline->op1) = NULL;
|
VAR_SOURCE(opline->op1) = NULL;
|
||||||
COPY_NODE(opline->op1, src->op1);
|
COPY_NODE(opline->op1, src->op1);
|
||||||
MAKE_NOP(src);
|
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->opcode == ZEND_CAST &&
|
||||||
src->extended_value == IS_STRING) {
|
src->extended_value == IS_STRING) {
|
||||||
/* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
|
/* 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;
|
VAR_SOURCE(opline->op1) = NULL;
|
||||||
COPY_NODE(opline->op1, src->op1);
|
COPY_NODE(opline->op1, src->op1);
|
||||||
MAKE_NOP(src);
|
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->opcode == ZEND_CAST &&
|
||||||
src->extended_value == IS_STRING) {
|
src->extended_value == IS_STRING) {
|
||||||
/* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
|
/* 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);
|
zend_op *src = VAR_SOURCE(opline->op2);
|
||||||
VAR_SOURCE(opline->op2) = NULL;
|
VAR_SOURCE(opline->op2) = NULL;
|
||||||
COPY_NODE(opline->op2, src->op1);
|
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);
|
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 */
|
/* adjust early binding list */
|
||||||
if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
|
if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
|
||||||
ZEND_ASSERT(op_array == &ctx->script->main_op_array);
|
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 */
|
/* Build CFG */
|
||||||
checkpoint = zend_arena_checkpoint(ctx->arena);
|
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);
|
zend_arena_release(&ctx->arena, checkpoint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
/* Update CV name table */
|
||||||
if (num_cvs != op_array->last_var) {
|
if (num_cvs != op_array->last_var) {
|
||||||
if (num_cvs) {
|
if (num_cvs) {
|
||||||
|
|
|
@ -478,79 +478,6 @@ static inline zend_bool may_break_varargs(const zend_op_array *op_array, const z
|
||||||
return 0;
|
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 dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reorder_dtor_effects) {
|
||||||
int i;
|
int i;
|
||||||
zend_ssa_phi *phi;
|
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 */
|
/* Eliminate dead instructions */
|
||||||
ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
|
ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
|
||||||
removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);
|
removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);
|
||||||
|
|
|
@ -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 */
|
/* update try/catch array */
|
||||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
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];
|
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);
|
take_successor_1(ssa, block_num, block);
|
||||||
goto optimize_nop;
|
goto optimize_nop;
|
||||||
} else {
|
} 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->opcode = ZEND_JMP;
|
||||||
opline->result_type = IS_UNUSED;
|
opline->result_type = IS_UNUSED;
|
||||||
zend_ssa_remove_result_def(ssa, ssa_op);
|
zend_ssa_remove_result_def(ssa, ssa_op);
|
||||||
|
|
|
@ -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);
|
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 */
|
/* update try/catch array */
|
||||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
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];
|
op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
|
||||||
|
|
|
@ -182,14 +182,6 @@ void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_c
|
||||||
opline--;
|
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);
|
zend_arena_release(&ctx->arena, checkpoint);
|
||||||
op_array->T = max + 1;
|
op_array->T = max + 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -340,7 +340,6 @@ static zend_bool try_replace_op2(
|
||||||
ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use);
|
ZEND_ASSERT(ssa_op->result_def == (ssa_op + 1)->op2_use);
|
||||||
if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) {
|
if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline + 1, &zv)) {
|
||||||
zend_ssa_op *next_op = ssa_op + 1;
|
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);
|
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 = -1;
|
||||||
next_op->op2_use_chain = -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;
|
uint32_t old_var = opline->result.var;
|
||||||
|
|
||||||
ssa_op->result_def = -1;
|
ssa_op->result_def = -1;
|
||||||
zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
|
|
||||||
if (opline->opcode == ZEND_DO_ICALL) {
|
if (opline->opcode == ZEND_DO_ICALL) {
|
||||||
removed_ops = remove_call(ctx, opline, ssa_op) - 1;
|
removed_ops = remove_call(ctx, opline, ssa_op) - 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2143,9 +2141,6 @@ static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var,
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} 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);
|
zend_ssa_remove_result_def(ssa, ssa_op);
|
||||||
if (opline->opcode == ZEND_DO_ICALL) {
|
if (opline->opcode == ZEND_DO_ICALL) {
|
||||||
removed_ops = remove_call(ctx, opline, ssa_op);
|
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_op->result_def >= 0) {
|
||||||
if (ssa->vars[ssa_op->result_def].use_chain < 0
|
if (ssa->vars[ssa_op->result_def].use_chain < 0
|
||||||
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
|
&& 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);
|
zend_ssa_remove_result_def(ssa, ssa_op);
|
||||||
opline->result_type = IS_UNUSED;
|
opline->result_type = IS_UNUSED;
|
||||||
} else if (opline->opcode != ZEND_PRE_INC &&
|
} else if (opline->opcode != ZEND_PRE_INC &&
|
||||||
|
|
|
@ -184,20 +184,24 @@ 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
|
/* 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
|
* not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
|
||||||
* is necessary for the correctness of temporary compaction. */
|
* 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;
|
uint32_t i;
|
||||||
const zend_op_array *op_array = scdf->op_array;
|
const zend_op_array *op_array = scdf->op_array;
|
||||||
const zend_cfg *cfg = &scdf->ssa->cfg;
|
const zend_cfg *cfg = &scdf->ssa->cfg;
|
||||||
for (i = 0; i < op_array->last_live_range; i++) {
|
const zend_basic_block *block = &cfg->blocks[block_idx];
|
||||||
zend_live_range *live_range = &op_array->live_range[i];
|
for (i = block->start; i < block->start + block->len; i++) {
|
||||||
uint32_t start_block = cfg->map[live_range->start];
|
zend_op *opline = &op_array->opcodes[i];
|
||||||
uint32_t end_block = cfg->map[live_range->end];
|
if (opline->opcode == ZEND_FE_FREE ||
|
||||||
|
(opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH)) {
|
||||||
if (end_block == block && start_block != block
|
zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline);
|
||||||
&& zend_bitset_in(scdf->executable_blocks, start_block)) {
|
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 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +216,7 @@ int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
|
||||||
for (i = 0; i < ssa->cfg.blocks_count; i++) {
|
for (i = 0; i < ssa->cfg.blocks_count; i++) {
|
||||||
if (!zend_bitset_in(scdf->executable_blocks, i)
|
if (!zend_bitset_in(scdf->executable_blocks, i)
|
||||||
&& (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
|
&& (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;
|
removed_ops += ssa->cfg.blocks[i].len;
|
||||||
zend_ssa_remove_block(scdf->op_array, ssa, i);
|
zend_ssa_remove_block(scdf->op_array, ssa, i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,49 +118,6 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
|
||||||
do {
|
do {
|
||||||
changed = 0;
|
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 */
|
/* Add exception paths */
|
||||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
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);
|
} 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;
|
zval *zv;
|
||||||
zend_bool extra_entry_block = 0;
|
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));
|
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;
|
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) {
|
if (op_array->last_try_catch) {
|
||||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||||
BB_START(op_array->try_catch_array[j].try_op);
|
BB_START(op_array->try_catch_array[j].try_op);
|
||||||
|
|
|
@ -29,8 +29,6 @@
|
||||||
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
|
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
|
||||||
#define ZEND_BB_FINALLY (1<<7) /* start of finally 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_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_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
|
||||||
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
|
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
|
||||||
|
|
||||||
|
@ -39,7 +37,7 @@
|
||||||
|
|
||||||
#define ZEND_BB_REACHABLE (1<<31)
|
#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 {
|
typedef struct _zend_basic_block {
|
||||||
int *successors; /* successor block indices */
|
int *successors; /* successor block indices */
|
||||||
|
@ -99,7 +97,6 @@ typedef struct _zend_cfg {
|
||||||
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
|
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
|
||||||
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
|
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
|
||||||
#define ZEND_SSA_RC_INFERENCE (1<<27)
|
#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_NO_ENTRY_PREDECESSORS (1<<25)
|
||||||
#define ZEND_CFG_RECV_ENTRY (1<<24)
|
#define ZEND_CFG_RECV_ENTRY (1<<24)
|
||||||
#define ZEND_CALL_TREE (1<<23)
|
#define ZEND_CALL_TREE (1<<23)
|
||||||
|
|
|
@ -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) {
|
if (b->flags & ZEND_BB_FINALLY_END) {
|
||||||
fprintf(stderr, " 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)) {
|
if (!(dump_flags & ZEND_DUMP_HIDE_UNREACHABLE) && !(b->flags & ZEND_BB_REACHABLE)) {
|
||||||
fprintf(stderr, " unreachable");
|
fprintf(stderr, " unreachable");
|
||||||
}
|
}
|
||||||
|
if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
|
||||||
|
fprintf(stderr, " unreachable_free");
|
||||||
|
}
|
||||||
if (b->flags & ZEND_BB_LOOP_HEADER) {
|
if (b->flags & ZEND_BB_LOOP_HEADER) {
|
||||||
fprintf(stderr, " 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");
|
fprintf(stderr, "LIVE RANGES:\n");
|
||||||
for (i = 0; i < op_array->last_live_range; i++) {
|
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 ",
|
fprintf(stderr, " %u: L%u - L%u ",
|
||||||
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
EX_VAR_TO_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK),
|
||||||
op_array->live_range[i].start,
|
op_array->live_range[i].start,
|
||||||
op_array->live_range[i].end);
|
op_array->live_range[i].end);
|
||||||
}
|
|
||||||
switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
|
switch (op_array->live_range[i].var & ZEND_LIVE_MASK) {
|
||||||
case ZEND_LIVE_TMPVAR:
|
case ZEND_LIVE_TMPVAR:
|
||||||
fprintf(stderr, "(tmp/var)\n");
|
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);
|
zend_dump_op(op_array, NULL, opline, dump_flags, data);
|
||||||
opline++;
|
opline++;
|
||||||
}
|
}
|
||||||
if (op_array->last_live_range) {
|
if (op_array->last_live_range && (dump_flags & ZEND_DUMP_LIVE_RANGES)) {
|
||||||
fprintf(stderr, "LIVE RANGES:\n");
|
fprintf(stderr, "LIVE RANGES:\n");
|
||||||
for (i = 0; i < op_array->last_live_range; i++) {
|
for (i = 0; i < op_array->last_live_range; i++) {
|
||||||
fprintf(stderr, " %u: L%u - L%u ",
|
fprintf(stderr, " %u: L%u - L%u ",
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define ZEND_DUMP_RC_INFERENCE (1<<1)
|
#define ZEND_DUMP_RC_INFERENCE (1<<1)
|
||||||
#define ZEND_DUMP_CFG (1<<2)
|
#define ZEND_DUMP_CFG (1<<2)
|
||||||
#define ZEND_DUMP_SSA (1<<3)
|
#define ZEND_DUMP_SSA (1<<3)
|
||||||
|
#define ZEND_DUMP_LIVE_RANGES (1<<4)
|
||||||
#define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS
|
#define ZEND_DUMP_RT_CONSTANTS ZEND_RT_CONSTANTS
|
||||||
|
|
||||||
BEGIN_EXTERN_C()
|
BEGIN_EXTERN_C()
|
||||||
|
|
|
@ -611,104 +611,6 @@ handle_static_prop:
|
||||||
return 1;
|
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,
|
int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||||
zend_op *opline,
|
zend_op *opline,
|
||||||
zend_uchar type,
|
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);
|
ZEND_ASSERT(m->opcode == ZEND_FREE && m->op1_type == type && m->op1.var == var);
|
||||||
MAKE_NOP(m);
|
MAKE_NOP(m);
|
||||||
zval_ptr_dtor_nogc(val);
|
zval_ptr_dtor_nogc(val);
|
||||||
zend_optimizer_remove_live_range(op_array, var);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case ZEND_SWITCH_LONG:
|
case ZEND_SWITCH_LONG:
|
||||||
case ZEND_SWITCH_STRING:
|
case ZEND_SWITCH_STRING:
|
||||||
case ZEND_CASE:
|
case ZEND_CASE: {
|
||||||
case ZEND_FREE: {
|
zend_op *end = op_array->opcodes + op_array->last;
|
||||||
zend_op *m, *n;
|
while (opline < end) {
|
||||||
int brk = op_array->last_live_range;
|
if (opline->op1_type == type && opline->op1.var == var) {
|
||||||
zend_bool in_switch = 0;
|
if (opline->opcode == ZEND_CASE
|
||||||
while (brk--) {
|
|| opline->opcode == ZEND_SWITCH_LONG
|
||||||
if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
|
|| opline->opcode == ZEND_SWITCH_STRING) {
|
||||||
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) {
|
|
||||||
zval v;
|
zval v;
|
||||||
|
|
||||||
if (m->opcode == ZEND_CASE) {
|
if (opline->opcode == ZEND_CASE) {
|
||||||
m->opcode = ZEND_IS_EQUAL;
|
opline->opcode = ZEND_IS_EQUAL;
|
||||||
}
|
}
|
||||||
ZVAL_COPY(&v, val);
|
ZVAL_COPY(&v, val);
|
||||||
if (Z_TYPE(v) == IS_STRING) {
|
if (Z_TYPE(v) == IS_STRING) {
|
||||||
zend_string_hash_val(Z_STR(v));
|
zend_string_hash_val(Z_STR(v));
|
||||||
}
|
}
|
||||||
m->op1.constant = zend_optimizer_add_literal(op_array, &v);
|
opline->op1.constant = zend_optimizer_add_literal(op_array, &v);
|
||||||
m->op1_type = IS_CONST;
|
opline->op1_type = IS_CONST;
|
||||||
} else if (m->opcode == ZEND_FREE) {
|
} else if (opline->opcode == ZEND_FREE) {
|
||||||
MAKE_NOP(m);
|
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 {
|
} else {
|
||||||
ZEND_ASSERT(0);
|
ZEND_ASSERT(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m++;
|
opline++;
|
||||||
}
|
}
|
||||||
zval_ptr_dtor_nogc(val);
|
zval_ptr_dtor_nogc(val);
|
||||||
zend_optimizer_remove_live_range(op_array, var);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case ZEND_VERIFY_RETURN_TYPE: {
|
case ZEND_VERIFY_RETURN_TYPE: {
|
||||||
|
@ -858,20 +737,12 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (zend_optimizer_update_op1_const(op_array, opline, val)) {
|
return zend_optimizer_update_op1_const(op_array, opline, val);
|
||||||
zend_optimizer_remove_live_range(op_array, var);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opline->op2_type == type &&
|
if (opline->op2_type == type &&
|
||||||
opline->op2.var == var) {
|
opline->op2.var == var) {
|
||||||
if (zend_optimizer_update_op2_const(op_array, opline, val)) {
|
return zend_optimizer_update_op2_const(op_array, opline, val);
|
||||||
zend_optimizer_remove_live_range(op_array, var);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
opline++;
|
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,
|
static void zend_optimize(zend_op_array *op_array,
|
||||||
zend_optimizer_ctx *ctx)
|
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) {
|
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
|
/* pass 1
|
||||||
|
@ -1442,6 +1326,8 @@ static void zend_optimize_op_array(zend_op_array *op_array,
|
||||||
|
|
||||||
/* Redo pass_two() */
|
/* Redo pass_two() */
|
||||||
zend_redo_pass_two(op_array);
|
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)
|
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
|
#endif
|
||||||
|
|
||||||
int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
|
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]);
|
func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
|
||||||
if (func_info && func_info->ssa.var_info) {
|
if (func_info && func_info->ssa.var_info) {
|
||||||
zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
|
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 {
|
} else {
|
||||||
zend_redo_pass_two(call_graph.op_arrays[i]);
|
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) &&
|
if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) &&
|
||||||
(ZEND_OPTIMIZER_PASS_7 & optimization_level)) {
|
(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_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_END();
|
||||||
|
|
||||||
ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
|
ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
|
||||||
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
|
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)) {
|
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();
|
||||||
} ZEND_HASH_FOREACH_END();
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
|
|
@ -90,9 +90,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||||
zend_uchar type,
|
zend_uchar type,
|
||||||
uint32_t var,
|
uint32_t var,
|
||||||
zval *val);
|
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_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||||
void zend_optimizer_pass2(zend_op_array *op_array);
|
void zend_optimizer_pass2(zend_op_array *op_array);
|
||||||
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||||
|
|
|
@ -1436,9 +1436,6 @@ void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{
|
||||||
continue;
|
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_defs_of_instr(ssa, &ssa->ops[j]);
|
||||||
zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]);
|
zend_ssa_remove_instr(ssa, &op_array->opcodes[j], &ssa->ops[j]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue