]> granicus.if.org Git - php/commitdiff
Changed ZEND_FREE.op2.num and ZEND_FE_FREE.op2.num back to use live_range_offset...
authorDmitry Stogov <dmitry@zend.com>
Wed, 11 Nov 2015 08:12:44 +0000 (11:12 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 11 Nov 2015 08:12:44 +0000 (11:12 +0300)
12 files changed:
Zend/tests/temporary_cleaning_009.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_010.phpt [new file with mode: 0644]
Zend/tests/try/try_finally_021.phpt [new file with mode: 0644]
Zend/tests/try/try_finally_022.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_execute.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_gen.php
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/zend_optimizer.c

diff --git a/Zend/tests/temporary_cleaning_009.phpt b/Zend/tests/temporary_cleaning_009.phpt
new file mode 100644 (file)
index 0000000..32a84a6
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+Live range & free on return
+--FILE--
+<?php
+class bar {
+        public $foo = 1;
+        public $bar = 1;
+
+        function __destruct() {
+                throw $this->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 (file)
index 0000000..96eab12
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+Live range & throw from finally
+--XFAIL--
+See Bug #62210 and attempt to fix it in "tmp_livelibess" branch
+--FILE--
+<?php
+function test() {
+    try {
+        $a = [1, 2, 3];
+        return $a + [];
+    } finally {
+        throw new Exception;
+    }
+}
+
+try {
+    test();
+} catch (Exception $e) {
+       echo "exception\n";
+}
+?>
+--EXPECT--
+exception
diff --git a/Zend/tests/try/try_finally_021.phpt b/Zend/tests/try/try_finally_021.phpt
new file mode 100644 (file)
index 0000000..ed162f4
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+Live range & return from finally
+--FILE--
+<?php
+$array = [1];
+foreach ([0] as $_) {
+    foreach ($array as $v) {
+        try {
+               echo "ok\n";
+            return;
+        } finally {
+               echo "ok\n";
+            return;
+        }
+    }
+}
+?>
+--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 (file)
index 0000000..51f6a26
--- /dev/null
@@ -0,0 +1,41 @@
+--TEST--
+Try finally (exception in "return" statement)
+--FILE--
+<?php
+class A {
+       public $x = 1;
+       public $y = 2;
+       function __destruct() {
+               throw new Exception();
+       }
+}
+try{
+       $a = 0;
+       switch ($a) {
+               case 0:
+       }
+       switch ($a) {
+               case 0:
+       }
+       switch ($a) {
+               case 0:
+       }
+       foreach([new stdClass()] as $x) {
+               foreach(new A() as $a) {
+                       foreach([new stdClass()] as $y) {
+                               try {
+                                       if (0) { echo "0" . (int)5; }
+                                       return $a;
+                               } catch (Exception $e) {
+                                       echo "exception1\n";
+                               }
+                       }
+               }
+       }
+} catch (Exception $e) {
+       echo "exception2\n";
+}
+?>
+--EXPECT--
+exception2
+
index 4f4701189c7ba88a2b920d05af0a6fa9a5e4c459..3e2c58ddf20cbe921c9e31fc49f65132c1029699 100644 (file)
@@ -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) {
index 0fd99e9f736cd75a0704ba95f321d8ebee4475ec..c0c9271f4b879e82b0ebc3eafb1065f9ca3c03db 100644 (file)
@@ -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;
index 55411de0859f89d53e4bc0fe6f7b20f6032e3e2c..f8f0fd9c0fcc11261a725d8ff5bb0c743fcb5119 100644 (file)
@@ -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;
index 9f2b247813f2879a023721ef92c3c6328de88741..c4aee3fa3e9c1a7e92ed970bb2f426a4ee1a6c93 100644 (file)
@@ -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;
index fb6681a0c4e2c7acfef4ed30af56c46fb2050c21..17bcfdd8e10aad1b34b30a3a03b169cb92dcf862 100644 (file)
@@ -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(
index d53ce5ecd946325c97236c21e62e266bae1acae9..b56cd930ff5cadf4a439a59d063c2ddbc0d454b4 100644 (file)
@@ -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,
index 039f6d8d4ece577c4034e2476fa8f9781b71fc26..52b4d0b5aabce9b6b4f235d5c830fc1e3e2ad07d 100644 (file)
 #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
index 20b7a75f56646bc0d0565fc4e470e67594c6f7dd..eb772553093ceda51c15a14e0aad516ea2b09beb 100644 (file)
@@ -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++;
+                       }
+               }
        }
 }