From 948b7f542162c83db4a748d446c0440caba161ee Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 11 Nov 2015 11:12:44 +0300 Subject: [PATCH] Changed ZEND_FREE.op2.num and ZEND_FE_FREE.op2.num back to use live_range_offset (try_catch_offset does't work) --- Zend/tests/temporary_cleaning_009.phpt | 27 ++++++++++ Zend/tests/temporary_cleaning_010.phpt | 23 +++++++++ Zend/tests/try/try_finally_021.phpt | 20 ++++++++ Zend/tests/try/try_finally_022.phpt | 41 +++++++++++++++ Zend/zend_compile.c | 70 +++++++++++++++----------- Zend/zend_execute.c | 12 ++--- Zend/zend_vm_def.h | 13 +++-- Zend/zend_vm_execute.h | 9 ++-- Zend/zend_vm_gen.php | 3 ++ Zend/zend_vm_opcodes.c | 4 +- Zend/zend_vm_opcodes.h | 2 + ext/opcache/Optimizer/zend_optimizer.c | 19 ++++++- 12 files changed, 192 insertions(+), 51 deletions(-) create mode 100644 Zend/tests/temporary_cleaning_009.phpt create mode 100644 Zend/tests/temporary_cleaning_010.phpt create mode 100644 Zend/tests/try/try_finally_021.phpt create mode 100644 Zend/tests/try/try_finally_022.phpt diff --git a/Zend/tests/temporary_cleaning_009.phpt b/Zend/tests/temporary_cleaning_009.phpt new file mode 100644 index 0000000000..32a84a6ffd --- /dev/null +++ b/Zend/tests/temporary_cleaning_009.phpt @@ -0,0 +1,27 @@ +--TEST-- +Live range & free on return +--FILE-- +foo; + } +} +foreach (new bar as &$foo) { + try { + $foo = new Exception; + return; // frees the loop variable + } catch (Exception $e) { + echo "exception\n"; + } +} +echo "end\n"; +?> +--EXPECTF-- +Fatal error: Uncaught Exception in %stemporary_cleaning_009.php:12 +Stack trace: +#0 {main} + thrown in %stemporary_cleaning_009.php on line 12 diff --git a/Zend/tests/temporary_cleaning_010.phpt b/Zend/tests/temporary_cleaning_010.phpt new file mode 100644 index 0000000000..96eab121b8 --- /dev/null +++ b/Zend/tests/temporary_cleaning_010.phpt @@ -0,0 +1,23 @@ +--TEST-- +Live range & throw from finally +--XFAIL-- +See Bug #62210 and attempt to fix it in "tmp_livelibess" branch +--FILE-- + +--EXPECT-- +exception diff --git a/Zend/tests/try/try_finally_021.phpt b/Zend/tests/try/try_finally_021.phpt new file mode 100644 index 0000000000..ed162f40d0 --- /dev/null +++ b/Zend/tests/try/try_finally_021.phpt @@ -0,0 +1,20 @@ +--TEST-- +Live range & return from finally +--FILE-- + +--EXPECT-- +ok +ok diff --git a/Zend/tests/try/try_finally_022.phpt b/Zend/tests/try/try_finally_022.phpt new file mode 100644 index 0000000000..51f6a26419 --- /dev/null +++ b/Zend/tests/try/try_finally_022.phpt @@ -0,0 +1,41 @@ +--TEST-- +Try finally (exception in "return" statement) +--FILE-- + +--EXPECT-- +exception2 + diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 4f4701189c..3e2c58ddf2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -58,7 +58,10 @@ typedef struct _zend_loop_var { zend_uchar opcode; zend_uchar var_type; uint32_t var_num; - uint32_t try_catch_offset; + union { + uint32_t try_catch_offset; + uint32_t live_range_offset; + } u; } zend_loop_var; static inline void zend_alloc_cache_slot(uint32_t literal) { @@ -573,15 +576,25 @@ void zend_stop_lexing(void) LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit); } -static void zend_add_live_range(zend_op_array *op_array, uint32_t start, uint32_t end) /* {{{ */ +static uint32_t zend_start_live_range(zend_op_array *op_array, uint32_t start) /* {{{ */ { zend_live_range *range; - if (start != end) { - 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; + 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(zend_op_array *op_array, uint32_t offset, uint32_t end) /* {{{ */ +{ + zend_live_range *range = op_array->live_range + offset; + + if (range->start == end && offset == op_array->last_live_range - 1) { + op_array->last_live_range--; + } else { range->end = end; } } @@ -598,11 +611,13 @@ static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var brk_cont_element->parent = parent; if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) { + uint32_t start = get_next_op_number(CG(active_op_array)); + info.opcode = free_opcode; info.var_type = loop_var->op_type; info.var_num = loop_var->u.op.var; - info.try_catch_offset = CG(active_op_array)->last_try_catch; - brk_cont_element->start = get_next_op_number(CG(active_op_array)); + info.u.live_range_offset = zend_start_live_range(CG(active_op_array), start); + brk_cont_element->start = start; } else { info.opcode = ZEND_NOP; /* The start field is used to free temporary variables in case of exceptions. @@ -616,12 +631,18 @@ static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var static inline void zend_end_loop(int cont_addr) /* {{{ */ { + uint32_t end = get_next_op_number(CG(active_op_array)); zend_brk_cont_element *brk_cont_element = &CG(context).brk_cont_array[CG(context).current_brk_cont]; brk_cont_element->cont = cont_addr; - brk_cont_element->brk = get_next_op_number(CG(active_op_array)); + 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(CG(active_op_array), loop_var->u.live_range_offset, end); + } + zend_stack_del_top(&CG(loop_var_stack)); } /* }}} */ @@ -3591,7 +3612,7 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */ opline->result.var = loop_var->var_num; SET_UNUSED(opline->op1); SET_UNUSED(opline->op2); - opline->op1.num = loop_var->try_catch_offset; + opline->op1.num = loop_var->u.try_catch_offset; } else if (loop_var->opcode == ZEND_RETURN) { /* Stack separator */ break; @@ -3609,7 +3630,7 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */ opline->op1_type = loop_var->var_type; opline->op1.var = loop_var->var_num; SET_UNUSED(opline->op2); - opline->op2.num = loop_var->try_catch_offset; + opline->op2.num = loop_var->u.live_range_offset; opline->extended_value = ZEND_FREE_ON_RETURN; depth--; } @@ -4010,8 +4031,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ zend_end_loop(opnum_fetch); opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL); - zend_add_live_range(CG(active_op_array), - opnum_fetch, opline - CG(active_op_array)->opcodes); } /* }}} */ @@ -4068,11 +4087,10 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ znode expr_node, case_node; zend_op *opline; uint32_t *jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0); - uint32_t opnum_default_jmp, opnum_start; + uint32_t opnum_default_jmp; zend_compile_expr(&expr_node, expr_ast); - opnum_start = get_next_op_number(CG(active_op_array)); zend_begin_loop(ZEND_FREE, &expr_node); case_node.op_type = IS_TMP_VAR; @@ -4136,8 +4154,6 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) { opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL); - zend_add_live_range(CG(active_op_array), - opnum_start, opline - CG(active_op_array)->opcodes); } else if (expr_node.op_type == IS_CONST) { zval_dtor(&expr_node.u.constant); } @@ -4185,7 +4201,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.try_catch_offset = try_catch_offset; + fast_call.u.try_catch_offset = try_catch_offset; zend_stack_push(&CG(loop_var_stack), &fast_call); } @@ -6451,10 +6467,9 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *expr_ast = ast->child[0]; znode silence_node; - uint32_t begin_opline_num; - zend_op *opline; + uint32_t range; - begin_opline_num = get_next_op_number(CG(active_op_array)); + range = zend_start_live_range(CG(active_op_array), get_next_op_number(CG(active_op_array))); zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL); if (expr_ast->kind == ZEND_AST_VAR) { @@ -6465,12 +6480,11 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */ zend_compile_expr(result, expr_ast); } - opline = zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL); - /* Store BEGIN_SILENCE/END_SILENCE pair to restore previous * EG(error_reporting) value on exception */ - zend_add_live_range(CG(active_op_array), - begin_opline_num + 1, opline - CG(active_op_array)->opcodes); + zend_end_live_range(CG(active_op_array), range, get_next_op_number(CG(active_op_array))); + + zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL); } /* }}} */ @@ -6790,6 +6804,7 @@ 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(CG(active_op_array), rope_init_lineno); init_opline->extended_value = j; opline->opcode = ZEND_ROPE_END; @@ -6804,8 +6819,7 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ i--; } - zend_add_live_range(CG(active_op_array), - rope_init_lineno, opline - CG(active_op_array)->opcodes); + zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes); /* Update all the previous opcodes to use the same variable */ while (opline != init_opline) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 0fd99e9f73..c0c9271f4b 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2549,16 +2549,12 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, { int i; - i = EX(func)->op_array.last_live_range; - while (i) { - const zend_live_range *range; - - i--; - range = &EX(func)->op_array.live_range[i]; - if (range->end <= op_num) { + for (i = 0; i < EX(func)->op_array.last_live_range; i++) { + const zend_live_range *range = &EX(func)->op_array.live_range[i]; + if (range->start > op_num) { /* further blocks will not be relevant... */ break; - } else if (op_num >= range->start) { + } else if (op_num < range->end) { if (!catch_op_num || catch_op_num >= range->end) { zend_op *opline = &EX(func)->op_array.opcodes[range->end]; uint32_t var_num = opline->op1.var; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 55411de085..f8f0fd9c0f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2711,7 +2711,7 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, JMP_ADDR) ZEND_VM_JMP(opline); } -ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, TRY_CATCH) +ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, LIVE_RANGE) { USE_OPLINE @@ -2720,7 +2720,7 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, TRY_CATCH) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, TRY_CATCH) +ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, LIVE_RANGE) { zval *var; USE_OPLINE @@ -7231,7 +7231,6 @@ ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY) ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { uint32_t op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes; - uint32_t last_try_catch = EX(func)->op_array.last_try_catch; int i; uint32_t catch_op_num = 0, finally_op_num = 0, finally_op_end = 0; int in_finally = 0; @@ -7243,14 +7242,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE) && exc_opline->extended_value & ZEND_FREE_ON_RETURN) { /* exceptions thrown because of loop var destruction on return/break/... - * are logically thrown at the end of the foreach loop, - * so don't check the inner exception regions + * are logically thrown at the end of the foreach loop, so adjust the + * op_num. */ - last_try_catch = exc_opline->op2.num; + op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end; } } - for (i = 0; i < last_try_catch; i++) { + for (i = 0; i < EX(func)->op_array.last_try_catch; i++) { if (EX(func)->op_array.try_catch_array[i].try_op > op_num) { /* further blocks will not be relevant... */ break; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9f2b247813..c4aee3fa3e 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1476,7 +1476,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { uint32_t op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes; - uint32_t last_try_catch = EX(func)->op_array.last_try_catch; int i; uint32_t catch_op_num = 0, finally_op_num = 0, finally_op_end = 0; int in_finally = 0; @@ -1488,14 +1487,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER( if ((exc_opline->opcode == ZEND_FREE || exc_opline->opcode == ZEND_FE_FREE) && exc_opline->extended_value & ZEND_FREE_ON_RETURN) { /* exceptions thrown because of loop var destruction on return/break/... - * are logically thrown at the end of the foreach loop, - * so don't check the inner exception regions + * are logically thrown at the end of the foreach loop, so adjust the + * op_num. */ - last_try_catch = exc_opline->op2.num; + op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end; } } - for (i = 0; i < last_try_catch; i++) { + for (i = 0; i < EX(func)->op_array.last_try_catch; i++) { if (EX(func)->op_array.try_catch_array[i].try_op > op_num) { /* further blocks will not be relevant... */ break; diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index fb6681a0c4..17bcfdd8e1 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -61,6 +61,7 @@ $vm_op_flags = array( "ZEND_VM_OP1_NUM" => 1<<3, "ZEND_VM_OP1_JMP_ADDR" => 1<<4, "ZEND_VM_OP1_TRY_CATCH" => 1<<5, + "ZEND_VM_OP1_LIVE_RANGE" => 1<<6, "ZEND_VM_OP2_SPEC" => 1<<8, "ZEND_VM_OP2_CONST" => 1<<9, @@ -68,6 +69,7 @@ $vm_op_flags = array( "ZEND_VM_OP2_NUM" => 1<<11, "ZEND_VM_OP2_JMP_ADDR" => 1<<12, "ZEND_VM_OP2_TRY_CATCH" => 1<<13, + "ZEND_VM_OP2_LIVE_RANGE" => 1<<14, "ZEND_VM_EXT_NUM" => 1<<16, "ZEND_VM_EXT_VAR" => 1<<17, @@ -99,6 +101,7 @@ $vm_op_decode = array( "NUM" => ZEND_VM_OP1_NUM, "JMP_ADDR" => ZEND_VM_OP1_JMP_ADDR, "TRY_CATCH" => ZEND_VM_OP1_TRY_CATCH, + "LIVE_RANGE" => ZEND_VM_OP1_LIVE_RANGE, ); $vm_ext_decode = array( diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index d53ce5ecd9..b56cd930ff 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -277,7 +277,7 @@ static uint32_t zend_vm_opcodes_flags[182] = { 0x00000801, 0x00011003, 0x00010300, - 0x00002005, + 0x00004005, 0x00800703, 0x00010703, 0x02000007, @@ -334,7 +334,7 @@ static uint32_t zend_vm_opcodes_flags[182] = { 0x00000103, 0x00001003, 0x00040001, - 0x00002005, + 0x00004005, 0x00010700, 0x00000000, 0x00000000, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 039f6d8d4e..52b4d0b5aa 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -34,12 +34,14 @@ #define ZEND_VM_OP1_NUM 0x00000008 #define ZEND_VM_OP1_JMP_ADDR 0x00000010 #define ZEND_VM_OP1_TRY_CATCH 0x00000020 +#define ZEND_VM_OP1_LIVE_RANGE 0x00000040 #define ZEND_VM_OP2_SPEC 0x00000100 #define ZEND_VM_OP2_CONST 0x00000200 #define ZEND_VM_OP2_TMPVAR 0x00000400 #define ZEND_VM_OP2_NUM 0x00000800 #define ZEND_VM_OP2_JMP_ADDR 0x00001000 #define ZEND_VM_OP2_TRY_CATCH 0x00002000 +#define ZEND_VM_OP2_LIVE_RANGE 0x00004000 #define ZEND_VM_EXT_NUM 0x00010000 #define ZEND_VM_EXT_VAR 0x00020000 #define ZEND_VM_EXT_JMP_ADDR 0x00040000 diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 20b7a75f56..eb77255309 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -352,9 +352,14 @@ 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; + uint32_t *map; + ALLOCA_FLAG(use_heap); + + map = (uint32_t *)DO_ALLOCA(sizeof(uint32_t) * op_array->last_live_range); do { if (op_array->opcodes[op_array->live_range[i].end].op1.var != var) { + map[i] = j; if (i != j) { op_array->live_range[j] = op_array->live_range[i]; } @@ -362,7 +367,19 @@ void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var) } i++; } while (i < op_array->last_live_range); - op_array->last_live_range = j; + if (i != j) { + zend_op *opline = op_array->opcodes; + zend_op *end = opline + op_array->last; + + op_array->last_live_range = j; + while (opline != end) { + if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) && + opline->extended_value == ZEND_FREE_ON_RETURN) { + opline->op2.num = map[opline->op2.num]; + } + opline++; + } + } } } -- 2.40.0