summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-11-13 15:35:07 +0300
committerDmitry Stogov <dmitry@zend.com>2015-11-13 15:35:07 +0300
commit3a5fa926bfc409963f500144ea6d5e890e712a86 (patch)
treef213ba3fd5557e6f4f7418c18451e851983d5dbe
parentcbe906226aeafb1d3e4b47744ceaff47ab130be2 (diff)
downloadphp-git-3a5fa926bfc409963f500144ea6d5e890e712a86.tar.gz
Squashed commit of the following:
commit afe963e6cc289696e60c6c679796ba2197c52b3b Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Nov 13 15:32:29 2015 +0300 Added news entry commit a126b891c97848dd7ef8f1abf716328c46e0f19c Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Nov 13 15:29:21 2015 +0300 VERIFY_RETURN_TYPE doesn't have to cleanup operand on exception, bacause now, live temporary variables are released by exception unwinder. commit 0db475e98786e6bcaa8401ee3e0b33743b9a2f2b Author: Dmitry Stogov <dmitry@zend.com> Date: Thu Nov 12 22:55:39 2015 +0300 Fixed copy/paste commit 0ac73fe7174bec9de9a610319a98b259bea67f7f Author: Dmitry Stogov <dmitry@zend.com> Date: Wed Nov 11 16:11:50 2015 +0300 Fixed bug #62210 (Exceptions can leak temporary variables)
-rw-r--r--NEWS3
-rw-r--r--Zend/tests/temporary_cleaning_001.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_003.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_004.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_005.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_006.phpt2
-rw-r--r--Zend/tests/temporary_cleaning_011.phpt20
-rw-r--r--Zend/zend_compile.c183
-rw-r--r--Zend/zend_compile.h7
-rw-r--r--Zend/zend_execute.c12
-rw-r--r--Zend/zend_opcode.c10
-rw-r--r--Zend/zend_vm_def.h2
-rw-r--r--Zend/zend_vm_execute.h10
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c8
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c2
15 files changed, 205 insertions, 62 deletions
diff --git a/NEWS b/NEWS
index 8c459625bf..1d9fd2a100 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? 2016, PHP 7.1.0
+Core:
+ . Fixed bug #62210 (Exceptions can leak temporary variables). (Dmitry, Bob)
+
Standard
. Implemented FR #55716 (Add an option to pass a custom stream context to
get_headers()). (Ferenc)
diff --git a/Zend/tests/temporary_cleaning_001.phpt b/Zend/tests/temporary_cleaning_001.phpt
index 40340bc3da..f2ccbb35b8 100644
--- a/Zend/tests/temporary_cleaning_001.phpt
+++ b/Zend/tests/temporary_cleaning_001.phpt
@@ -1,7 +1,5 @@
--TEST--
Temporary leak on exception
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_003.phpt b/Zend/tests/temporary_cleaning_003.phpt
index 0f7d9450eb..acff8c85f0 100644
--- a/Zend/tests/temporary_cleaning_003.phpt
+++ b/Zend/tests/temporary_cleaning_003.phpt
@@ -1,7 +1,5 @@
--TEST--
Fundamental memory leak test on temporaries
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_004.phpt b/Zend/tests/temporary_cleaning_004.phpt
index e2b093654f..b8a02516b0 100644
--- a/Zend/tests/temporary_cleaning_004.phpt
+++ b/Zend/tests/temporary_cleaning_004.phpt
@@ -1,7 +1,5 @@
--TEST--
Temporary leak with switch
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_005.phpt b/Zend/tests/temporary_cleaning_005.phpt
index f671c32543..e8c7febe0b 100644
--- a/Zend/tests/temporary_cleaning_005.phpt
+++ b/Zend/tests/temporary_cleaning_005.phpt
@@ -1,7 +1,5 @@
--TEST--
Temporary leak with foreach
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_006.phpt b/Zend/tests/temporary_cleaning_006.phpt
index 435e7b12dd..a7936d3915 100644
--- a/Zend/tests/temporary_cleaning_006.phpt
+++ b/Zend/tests/temporary_cleaning_006.phpt
@@ -1,7 +1,5 @@
--TEST--
Exception after separation during indirect write to fcall result
---XFAIL--
-See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
--FILE--
<?php
diff --git a/Zend/tests/temporary_cleaning_011.phpt b/Zend/tests/temporary_cleaning_011.phpt
new file mode 100644
index 0000000000..e4a6af3ab9
--- /dev/null
+++ b/Zend/tests/temporary_cleaning_011.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Live range & lists
+--FILE--
+<?php
+class A {
+ function __destruct() {
+ throw new Exception();
+ }
+}
+$b = new A();
+$x = 0;
+$c = [[$x,$x]];
+try {
+ list($a, $b) = $c[0];
+} catch (Exception $e) {
+ echo "exception\n";
+}
+?>
+--EXPECT--
+exception
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index c2bc756c84..e2398b7f62 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -86,6 +86,8 @@ ZEND_API zend_compiler_globals compiler_globals;
ZEND_API zend_executor_globals executor_globals;
#endif
+static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2);
+
static void zend_destroy_property_info_internal(zval *zv) /* {{{ */
{
zend_property_info *property_info = Z_PTR_P(zv);
@@ -588,7 +590,7 @@ static uint32_t zend_start_live_range(zend_op_array *op_array, uint32_t start) /
}
/* }}} */
-static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32_t end) /* {{{ */
+static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32_t end, uint32_t kind, uint32_t var) /* {{{ */
{
zend_live_range *range = op_array->live_range + offset;
@@ -596,6 +598,7 @@ static void zend_end_live_range(zend_op_array *op_array, uint32_t offset, uint32
op_array->last_live_range--;
} else {
range->end = end;
+ range->var = (var * sizeof(zval)) | kind;
}
}
/* }}} */
@@ -629,7 +632,7 @@ static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var
}
/* }}} */
-static inline void zend_end_loop(int cont_addr) /* {{{ */
+static inline void zend_end_loop(int cont_addr, const znode *var_node) /* {{{ */
{
uint32_t end = get_next_op_number(CG(active_op_array));
zend_brk_cont_element *brk_cont_element
@@ -640,7 +643,9 @@ static inline void zend_end_loop(int cont_addr) /* {{{ */
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_end_live_range(CG(active_op_array), 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));
@@ -650,11 +655,7 @@ static inline void zend_end_loop(int cont_addr) /* {{{ */
void zend_do_free(znode *op1) /* {{{ */
{
if (op1->op_type==IS_TMP_VAR) {
- zend_op *opline = get_next_op(CG(active_op_array));
-
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
} else if (op1->op_type==IS_VAR) {
zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
@@ -670,10 +671,7 @@ void zend_do_free(znode *op1) /* {{{ */
/* It's very rare and useless case. It's better to use
additional FREE opcode and simplify the FETCH handlers
their selves */
- opline = get_next_op(CG(active_op_array));
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
} else {
opline->result_type |= EXT_TYPE_UNUSED;
}
@@ -682,11 +680,7 @@ void zend_do_free(znode *op1) /* {{{ */
if (opline->opcode == ZEND_FETCH_LIST &&
opline->op1_type == IS_VAR &&
opline->op1.var == op1->u.op.var) {
- opline = get_next_op(CG(active_op_array));
-
- opline->opcode = ZEND_FREE;
- SET_NODE(opline->op1, op1);
- SET_UNUSED(opline->op2);
+ zend_emit_op(NULL, ZEND_FREE, op1, NULL);
return;
}
if (opline->result_type==IS_VAR
@@ -1889,6 +1883,119 @@ 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 does't have to be destroyed */
+ break;
+ } else if (def->opcode == ZEND_DECLARE_CLASS ||
+ def->opcode == ZEND_DECLARE_INHERITED_CLASS ||
+ def->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED ||
+ 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) */
+ def = CG(active_op_array)->opcodes + def->op2.opline_num - 1;
+ if (def + 1 == opline) {
+ break;
+ }
+ }
+ zend_end_live_range(CG(active_op_array),
+ zend_start_live_range(CG(active_op_array),
+ def + 1 - CG(active_op_array)->opcodes),
+ opline - CG(active_op_array)->opcodes,
+ ZEND_LIVE_TMPVAR, def->result.var);
+ break;
+ }
+ }
+}
+/* }}} */
+
+static zend_always_inline int zend_is_def_range(zend_op *opline, zend_uchar type, uint32_t var) /* {{{ */
+{
+ return opline->result_type == type &&
+ opline->result.var == var &&
+ opline->opcode != ZEND_ADD_ARRAY_ELEMENT &&
+ opline->opcode != ZEND_ROPE_ADD;
+}
+/* }}} */
+
+static void zend_check_live_ranges(zend_op *opline) /* {{{ */
+{
+ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) &&
+ !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 ||
+ opline->opcode == ZEND_ADD_INTERFACE ||
+ opline->opcode == ZEND_ADD_TRAIT ||
+ opline->opcode == ZEND_BIND_TRAITS ||
+ opline->opcode == ZEND_VERIFY_ABSTRACT_CLASS) {
+ /* 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_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 ||
+ opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
+ /* these opcodes are handled separately */
+ } else {
+ zend_find_live_range(opline, opline->op1_type, opline->op1.var);
+ }
+ }
+
+ if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) &&
+ !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 zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */
{
zend_op *opline = get_next_op(CG(active_op_array));
@@ -1906,6 +2013,8 @@ static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode
SET_NODE(opline->op2, op2);
}
+ zend_check_live_ranges(opline);
+
if (result) {
zend_make_var_result(result, opline);
}
@@ -1930,6 +2039,8 @@ static zend_op *zend_emit_op_tmp(znode *result, zend_uchar opcode, znode *op1, z
SET_NODE(opline->op2, op2);
}
+ zend_check_live_ranges(opline);
+
if (result) {
zend_make_tmp_result(result, opline);
}
@@ -2044,6 +2155,7 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
for (i = offset; i < count; ++i) {
opline = get_next_op(CG(active_op_array));
memcpy(opline, &oplines[i], sizeof(zend_op));
+ zend_check_live_ranges(opline);
}
CG(delayed_oplines_stack).top = offset;
return opline;
@@ -2838,10 +2950,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
}
}
- opline = get_next_op(CG(active_op_array));
- opline->opcode = opcode;
- SET_NODE(opline->op1, &arg_node);
- SET_UNUSED(opline->op2);
+ opline = zend_emit_op(NULL, opcode, &arg_node, NULL);
opline->op2.opline_num = arg_num;
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_num);
@@ -2944,13 +3053,14 @@ void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast) /
void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast) /* {{{ */
{
- zend_op *opline = get_next_op(CG(active_op_array));
if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) {
const char *colon;
zend_string *str = Z_STR(name_node->u.constant);
if ((colon = zend_memrchr(ZSTR_VAL(str), ':', ZSTR_LEN(str))) != NULL && colon > ZSTR_VAL(str) && *(colon - 1) == ':') {
zend_string *class = zend_string_init(ZSTR_VAL(str), colon - ZSTR_VAL(str) - 1, 0);
zend_string *method = zend_string_init(colon + 1, ZSTR_LEN(str) - (colon - ZSTR_VAL(str)) - 1, 0);
+ zend_op *opline = get_next_op(CG(active_op_array));
+
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
opline->op1_type = IS_CONST;
opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), class);
@@ -2959,6 +3069,8 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
zend_alloc_cache_slot(opline->op2.constant);
zval_ptr_dtor(&name_node->u.constant);
} else {
+ zend_op *opline = get_next_op(CG(active_op_array));
+
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
opline->op2_type = IS_CONST;
@@ -2966,9 +3078,7 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
zend_alloc_cache_slot(opline->op2.constant);
}
} else {
- opline->opcode = ZEND_INIT_DYNAMIC_CALL;
- SET_UNUSED(opline->op1);
- SET_NODE(opline->op2, name_node);
+ zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node);
}
zend_compile_call_common(result, args_ast, NULL);
@@ -3419,6 +3529,7 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
} else {
SET_NODE(opline->op2, &method_node);
}
+ zend_check_live_ranges(opline);
zend_compile_call_common(result, args_ast, NULL);
}
@@ -3871,7 +3982,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start);
- zend_end_loop(opnum_cond);
+ zend_end_loop(opnum_cond, NULL);
}
/* }}} */
@@ -3893,7 +4004,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, opnum_start);
- zend_end_loop(opnum_cond);
+ zend_end_loop(opnum_cond, NULL);
}
/* }}} */
@@ -3949,7 +4060,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */
zend_emit_cond_jump(ZEND_JMPNZ, &result, opnum_start);
- zend_end_loop(opnum_loop);
+ zend_end_loop(opnum_loop, NULL);
}
/* }}} */
@@ -4028,7 +4139,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
opline = &CG(active_op_array)->opcodes[opnum_fetch];
opline->extended_value = get_next_op_number(CG(active_op_array));
- zend_end_loop(opnum_fetch);
+ zend_end_loop(opnum_fetch, &reset_node);
opline = zend_emit_op(NULL, ZEND_FE_FREE, &reset_node, NULL);
}
@@ -4150,10 +4261,14 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
zend_update_jump_target_to_next(opnum_default_jmp);
}
- zend_end_loop(get_next_op_number(CG(active_op_array)));
+ zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node);
if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) {
- opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
+ /* don't use emit_op() to prevent automatic live-range construction */
+ opline = get_next_op(CG(active_op_array));
+ opline->opcode = ZEND_FREE;
+ SET_NODE(opline->op1, &expr_node);
+ SET_UNUSED(opline->op2);
} else if (expr_node.op_type == IS_CONST) {
zval_dtor(&expr_node.u.constant);
}
@@ -6482,7 +6597,8 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
/* Store BEGIN_SILENCE/END_SILENCE pair to restore previous
* EG(error_reporting) value on exception */
- zend_end_live_range(CG(active_op_array), range, get_next_op_number(CG(active_op_array)));
+ zend_end_live_range(CG(active_op_array), range, get_next_op_number(CG(active_op_array)),
+ ZEND_LIVE_SILENCE, silence_node.u.op.var);
zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
}
@@ -6819,7 +6935,8 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
i--;
}
- zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes);
+ zend_end_live_range(CG(active_op_array), 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) {
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 6e37aa5bb3..ff74ec26e5 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -173,7 +173,14 @@ typedef struct _zend_try_catch_element {
uint32_t finally_end;
} zend_try_catch_element;
+#define ZEND_LIVE_TMPVAR 0
+#define ZEND_LIVE_LOOP 1
+#define ZEND_LIVE_SILENCE 2
+#define ZEND_LIVE_ROPE 3
+#define ZEND_LIVE_MASK 3
+
typedef struct _zend_live_range {
+ uint32_t var; /* low bits are used for variable type (ZEND_LIVE_* macros) */
uint32_t start;
uint32_t end;
} zend_live_range;
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index c0c9271f4b..aa48b06767 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -2556,18 +2556,18 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num,
break;
} 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;
+ uint32_t kind = range->var & ZEND_LIVE_MASK;
+ uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
zval *var = EX_VAR(var_num);
- if (opline->opcode == ZEND_FREE) {
+ if (kind == ZEND_LIVE_TMPVAR) {
zval_ptr_dtor_nogc(var);
- } else if (opline->opcode == ZEND_FE_FREE) {
+ } else if (kind == ZEND_LIVE_LOOP) {
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
zend_hash_iterator_del(Z_FE_ITER_P(var));
}
zval_ptr_dtor_nogc(var);
- } else if (opline->opcode == ZEND_ROPE_END) {
+ } else if (kind == ZEND_LIVE_ROPE) {
zend_string **rope = (zend_string **)var;
zend_op *last = EX(func)->op_array.opcodes + op_num;
while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
@@ -2583,7 +2583,7 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num,
zend_string_release(rope[j]);
} while (j--);
}
- } else if (opline->opcode == ZEND_END_SILENCE) {
+ } else if (kind == ZEND_LIVE_SILENCE) {
/* restore previous error_reporting value */
if (!EG(error_reporting) && Z_LVAL_P(var) != 0) {
EG(error_reporting) = Z_LVAL_P(var);
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index 73f3744341..86fe4020a7 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -707,6 +707,16 @@ ZEND_API int pass_two(zend_op_array *op_array)
opline++;
}
+ if (op_array->live_range) {
+ uint32_t i;
+
+ 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);
+ }
+ }
+
op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
return 0;
}
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index f8f0fd9c0f..093f9f851e 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -3999,8 +3999,6 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
if (UNEXPECTED(EG(exception) != NULL)) {
if (OP1_TYPE == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
- FREE_OP1();
}
}
#endif
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index c4aee3fa3e..a2e63a1a8d 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -7590,8 +7590,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CONST_
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_CONST == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
-
}
}
#endif
@@ -13443,8 +13441,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_TMP_VAR == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
- zval_ptr_dtor_nogc(free_op1);
}
}
#endif
@@ -19125,8 +19121,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_VAR == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
- zval_ptr_dtor_nogc(free_op1);
}
}
#endif
@@ -25227,8 +25221,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_UNUSED == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
-
}
}
#endif
@@ -34859,8 +34851,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
if (UNEXPECTED(EG(exception) != NULL)) {
if (IS_CV == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
- } else {
-
}
}
#endif
diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c
index 172f9a1a62..586471017c 100644
--- a/ext/opcache/Optimizer/optimize_temp_vars_5.c
+++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c
@@ -224,6 +224,14 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *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/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
index 70b240fdf1..9fb7493054 100644
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ b/ext/opcache/Optimizer/zend_optimizer.c
@@ -358,7 +358,7 @@ void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
do {
- if (op_array->opcodes[op_array->live_range[i].end].op1.var != var) {
+ if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
map[i] = j;
if (i != j) {
op_array->live_range[j] = op_array->live_range[i];