]> granicus.if.org Git - php/commitdiff
Fixed bug #62210 (Exceptions can leak temporary variables. As a part of the fix serio...
authorDmitry Stogov <dmitry@zend.com>
Tue, 7 Jul 2015 12:41:12 +0000 (15:41 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 7 Jul 2015 12:41:12 +0000 (15:41 +0300)
Squashed commit of the following:

commit 38e22106d4bdcc829dd2b64be1d3c6cdc089f3ed
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 15:12:19 2015 +0300

    Added NEWS entry

commit 0a355935bfb10b5a4c893f4db9496ea8abbcf71b
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 15:06:32 2015 +0300

    Inline function, to eliminate repeatable checks

commit d937584f3aef0baae6001377b61fd700b6f36e14
Merge: 0341626 32677f5
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 14:35:49 2015 +0300

    Merge branch 'master' into temporary_cleaning

    * master:
      Fixed bug #70006 (cli - function with default arg = STDOUT crash output).
      Fix x86 build
      Fixed use after free on closure_call_leak_with_exception.phpt
      Fixed test

commit 0341626ea94a5e474c660732d33884460847d5e7
Merge: 74869fa dec35de
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 12:00:53 2015 +0300

    Merge branch 'temporary_cleaning' of https://github.com/laruence/php-src into temporary_cleaning

    * 'temporary_cleaning' of https://github.com/laruence/php-src:
      Fixed checkpoint get
      Fixed crash of invalid pointer derefer
      cleanup

commit 74869fa67375b8daf772ac30b6b936fd2a2132c6
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 11:45:34 2015 +0300

    Fixed wrong GOTO resolution

commit dec35ded3294e3022e88a623188c3d1d71381675
Author: Xinchen Hui <laruence@gmail.com>
Date:   Tue Jul 7 15:58:49 2015 +0800

    Fixed checkpoint get

commit b0f419540ad24c44810c9b05da046965618ffc65
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 10:47:11 2015 +0300

    Fixed crash of invalid pointer derefer (laruence)

commit 7a428d98ca2899c5933914caa0cd17b4126e952c
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 10:35:47 2015 +0300

    Fixed identation

commit 9c3a4dce9ce02034d19d28182aa9c1298d528daf
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 10:33:52 2015 +0300

    Fixed invalid size

commit 653abc670b2a1f453b0fc3fc4a9eca919ee870cc
Author: Xinchen Hui <laruence@gmail.com>
Date:   Tue Jul 7 11:29:14 2015 +0800

    Fixed crash of invalid pointer derefer

commit e04500ceda606ac4f364d03bcd562327bdc74eee
Author: Xinchen Hui <laruence@gmail.com>
Date:   Tue Jul 7 11:28:26 2015 +0800

    cleanup

commit 34183e1687681038e77b650078927b35ee84e933
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 04:49:46 2015 +0300

    op_array->T_liveliness compression

commit 2f6ad845795a08c3d7ac219e9c42950565b20394
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 04:44:44 2015 +0300

    White spaces

commit be83f115a3f82a548c8d377c66574de5e5187410
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 04:42:26 2015 +0300

    Identation

commit 1f5084b99038c374ac012b017c4d1652bb5d4222
Merge: 91b620d 1adf3df
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 04:41:54 2015 +0300

    Merge branch 'master' into temporary_cleaning

    * master:
      Throw TypeError for invalid callback
      Fix crash when exception occurs during nested rope
      Fix crash when exception is thrown during ROPE_END
      Small cleanup in ternary compilation
      move the define to the right place
      fix ext/ldap build
      Rectify information about invalid shift warning being now ArithmeticError

commit 91b620d684c5a2296774432d5d0ff8f5d14397d6
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 04:32:04 2015 +0300

    Replace GOTO by FREE/FE_FREE and JMP at compile time

commit 7052e5697918fab83d2975977c3392f7188fbc87
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue Jul 7 02:25:08 2015 +0300

    Use zend_regenerate_var_liveliness_info() to regenerate information after pass two.

commit ae72b0dc6797815a846b8f95abccb36367422d27
Merge: a81c4bb a919fe8
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 21:02:34 2015 +0300

    Merge branch 'master' into temporary_cleaning

    * master:
      Do not display EXT_TYPE_UNUSED in phpdbg opcodes
      Run debug build with opcache on travis

commit a81c4bb8c6f1ba8124a5a7636694480ff0f1328c
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 20:59:34 2015 +0300

    Improved algorithm. It's actually the same algorithm with second loop removed and simpler temporary data structures. The only difference may be in "re-defined" vatriable handling. Now live-range in that case started from the seconnd definition (this must be more safe).

commit 9a16810f7a7c10373603c5250d985616acf45e97
Merge: bbfbe47 001ecd3
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 17:57:45 2015 +0300

    Merge branch 'master' into temporary_cleaning

    * master:
      Simplify TMP var number decoding (without HashTable)

commit bbfbe470c865cb8b3cae8bf6518e6d06af525522
Merge: 0bda4ab 436b01e
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 17:02:01 2015 +0300

    Merge branch 'master' into temporary_cleaning

    * master:
      Avoid dangerous optimization
      Fixed JMPZNZ instruction printing
      Attempt at falling back on ldap_find_control for Mac OS

commit 0bda4abea7ba0a51c2ec125edb547645d0329792
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 16:05:33 2015 +0300

    Fixed live-range construction for OP_DATA opcode
    Added comments about algorithm assumtions

commit 521ad9df98fdf1dd8b7c212799ddeb1a84483e6f
Merge: 4398dab a09dcb0
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Mon Jul 6 14:54:15 2015 +0200

    Merge branch 'master' of https://github.com/php/php-src into temporary_cleaning

commit 4398dab82f9a5ce64df55b24988ce7d31e24074f
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Mon Jul 6 13:51:27 2015 +0200

    Add a few phpt tests related to temporary cleaning

commit 739656f83ff5b570bb311d2c7cb2d72380a3c759
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 14:28:49 2015 +0300

    Fixed Zend/tests/foreach_004.phpt failure (FE_FETCH shouldn't be included into TMP vatriablr live range)

commit 3df462a2bcf5fa8f9244ea299178152a5d436277
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 13:41:02 2015 +0300

    Improve data layout (reduce the op_array structure size on 64-bit systems)

commit 883b73c56e6dab6489ae4cda2c1331b53a995586
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon Jul 6 13:28:45 2015 +0300

    Removed op_array->brk_cont_array

commit ae5e58b59843513505e84e396c1446ac35cb1b94
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Mon Jul 6 04:22:58 2015 +0200

    Fix bug with brk_cont variable free / free loop vars via temporary liveliness info

commit b4223ca62771e1003c9ab778a09a177ad71e6d57
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Mon Jul 6 04:07:07 2015 +0200

    Fix bugs / cleanup fixes

commit ea33189d220c7fc0884848571635abe3cddd2f4d
Author: Xinchen Hui <laruence@gmail.com>
Date:   Sun Jul 5 20:58:38 2015 +0800

    Removed useless TsTop

commit 1dbb007e4addba9ac3bfc227db27a651cbcf5ede
Merge: 550bbf8 3a8af24
Author: Xinchen Hui <laruence@gmail.com>
Date:   Sat Jul 4 15:06:44 2015 +0800

    Merge branch 'temporary_cleaning' of https://github.com/dstogov/php-src into temporary_cleaning

commit 3a8af245290ceb507108340831254672f24022fa
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 3 16:15:36 2015 +0300

    More exceptions from regular liveliness analyses (with explanation in comments).
    Mark old "unexplained" exceptions with ???.

commit ba721efa2cbd2136668fec956ef3b034ac1a29d6
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 3 14:16:09 2015 +0300

    Print list of live temp variables (at least for internal debugging)

commit 8d1f88fe91e62b4333703c58b871d85b66fb7b70
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 3 13:31:56 2015 +0300

    Use op_array->T_liveliness to free incomplete ropes and restore error_reporting level on exception

commit 80c1d0d779e6e9609a211907838f3727aa7b301a
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jul 3 11:05:39 2015 +0300

    Don't keep empty T_liveliness

commit 501ae8aaac0a92368b50e9f342b04d7334d263f6
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jul 2 22:31:48 2015 +0300

    Reverted changes to Zend/zend_arena.h.
    Reuse CG(arena) instead of creating a new one.

commit a4fce36907147df5ac1af78b44135e3f07c1844c
Merge: 6ff7246 fd0fcce
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jul 2 22:01:42 2015 +0300

    Merge branch 'temporary_cleaning' of github.com:bwoebi/php-src into temporary_cleaning

    * 'temporary_cleaning' of github.com:bwoebi/php-src:
      Fix remaining issues with compacted temporaries
      Fix regression from last commit (+1 ?!)
      Fix off-by-one (opcache may remove last ZEND_RETURN)
      Speed algorithm up, more fail safety when reusing temporaries
      Dumb bug in opcode.c (forgot to update Ts[i])
      Fix opcache support
      Exempt ROPE temporaries from freeing
      Hmm, we need temporary info for all the opcodes
      Add opcache support for cleaning in optimization step (Opcache seems to have a few unrelated issues which blow up together with that patch)
      Add proper temporary cleaning upon frame abortion
      Fix arena on small sizes (size < sizeof(zend_arena))

commit fd0fcce81177717f3a05ac87192b5ed05eead0a1
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Thu Jul 2 20:00:33 2015 +0200

    Fix remaining issues with compacted temporaries

commit 427dc58bbb93022d1c2077f874afcdb9dd82d5c5
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Wed Jul 1 22:49:12 2015 +0200

    Fix regression from last commit (+1 ?!)

commit 1adcf56a6e9f09e7ad06331d4d6280035b17a7d1
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Wed Jul 1 22:17:07 2015 +0200

    Fix off-by-one (opcache may remove last ZEND_RETURN)

commit 25b231b7841fa4078c65976cabdd843845a6cbe6
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Wed Jul 1 20:59:24 2015 +0200

    Speed algorithm up, more fail safety when reusing temporaries

commit 22d9d05350e35d180018d0bccbad6f173cb4797d
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Wed Jul 1 16:48:46 2015 +0200

    Dumb bug in opcode.c (forgot to update Ts[i])

commit 6538269bfa5bcbad34fc2f051b0fd5e4ebf2ff00
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Wed Jul 1 13:05:52 2015 +0200

    Fix opcache support

commit 333a7c4a8813a45dc79ce55b8e9c0a0b98671e13
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Sat Jun 27 22:40:21 2015 +0200

    Exempt ROPE temporaries from freeing

commit 02585f77085427baea48448c134a96c542af3337
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Fri Jun 26 16:20:55 2015 +0200

    Hmm, we need temporary info for all the opcodes

commit cbcaedbd78199897e5cacffd700b706f21590abf
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Fri Jun 26 01:04:09 2015 +0200

    Add opcache support for cleaning in optimization step
    (Opcache seems to have a few unrelated issues which blow up together with that patch)

commit fef649f4067823a1f96f85340cf715e5877310bc
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Fri Jun 26 01:02:27 2015 +0200

    Add proper temporary cleaning upon frame abortion

commit 1cec2e7271b789b84601f8acf385950af1bb0c7c
Author: Bob Weinand <bobwei9@hotmail.com>
Date:   Thu Jun 25 23:33:21 2015 +0200

    Fix arena on small sizes (size < sizeof(zend_arena))

24 files changed:
NEWS
Zend/tests/jump15.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_001.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_002.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_003.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_004.phpt [new file with mode: 0644]
Zend/tests/temporary_cleaning_005.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/block_pass.c
ext/opcache/Optimizer/nop_removal.c
ext/opcache/Optimizer/pass1_5.c
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_optimizer_internal.h
ext/opcache/zend_file_cache.c
ext/opcache/zend_persist.c
ext/opcache/zend_persist_calc.c
sapi/phpdbg/phpdbg_opcode.c

diff --git a/NEWS b/NEWS
index 5de290a5d2aace1ba01568d2d3398d57496c0292..6ef4d96f447fe3e648ec5178221adb5c5359009b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,11 @@ PHP                                                                        NEWS
     ArrayAccess object). (Laruence)
   . Fixed bug #69957 (Different ways of handling div/mod/intdiv). (Bob)
   . Fixed bug #69900 (Too long timeout on pipes). (Anatol)
+  . Fixed bug #62210 (Exceptions can leak temporary variables. As a part of
+    the fix serious refactoring was done. op_array->brk_cont_array was removed,
+    and replaced with more general and speed efficient op_array->T_liveliness.
+    ZEND_GOTO opcode is always replaced by ZEND_JMP at compile time).
+    (Bob, Dmitry, Laruence)
 
 - CLI server:
   . Fixed bug #69655 (php -S changes MKCALENDAR request method to MKCOL). (cmb)
diff --git a/Zend/tests/jump15.phpt b/Zend/tests/jump15.phpt
new file mode 100644 (file)
index 0000000..456d277
--- /dev/null
@@ -0,0 +1,29 @@
+--TEST--
+jump 15: goto from loop (forward)
+--FILE--
+<?php
+$ar = array("1","2","3");
+foreach ($ar as $val) {
+       switch ($val) {
+               case "1":
+                       echo "1: ok\n";
+                       break;
+               case "2":
+                       echo "2: ok\n";
+                       goto L1;
+               case "3":
+                       echo "bug\n";
+                       break;
+       }
+}
+echo "bug\n";
+L1:
+try {
+       echo "3: ok\n";
+} finally {
+}
+?>
+--EXPECT--
+1: ok
+2: ok
+3: ok
diff --git a/Zend/tests/temporary_cleaning_001.phpt b/Zend/tests/temporary_cleaning_001.phpt
new file mode 100644 (file)
index 0000000..f2ccbb3
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+Temporary leak on exception
+--FILE--
+<?php
+
+function ops() {
+    throw new Exception();
+}
+
+try {
+       $x = 2;
+       $y = new stdClass;
+       while ($x-- && new stdClass) {
+               $r = [$x] + ($y ? ((array) $x) + [2] : ops());
+               $y = (array) $y;
+       }
+} catch (Exception $e) {
+}
+
+?>
+==DONE==
+--EXPECT--
+==DONE==
diff --git a/Zend/tests/temporary_cleaning_002.phpt b/Zend/tests/temporary_cleaning_002.phpt
new file mode 100644 (file)
index 0000000..bea54e7
--- /dev/null
@@ -0,0 +1,32 @@
+--TEST--
+Temporary leak on rope (encapsed string)
+--FILE--
+<?php
+class Obj {
+       function __get($x) {
+               throw new Exception();
+       }
+}
+
+$x = new Obj;
+$y = 0;
+
+try {
+       $r = "$y|$x->x|";
+} catch (Exception $e) {
+}
+
+try {
+       $r = "$x->x|$y|";
+} catch (Exception $e) {
+}
+
+try {
+       $r = "$y|$y|$x->x";
+} catch (Exception $e) {
+}
+
+?>
+==DONE==
+--EXPECT--
+==DONE==
diff --git a/Zend/tests/temporary_cleaning_003.phpt b/Zend/tests/temporary_cleaning_003.phpt
new file mode 100644 (file)
index 0000000..acff8c8
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+Fundamental memory leak test on temporaries
+--FILE--
+<?php
+
+function ops() {
+    throw new Exception();
+}
+
+try{
+    $x = 1;
+    $r = [$x] + ops();
+} catch (Exception $e) {
+}
+
+?>
+==DONE==
+--EXPECT--
+==DONE==
diff --git a/Zend/tests/temporary_cleaning_004.phpt b/Zend/tests/temporary_cleaning_004.phpt
new file mode 100644 (file)
index 0000000..b8a0251
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+Temporary leak with switch
+--FILE--
+<?php
+
+function ops() {
+    throw new Exception();
+}
+
+$a = [new stdClass, new stdClass];
+switch ($a[0]) {
+       case false:
+       break;
+       default:
+               try {
+                       $x = 2;
+                       $y = new stdClass;
+                       while ($x-- && new stdClass) {
+                               $r = [$x] + ($y ? ((array) $x) + [2] : ops());
+                               $y = (array) $y;
+                       }
+               } catch (Exception $e) {
+               }
+}
+
+try {
+       switch ($a[0]) {
+               case false:
+               break;
+               default:
+                       $x = 2;
+                       $y = new stdClass;
+                       while ($x-- && new stdClass) {
+                               $r = [$x] + ($y ? ((array) $x) + [2] : ops());
+                               $y = (array) $y;
+                       }
+       }
+} catch (Exception $e) {
+}
+
+?>
+==DONE==
+--EXPECT--
+==DONE==
diff --git a/Zend/tests/temporary_cleaning_005.phpt b/Zend/tests/temporary_cleaning_005.phpt
new file mode 100644 (file)
index 0000000..e8c7feb
--- /dev/null
@@ -0,0 +1,48 @@
+--TEST--
+Temporary leak with foreach
+--FILE--
+<?php
+
+function ops() {
+    throw new Exception();
+}
+
+$a = [new stdClass, new stdClass];
+foreach ([$a, [new stdClass]] as $b) {
+       switch ($b[0]) {
+               case false:
+               break;
+               default:
+                       try {
+                               $x = 2;
+                               $y = new stdClass;
+                               while ($x-- && new stdClass) {
+                                       $r = [$x] + ($y ? ((array) $x) + [2] : ops());
+                                       $y = (array) $y;
+                               }
+                       } catch (Exception $e) {
+                       }
+       }
+}
+
+foreach ([$a, [new stdClass]] as $b) {
+       try {
+               switch ($b[0]) {
+                       case false:
+                       break;
+                       default:
+                               $x = 2;
+                               $y = new stdClass;
+                               while ($x-- && new stdClass) {
+                                       $r = [$x] + ($y ? ((array) $x) + [2] : ops());
+                                       $y = (array) $y;
+                               }
+               }
+       } catch (Exception $e) {
+       }
+}
+
+?>
+==DONE==
+--EXPECT--
+==DONE==
index f8efaf83c59c5e4dc154d5f02094fa9258be26d0..e655e8480c9a7ac31590382c1c4918049d0fe9db 100644 (file)
@@ -32,6 +32,7 @@
 #include "zend_multibyte.h"
 #include "zend_language_scanner.h"
 #include "zend_inheritance.h"
+#include "zend_vm.h"
 
 #define SET_NODE(target, src) do { \
                target ## _type = (src)->op_type; \
@@ -210,16 +211,21 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
        CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE;
        CG(context).vars_size = 0;
        CG(context).literals_size = 0;
-       CG(context).current_brk_cont = -1;
        CG(context).backpatch_count = 0;
        CG(context).in_finally = 0;
        CG(context).fast_call_var = -1;
+       CG(context).current_brk_cont = -1;
+       CG(context).last_brk_cont = 0;
+       CG(context).brk_cont_array = NULL;
        CG(context).labels = NULL;
 }
 /* }}} */
 
 void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */
 {
+       if (CG(context).brk_cont_array) {
+               efree(CG(context).brk_cont_array);
+       }
        if (CG(context).labels) {
                zend_hash_destroy(CG(context).labels);
                FREE_HASHTABLE(CG(context).labels);
@@ -562,7 +568,7 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
        zend_brk_cont_element *brk_cont_element;
        int parent = CG(context).current_brk_cont;
 
-       CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont;
+       CG(context).current_brk_cont = CG(context).last_brk_cont;
        brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
        brk_cont_element->parent = parent;
 
@@ -580,7 +586,7 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
 static inline void zend_end_loop(int cont_addr) /* {{{ */
 {
        zend_brk_cont_element *brk_cont_element
-               = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont];
+               = &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));
        CG(context).current_brk_cont = brk_cont_element->parent;
@@ -874,61 +880,6 @@ static void str_dtor(zval *zv)  /* {{{ */ {
 }
 /* }}} */
 
-void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */
-{
-       zend_label *dest;
-       int current, distance;
-       zval *label;
-
-       if (pass2) {
-               label = RT_CONSTANT(op_array, opline->op2);
-       } else {
-               label = CT_CONSTANT_EX(op_array, opline->op2.constant);
-       }
-       if (CG(context).labels == NULL ||
-           (dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
-
-               if (pass2) {
-                       CG(in_compilation) = 1;
-                       CG(active_op_array) = op_array;
-                       CG(zend_lineno) = opline->lineno;
-                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
-               } else {
-                       /* Label is not defined. Delay to pass 2. */
-                       return;
-               }
-       }
-
-       opline->op1.opline_num = dest->opline_num;
-       zval_dtor(label);
-       ZVAL_NULL(label);
-
-       /* Check that we are not moving into loop or switch */
-       current = opline->extended_value;
-       for (distance = 0; current != dest->brk_cont; distance++) {
-               if (current == -1) {
-                       if (pass2) {
-                               CG(in_compilation) = 1;
-                               CG(active_op_array) = op_array;
-                               CG(zend_lineno) = opline->lineno;
-                       }
-                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
-               }
-               current = op_array->brk_cont_array[current].parent;
-       }
-
-       if (distance == 0) {
-               /* Nothing to break out of, optimize to ZEND_JMP */
-               opline->opcode = ZEND_JMP;
-               opline->extended_value = 0;
-               SET_UNUSED(opline->op2);
-       } else {
-               /* Set real break distance */
-               ZVAL_LONG(label, distance);
-       }
-}
-/* }}} */
-
 static zend_bool zend_is_call(zend_ast *ast);
 
 static int generate_free_loop_var(znode *var) /* {{{ */
@@ -3615,12 +3566,12 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
                                        depth, depth == 1 ? "" : "s");
                        }
 
-                       if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) {
+                       if (nest_level > 1 && CG(context).brk_cont_array[array_offset].start >= 0) {
                                generate_free_loop_var(loop_var);
                                loop_var--;
                        }
 
-                       array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent;
+                       array_offset = CG(context).brk_cont_array[array_offset].parent;
                } while (--nest_level > 0);
        }
        opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
@@ -3629,16 +3580,125 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
+void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline) /* {{{ */
+{
+       zend_label *dest;
+       int current, distance, free_vars;
+       zval *label;
+       znode *loop_var = NULL;
+
+       if (pass2_opline) {
+               label = RT_CONSTANT(op_array, pass2_opline->op2);
+       } else {
+               label = &label_node->u.constant;
+       }
+       if (CG(context).labels == NULL ||
+           (dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
+
+               if (pass2_opline) {
+                       CG(in_compilation) = 1;
+                       CG(active_op_array) = op_array;
+                       CG(zend_lineno) = pass2_opline->lineno;
+                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
+               } else {
+                       /* Label is not defined. Delay to pass 2. */
+                       zend_op *opline;
+
+                       current = CG(context).current_brk_cont;
+                       while (current != -1) {
+                               if (CG(context).brk_cont_array[current].start >= 0) {
+                                       zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
+                               }
+                               current = CG(context).brk_cont_array[current].parent;
+                       }
+                       opline = zend_emit_op(NULL, ZEND_GOTO, NULL, label_node);
+                       opline->extended_value = CG(context).current_brk_cont;
+                       return;
+               }
+       }
+
+       zval_dtor(label);
+       ZVAL_NULL(label);
+
+       /* Check that we are not moving into loop or switch */
+       if (pass2_opline) {
+               current = pass2_opline->extended_value;
+       } else {
+               current = CG(context).current_brk_cont;
+       }
+       if (!pass2_opline) {
+               loop_var = zend_stack_top(&CG(loop_var_stack));
+       }
+       for (distance = 0, free_vars = 0; current != dest->brk_cont; distance++) {
+               if (current == -1) {
+                       if (pass2_opline) {
+                               CG(in_compilation) = 1;
+                               CG(active_op_array) = op_array;
+                               CG(zend_lineno) = pass2_opline->lineno;
+                       }
+                       zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
+               }
+               if (CG(context).brk_cont_array[current].start >= 0) {
+                       if (pass2_opline) {
+                               free_vars++;
+                       } else {
+                               generate_free_loop_var(loop_var);
+                               loop_var--;
+                       }
+               }
+               current = CG(context).brk_cont_array[current].parent;
+       }
+
+       if (pass2_opline) {
+               if (free_vars) {
+                       current = pass2_opline->extended_value;
+                       while (current != dest->brk_cont) {
+                               if (CG(context).brk_cont_array[current].start >= 0) {
+                                       zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk];
+
+                                       if (brk_opline->opcode == ZEND_FREE) {
+                                               (pass2_opline - free_vars)->opcode = ZEND_FREE;
+                                               (pass2_opline - free_vars)->op1_type = brk_opline->op1_type;
+                                               if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+                                                       (pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
+                                               } else {
+                                                       (pass2_opline - free_vars)->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + brk_opline->op1.var);
+                                                       ZEND_VM_SET_OPCODE_HANDLER(pass2_opline - free_vars);
+                                               }
+                                               free_vars--;
+                                       } else if (brk_opline->opcode == ZEND_FE_FREE) {
+                                               (pass2_opline - free_vars)->opcode = ZEND_FE_FREE;
+                                               (pass2_opline - free_vars)->op1_type = brk_opline->op1_type;
+                                               if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+                                                       (pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
+                                               } else {
+                                                       (pass2_opline - free_vars)->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + brk_opline->op1.var);
+                                                       ZEND_VM_SET_OPCODE_HANDLER(pass2_opline - free_vars);
+                                               }
+                                               free_vars--;
+                                       }
+                               }
+                               current = CG(context).brk_cont_array[current].parent;
+                       }
+               }
+               pass2_opline->opcode = ZEND_JMP;
+               pass2_opline->op1.opline_num = dest->opline_num;
+               SET_UNUSED(pass2_opline->op2);
+               pass2_opline->extended_value = 0;
+       } else {
+               zend_op *opline = zend_emit_op(NULL, ZEND_JMP, NULL, NULL);
+               opline->op1.opline_num = dest->opline_num;
+       }
+}
+/* }}} */
+
 void zend_compile_goto(zend_ast *ast) /* {{{ */
 {
        zend_ast *label_ast = ast->child[0];
        znode label_node;
-       zend_op *opline;
 
        zend_compile_expr(&label_node, label_ast);
-       opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node);
-       opline->extended_value = CG(context).current_brk_cont;
-       zend_resolve_goto_label(CG(active_op_array), opline, 0);
+       zend_resolve_goto_label(CG(active_op_array), &label_node, NULL);
 }
 /* }}} */
 
@@ -6278,10 +6338,7 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
 {
        zend_ast *expr_ast = ast->child[0];
        znode silence_node;
-       uint32_t begin_opline_num, end_opline_num;
-       zend_brk_cont_element *brk_cont_element;
 
-       begin_opline_num = 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) {
@@ -6292,15 +6349,7 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
                zend_compile_expr(result, expr_ast);
        }
 
-       end_opline_num = get_next_op_number(CG(active_op_array));
        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 */
-       brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
-       brk_cont_element->start = begin_opline_num;
-       brk_cont_element->cont = brk_cont_element->brk = end_opline_num;
-       brk_cont_element->parent = -1;
 }
 /* }}} */
 
@@ -6628,10 +6677,6 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
                GET_NODE(result, opline->result);
        } else {
                uint32_t var;
-               zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array));
-               info->start = rope_init_lineno;
-               info->parent = CG(context).current_brk_cont;
-               info->cont = info->brk = opline - CG(active_op_array)->opcodes;
 
                init_opline->extended_value = j;
                opline->opcode = ZEND_ROPE_END;
index a3bc17f4256dd828e58ef9941683c11213e090be..993a19fb6b47e2d2fb866abe71b8e4e3b5fe135e 100644 (file)
@@ -109,15 +109,36 @@ typedef struct _zend_declarables {
        zend_long ticks;
 } zend_declarables;
 
+typedef struct _zend_brk_cont_element {
+       int start;
+       int cont;
+       int brk;
+       int parent;
+} zend_brk_cont_element;
+
+typedef struct _zend_label {
+       int brk_cont;
+       uint32_t opline_num;
+} zend_label;
+
+typedef struct _zend_try_catch_element {
+       uint32_t try_op;
+       uint32_t catch_op;  /* ketchup! */
+       uint32_t finally_op;
+       uint32_t finally_end;
+} zend_try_catch_element;
+
 /* Compilation context that is different for each op array. */
 typedef struct _zend_oparray_context {
        uint32_t   opcodes_size;
        int        vars_size;
        int        literals_size;
-       int        current_brk_cont;
        int        backpatch_count;
        int        in_finally;
        uint32_t   fast_call_var;
+       int        current_brk_cont;
+       int        last_brk_cont;
+       zend_brk_cont_element *brk_cont_array;
        HashTable *labels;
 } zend_oparray_context;
 
@@ -163,26 +184,6 @@ struct _zend_op {
        zend_uchar result_type;
 };
 
-
-typedef struct _zend_brk_cont_element {
-       int start;
-       int cont;
-       int brk;
-       int parent;
-} zend_brk_cont_element;
-
-typedef struct _zend_label {
-       int brk_cont;
-       uint32_t opline_num;
-} zend_label;
-
-typedef struct _zend_try_catch_element {
-       uint32_t try_op;
-       uint32_t catch_op;  /* ketchup! */
-       uint32_t finally_op;
-       uint32_t finally_end;
-} zend_try_catch_element;
-
 /* method flags (types) */
 #define ZEND_ACC_STATIC                        0x01
 #define ZEND_ACC_ABSTRACT              0x02
@@ -328,6 +329,11 @@ typedef struct _zend_internal_function_info {
        zend_bool _is_variadic;
 } zend_internal_function_info;
 
+#define ZEND_LIVE_ROPE    1
+#define ZEND_LIVE_SILENCE 2
+#define ZEND_LIVE_LOOP    3
+#define ZEND_LIVE_MASK    3
+
 struct _zend_op_array {
        /* Common elements */
        zend_uchar type;
@@ -351,10 +357,12 @@ struct _zend_op_array {
        int last_var;
        uint32_t T;
        zend_string **vars;
+       uint32_t *T_liveliness;
+
+       void **run_time_cache;
+       int  cache_size;
 
-       int last_brk_cont;
        int last_try_catch;
-       zend_brk_cont_element *brk_cont_array;
        zend_try_catch_element *try_catch_array;
 
        /* static variables support */
@@ -369,9 +377,6 @@ struct _zend_op_array {
        int last_literal;
        zval *literals;
 
-       int  cache_size;
-       void **run_time_cache;
-
        void *reserved[ZEND_MAX_RESERVED_RESOURCES];
 };
 
@@ -712,7 +717,7 @@ void zend_do_extended_fcall_end(void);
 
 void zend_verify_namespace(void);
 
-void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2);
+void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline);
 
 ZEND_API void function_add_ref(zend_function *function);
 
@@ -727,6 +732,8 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...);
 ZEND_API int open_file_for_scanning(zend_file_handle *file_handle);
 ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size);
 ZEND_API void destroy_op_array(zend_op_array *op_array);
+ZEND_API void zend_generate_var_liveliness_info(zend_op_array *op_array);
+ZEND_API void zend_regenerate_var_liveliness_info(zend_op_array *op_array);
 ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle);
 ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce);
 ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce);
@@ -951,6 +958,7 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
 #define ZEND_ARRAY_SIZE_SHIFT          2
 
 /* Pseudo-opcodes that are used only temporarily during compilation */
+#define ZEND_GOTO 253
 #define ZEND_BRK 254
 #define ZEND_CONT 255
 
index 3bd1917539fa89e6b9fd61c33c700de57067c58e..da150e29ffbf2a64a7630274775be565ba0cb282 100644 (file)
@@ -1939,31 +1939,6 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
        }
 }
 
-static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
-{
-       zend_brk_cont_element *jmp_to;
-
-       do {
-               ZEND_ASSERT(array_offset != -1);
-               jmp_to = &op_array->brk_cont_array[array_offset];
-               if (nest_levels > 1 && jmp_to->start >= 0) {
-                       zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
-
-                       if (brk_opline->opcode == ZEND_FREE) {
-                               zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
-                       } else if (brk_opline->opcode == ZEND_FE_FREE) {
-                               zval *var = EX_VAR(brk_opline->op1.var);
-                               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);
-                       }
-               }
-               array_offset = jmp_to->parent;
-       } while (--nest_levels > 0);
-       return jmp_to;
-}
-
 #if ZEND_INTENSIVE_DEBUGGING
 
 #define CHECK_SYMBOL_TABLES()                                                                                                  \
@@ -2384,7 +2359,66 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut
 
 static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */
 {
-       int i;
+       if (EX(func)->op_array.T_liveliness
+        && op_num < EX(func)->op_array.last
+        && EX(func)->op_array.T_liveliness[op_num] != (uint32_t)-1) {
+               uint32_t *off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num];
+               uint32_t *catch_off = NULL;
+               uint32_t var = *off;
+
+               if (catch_op_num && EX(func)->op_array.T_liveliness[catch_op_num] != (uint32_t)-1) {
+                       catch_off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[catch_op_num];
+               }
+
+               do {
+                       /* we should be safe to assume that all temporaries at catch_op_num will be present at op_num too, in same order */
+                       if (catch_off && *catch_off == var) {
+                               catch_off++;
+                               var = *(++off);
+                               continue;
+                       }
+
+                       if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_ROPE) {
+                               /* free incomplete rope */
+                               zend_string **rope;
+                               zend_op *last;
+
+                               var = var & ~ZEND_LIVE_ROPE;
+                               rope = (zend_string **) EX_VAR(var);
+                               last = EX(func)->op_array.opcodes + op_num;
+                               while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
+                                               || last->result.var != var) {
+                                       ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
+                                       last--;
+                               }
+                               if (last->opcode == ZEND_ROPE_INIT) {
+                                       zend_string_release(*rope);
+                               } else {
+                                       int j = last->extended_value;
+                                       do {
+                                               zend_string_release(rope[j]);
+                                       } while (j--);
+                               }
+                       } else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_SILENCE) {
+                               /* restore previous error_reporting value */
+                               var = var & ~ZEND_LIVE_SILENCE;
+                               if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(var)) != 0) {
+                                       EG(error_reporting) = Z_LVAL_P(EX_VAR(var));
+                               }
+                       } else if ((var & ZEND_LIVE_MASK) == ZEND_LIVE_LOOP) {
+                               /* free loop variables */
+                               var = var & ~ZEND_LIVE_LOOP;
+                               if (Z_TYPE_P(EX_VAR(var)) != IS_ARRAY && Z_FE_ITER_P(EX_VAR(var)) != (uint32_t) -1) {
+                                       zend_hash_iterator_del(Z_FE_ITER_P(EX_VAR(var)));
+                               }
+                               zval_ptr_dtor_nogc(EX_VAR(var));
+                       } else {
+                               zval_ptr_dtor_nogc(EX_VAR(var));
+                       }
+                       var = *(++off);
+               } while (var != (uint32_t)-1);
+       }
+
        if (UNEXPECTED(EX(call))) {
                zend_execute_data *call = EX(call);
                zend_op *opline = EX(func)->op_array.opcodes + op_num;
@@ -2498,51 +2532,6 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data
                        call = EX(call);
                } while (call);
        }
-
-       for (i = 0; i < EX(func)->op_array.last_brk_cont; i++) {
-               const zend_brk_cont_element *brk_cont = &EX(func)->op_array.brk_cont_array[i];
-               if (brk_cont->start < 0) {
-                       continue;
-               } else if (brk_cont->start > op_num) {
-                       /* further blocks will not be relevant... */
-                       break;
-               } else if (op_num < brk_cont->brk) {
-                       if (!catch_op_num || catch_op_num >= brk_cont->brk) {
-                               zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk];
-
-                               if (brk_opline->opcode == ZEND_FREE) {
-                                       zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
-                               } else if (brk_opline->opcode == ZEND_FE_FREE) {
-                                       zval *var = EX_VAR(brk_opline->op1.var);
-                                       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 (brk_opline->opcode == ZEND_ROPE_END) {
-                                       zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var);
-                                       zend_op *last = EX(func)->op_array.opcodes + op_num;
-                                       while ((last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT)
-                                                       || last->result.var != brk_opline->op1.var) {
-                                               ZEND_ASSERT(last >= EX(func)->op_array.opcodes);
-                                               last--;
-                                       }
-                                       if (last->opcode == ZEND_ROPE_INIT) {
-                                               zend_string_release(*rope);
-                                       } else {
-                                               int j = last->extended_value;
-                                               do {
-                                                       zend_string_release(rope[j]);
-                                               } while (j--);
-                                       }
-                               } else if (brk_opline->opcode == ZEND_END_SILENCE) {
-                                       /* restore previous error_reporting value */
-                                       if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
-                                               EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var));
-                                       }
-                               }
-                       }
-               }
-       }
 }
 /* }}} */
 
index a971a5e90047da8fff99973aff571e4cb6f78c5a..8f3ecddbda6d3a32a664b5f8c3588da9130dc7eb 100644 (file)
@@ -65,6 +65,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
        op_array->vars = NULL;
 
        op_array->T = 0;
+       op_array->T_liveliness = NULL;
 
        op_array->function_name = NULL;
        op_array->filename = zend_get_compiled_filename();
@@ -77,9 +78,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
        op_array->scope = NULL;
        op_array->prototype = NULL;
 
-       op_array->brk_cont_array = NULL;
        op_array->try_catch_array = NULL;
-       op_array->last_brk_cont = 0;
 
        op_array->static_variables = NULL;
        op_array->last_try_catch = 0;
@@ -383,12 +382,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
        if (op_array->doc_comment) {
                zend_string_release(op_array->doc_comment);
        }
-       if (op_array->brk_cont_array) {
-               efree(op_array->brk_cont_array);
-       }
        if (op_array->try_catch_array) {
                efree(op_array->try_catch_array);
        }
+       if (op_array->T_liveliness) {
+               efree(op_array->T_liveliness);
+       }
        if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
                zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array);
        }
@@ -447,9 +446,9 @@ int get_next_op_number(zend_op_array *op_array)
 
 zend_brk_cont_element *get_next_brk_cont_element(zend_op_array *op_array)
 {
-       op_array->last_brk_cont++;
-       op_array->brk_cont_array = erealloc(op_array->brk_cont_array, sizeof(zend_brk_cont_element)*op_array->last_brk_cont);
-       return &op_array->brk_cont_array[op_array->last_brk_cont-1];
+       CG(context).last_brk_cont++;
+       CG(context).brk_cont_array = erealloc(CG(context).brk_cont_array, sizeof(zend_brk_cont_element)*CG(context).last_brk_cont);
+       return &CG(context).brk_cont_array[CG(context).last_brk_cont-1];
 }
 
 static void zend_update_extended_info(zend_op_array *op_array)
@@ -576,7 +575,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num,
                        fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var;
 
                        /* generate a FAST_CALL to finally block */
-                   start_op = get_next_op_number(op_array);
+                       start_op = get_next_op_number(op_array);
 
                        opline = get_next_op(op_array);
                        opline->opcode = ZEND_FAST_CALL;
@@ -672,7 +671,7 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze
        int array_offset = opline->op1.num;
        zend_brk_cont_element *jmp_to;
        do {
-               jmp_to = &op_array->brk_cont_array[array_offset];
+               jmp_to = &CG(context).brk_cont_array[array_offset];
                if (nest_levels > 1) {
                        array_offset = jmp_to->parent;
                }
@@ -700,11 +699,8 @@ static void zend_resolve_finally_calls(zend_op_array *op_array)
                                break;
                        case ZEND_GOTO:
                                if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) != IS_LONG) {
-                                       uint32_t num = opline->op2.constant;
-
                                        ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
-                                       zend_resolve_goto_label(op_array, opline, 1);
-                                       opline->op2.constant = num;
+                                       zend_resolve_goto_label(op_array, NULL, opline);
                                }
                                /* break omitted intentionally */
                        case ZEND_JMP:
@@ -751,6 +747,9 @@ ZEND_API int pass_two(zend_op_array *op_array)
                op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
                CG(context).literals_size = op_array->last_literal;
        }
+
+       zend_generate_var_liveliness_info(op_array);
+
        opline = op_array->opcodes;
        end = opline + op_array->last;
        while (opline < end) {
@@ -787,7 +786,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
                                break;
                        case ZEND_GOTO:
                                if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op2)) != IS_LONG) {
-                                       zend_resolve_goto_label(op_array, opline, 1);
+                                       zend_resolve_goto_label(op_array, NULL, opline);
                                }
                                /* break omitted intentionally */
                        case ZEND_JMP:
@@ -840,6 +839,214 @@ int pass_two_wrapper(zval *el)
        return pass_two((zend_op_array *) Z_PTR_P(el));
 }
 
+/* The following liveliness analyzing algorithm assumes that
+ * 1) temporary variables are defined before use
+ * 2) they have linear live-ranges without "holes"
+ * 3) Opcodes never use and define the same temorary variables
+ */
+typedef struct _op_var_info {
+       struct _op_var_info *next;
+       uint32_t var;
+} op_var_info;
+
+static zend_always_inline uint32_t liveliness_kill_var(zend_op_array *op_array, zend_op *cur_op, uint32_t var, uint32_t *Tstart, op_var_info **opTs)
+{
+       uint32_t start = Tstart[var];
+       uint32_t end = cur_op - op_array->opcodes;
+       uint32_t count = 0;
+       uint32_t var_offset, j;
+
+       Tstart[var] = -1;
+       if (cur_op->opcode == ZEND_OP_DATA) {
+               end--;
+       }
+       start++;
+       if (op_array->opcodes[start].opcode == ZEND_OP_DATA
+        || op_array->opcodes[start].opcode == ZEND_FE_FETCH_R
+        || op_array->opcodes[start].opcode == ZEND_FE_FETCH_RW) {
+               start++;
+       }
+       if (start < end) {
+               op_var_info *new_opTs;
+
+               var_offset = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + var);
+               if (op_array->opcodes[end].opcode == ZEND_ROPE_END) {
+                       var_offset |= ZEND_LIVE_ROPE;
+               } else if (op_array->opcodes[end].opcode == ZEND_END_SILENCE) {
+                       var_offset |= ZEND_LIVE_SILENCE;
+               } else if (op_array->opcodes[end].opcode == ZEND_FE_FREE) {
+                       var_offset |= ZEND_LIVE_LOOP;
+               }
+
+               if (opTs[start]) {
+                       if (start > 0 && opTs[start-1] == opTs[start]) {
+                               op_var_info *opT = opTs[start];
+                               do {
+                                       count++;
+                                       opT = opT->next;
+                               } while (opT);
+                               count += 2;
+                       } else {
+                               count++;
+                       }
+               } else {
+                       count += 2;
+               }
+
+               new_opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
+               new_opTs->next = opTs[start];
+               new_opTs->var = var_offset;
+               opTs[start] = new_opTs;
+
+               for (j = start + 1; j < end; j++) {
+                       if (opTs[j-1]->next == opTs[j]) {
+                               opTs[j] = opTs[j-1];
+                       } else {
+                           if (opTs[j]) {
+                                       count++;
+                           } else {
+                                       count += 2;
+                               }
+                               new_opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info));
+                               new_opTs->next = opTs[j];
+                               new_opTs->var = var_offset;
+                               opTs[j] = new_opTs;
+                       }
+               }
+       }
+
+       return count;
+}
+
+static zend_always_inline uint32_t *generate_var_liveliness_info_ex(zend_op_array *op_array, zend_bool done_pass_two)
+{
+       zend_op      *opline, *end;
+       uint32_t      var, i, op_live_total = 0;
+       uint32_t     *info, info_off = op_array->last + 1;
+       void         *checkpoint = zend_arena_checkpoint(CG(arena));
+       uint32_t     *Tstart = zend_arena_alloc(&CG(arena), sizeof(uint32_t) * op_array->T);
+       op_var_info **opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info *) * op_array->last);
+
+       memset(Tstart, -1, sizeof(uint32_t) * op_array->T);
+       memset(opTs, 0, sizeof(op_var_info *) * op_array->last);
+
+       opline = op_array->opcodes;
+       end = opline + op_array->last;
+       do {
+               if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
+                       && !((opline)->result_type & EXT_TYPE_UNUSED)
+                       /* the following opcodes are used in inline branching
+                        * (and anyway always bool, so no need to free) and may
+                        * not be defined depending on the taken branch */
+                       && opline->opcode != ZEND_BOOL
+                       && opline->opcode != ZEND_JMPZ_EX
+                       && opline->opcode != ZEND_JMPNZ_EX
+                       /* these two consecutive ops appear on ternary,
+                        * the result of true branch is undefined for false branch */
+                       && (opline->opcode != ZEND_QM_ASSIGN || (opline + 1)->opcode != ZEND_JMP)
+                       /* exception for opcache, it might nowhere use the temporary
+                        * (anyway bool, so no need to free) */
+                       && opline->opcode != ZEND_CASE
+                       /* the following opcodes reuse TMP created before */
+                       && opline->opcode != ZEND_ROPE_ADD
+                       && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
+                       /* passes fast_call */
+                       && opline->opcode != ZEND_FAST_CALL
+                       /* the following opcodes pass class_entry */
+                       && opline->opcode != ZEND_FETCH_CLASS
+                       && opline->opcode != ZEND_DECLARE_CLASS
+                       && opline->opcode != ZEND_DECLARE_INHERITED_CLASS
+                       && opline->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED
+                       && opline->opcode != ZEND_DECLARE_ANON_CLASS
+                       && opline->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS) {
+                       if (done_pass_two) {
+                               var = EX_VAR_TO_NUM(opline->result.var) - op_array->last_var;
+                       } else {
+                               var = opline->result.var;
+                       }
+                       /* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */
+                       if (opline->opcode == ZEND_NEW) {
+                               Tstart[var] = opline->op2.opline_num - 1;
+                       } else {
+                               Tstart[var] = opline - op_array->opcodes;
+                       }
+               }
+               if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
+                       if (done_pass_two) {
+                               var = EX_VAR_TO_NUM(opline->op1.var) - op_array->last_var;
+                       } else {
+                               var = opline->op1.var;
+                       }
+                       if (Tstart[var] != (uint32_t)-1
+                               /* the following opcodes don't free TMP */
+                               && opline->opcode != ZEND_ROPE_ADD
+                               && opline->opcode != ZEND_FETCH_LIST
+                               && opline->opcode != ZEND_CASE
+                               && opline->opcode != ZEND_FE_FETCH_R
+                               && opline->opcode != ZEND_FE_FETCH_RW) {
+                               op_live_total += liveliness_kill_var(op_array, opline, var, Tstart, opTs);
+                       }
+               }
+               if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
+                       if (done_pass_two) {
+                               var = EX_VAR_TO_NUM(opline->op2.var) - op_array->last_var;
+                       } else {
+                               var = opline->op2.var;
+                       }
+                       if (Tstart[var] != (uint32_t)-1) {
+                               op_live_total += liveliness_kill_var(op_array, opline, var, Tstart, opTs);
+                       }
+               }
+       } while (++opline != end);
+
+#if ZEND_DEBUG
+       /* Check that all TMP variable live-ranges are closed */
+       for (i = 0; i < op_array->T; i++) {
+               ZEND_ASSERT(Tstart[i] == (uint32_t)-1);
+       }
+#endif
+
+       if (!op_live_total) {
+               info = NULL;
+       } else {
+               info = emalloc((op_array->last + 1 + op_live_total) * sizeof(uint32_t));
+
+               for (i = 0; i < op_array->last; i++) {
+                       if (!opTs[i]) {
+                               info[i] = (uint32_t)-1;
+                       } else if (i > 0 && opTs[i-1] == opTs[i]) {
+                               info[i] = info[i-1];
+                       } else {
+                               op_var_info *opT = opTs[i];
+                               info[i] = info_off;
+                               while (opT) {
+                                       info[info_off++] = opT->var;
+                                       opT = opT->next;
+                               }
+                               info[info_off++] = (uint32_t)-1;
+                       }
+               }
+               info[op_array->last] = info_off;
+               ZEND_ASSERT(info_off == op_array->last + 1 + op_live_total);
+       }
+
+       zend_arena_release(&CG(arena), checkpoint);
+       return info;
+}
+
+ZEND_API void zend_generate_var_liveliness_info(zend_op_array *op_array)
+{
+       op_array->T_liveliness = generate_var_liveliness_info_ex(op_array, 0);
+}
+
+ZEND_API void zend_regenerate_var_liveliness_info(zend_op_array *op_array)
+{
+       if (op_array->T_liveliness) {
+               efree(op_array->T_liveliness);
+       }
+       op_array->T_liveliness = generate_var_liveliness_info_ex(op_array, 1);
+}
+
 int print_class(zend_class_entry *class_entry)
 {
        printf("Class %s:\n", ZSTR_VAL(class_entry->name));
index 7bfec961cb55399c83288f3ee8703bb59017e203..ea2c51a29c0b01972f092afa416611fae93060d5 100644 (file)
@@ -4866,31 +4866,6 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY)
        ZEND_VM_NEXT_OPCODE();
 }
 
-ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
-{
-       USE_OPLINE
-       zend_brk_cont_element *el;
-
-       SAVE_OPLINE();
-       el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
-                          &EX(func)->op_array, execute_data);
-
-       if (el->start >= 0) {
-               zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
-
-               if (brk_opline->opcode == ZEND_FREE) {
-                       zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
-               } else if (brk_opline->opcode == ZEND_FE_FREE) {
-                       zval *var = EX_VAR(brk_opline->op1.var);
-                       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);
-               }
-       }
-       ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
-}
-
 ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
 {
        USE_OPLINE
index eebfc0f1e736c3cc30caf5d88859f3737627b991..8dfa511ed900a177d2b653773449d53fc9182305 100644 (file)
@@ -2225,31 +2225,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
        ZEND_VM_NEXT_OPCODE();
 }
 
-static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
-       USE_OPLINE
-       zend_brk_cont_element *el;
-
-       SAVE_OPLINE();
-       el = zend_brk_cont(Z_LVAL_P(EX_CONSTANT(opline->op2)), opline->extended_value,
-                          &EX(func)->op_array, execute_data);
-
-       if (el->start >= 0) {
-               zend_op *brk_opline = EX(func)->op_array.opcodes + el->brk;
-
-               if (brk_opline->opcode == ZEND_FREE) {
-                       zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
-               } else if (brk_opline->opcode == ZEND_FE_FREE) {
-                       zval *var = EX_VAR(brk_opline->op1.var);
-                       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);
-               }
-       }
-       ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
-}
-
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -48422,27 +48397,27 @@ void zend_init_opcodes_handlers(void)
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_GOTO_SPEC_CONST_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
-       ZEND_GOTO_SPEC_CONST_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
+       ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
        ZEND_NULL_HANDLER,
index 705ab9cd29c8fc8527b0f7690321beae0e17e662..1f26f0439e42d2a90cb83919b9663b96acfc889b 100644 (file)
@@ -122,7 +122,7 @@ const char *zend_vm_opcodes_map[173] = {
        "ZEND_FETCH_OBJ_UNSET",
        "ZEND_FETCH_LIST",
        "ZEND_FETCH_CONSTANT",
-       "ZEND_GOTO",
+       NULL,
        "ZEND_EXT_STMT",
        "ZEND_EXT_FCALL_BEGIN",
        "ZEND_EXT_FCALL_END",
index f6de5b1b577ea293d0ff52d688e4d9fc6e018d1f..bb2d7717bdc179b1195bf5ec099713f290c679c0 100644 (file)
@@ -130,7 +130,6 @@ END_EXTERN_C()
 #define ZEND_FETCH_OBJ_UNSET                  97
 #define ZEND_FETCH_LIST                       98
 #define ZEND_FETCH_CONSTANT                   99
-#define ZEND_GOTO                            100
 #define ZEND_EXT_STMT                        101
 #define ZEND_EXT_FCALL_BEGIN                 102
 #define ZEND_EXT_FCALL_END                   103
index e1e07ea01b69a4d4908e4294caa643c048f738a6..dfe0e4baef9d5e6cef205be1849c7bf9750d7df5 100644 (file)
@@ -123,10 +123,6 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
        blocks[0].start_opline_no = 0;
        while (opline < end) {
                switch((unsigned)opline->opcode) {
-                       case ZEND_GOTO:
-                               /* would not optimize GOTOs - we cannot really know where it jumps,
-                                * so these optimizations are too dangerous */
-                               return 0;
                        case ZEND_FAST_CALL:
                                START_BLOCK_OP(ZEND_OP1(opline).opline_num);
                                if (opline->extended_value) {
@@ -197,65 +193,6 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
                        blocks[op_array->try_catch_array[i].try_op].protected = 1;
                }
        }
-       /* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes,
-        * but, we have to keep brk_cont_array to avoid memory leaks during
-        * exception handling */
-       if (op_array->last_brk_cont) {
-               int i, j;
-
-               j = 0;
-               for (i = 0; i< op_array->last_brk_cont; i++) {
-                       if (op_array->brk_cont_array[i].start >= 0 &&
-                           (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
-                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
-                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
-                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
-                               int parent = op_array->brk_cont_array[i].parent;
-
-                               while (parent >= 0 &&
-                                      op_array->brk_cont_array[parent].start < 0 &&
-                                      (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
-                                       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
-                                            op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END ||
-                                       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
-                                       parent = op_array->brk_cont_array[parent].parent;
-                               }
-                               op_array->brk_cont_array[i].parent = parent;
-                               j++;
-                       }
-               }
-               if (j) {
-                       cfg->loop_start = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
-                       cfg->loop_cont  = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
-                       cfg->loop_brk   = zend_arena_calloc(&ctx->arena, op_array->last_brk_cont, sizeof(zend_code_block *));
-                       j = 0;
-                       for (i = 0; i< op_array->last_brk_cont; i++) {
-                               if (op_array->brk_cont_array[i].start >= 0 &&
-                                   (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
-                                    op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
-                                    op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END ||
-                                    op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
-                                       if (i != j) {
-                                               op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
-                                       }
-                                       cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start];
-                                       cfg->loop_cont[j]  = &blocks[op_array->brk_cont_array[j].cont];
-                                       cfg->loop_brk[j]   = &blocks[op_array->brk_cont_array[j].brk];
-                                       START_BLOCK_OP(op_array->brk_cont_array[j].start);
-                                       START_BLOCK_OP(op_array->brk_cont_array[j].cont);
-                                       START_BLOCK_OP(op_array->brk_cont_array[j].brk);
-                                       blocks[op_array->brk_cont_array[j].start].protected = 1;
-                                       blocks[op_array->brk_cont_array[j].brk].protected = 1;
-                                       j++;
-                               }
-                       }
-                       op_array->last_brk_cont = j;
-               } else {
-                       efree(op_array->brk_cont_array);
-                       op_array->brk_cont_array = NULL;
-                       op_array->last_brk_cont = 0;
-               }
-       }
 
        /* Build CFG (Control Flow Graph) */
        cur_block = blocks;
@@ -523,16 +460,6 @@ static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int
        /* Walk thorough all paths */
        zend_access_path(start, ctx);
 
-       /* Add brk/cont paths */
-       if (op_array->last_brk_cont) {
-               int i;
-               for (i=0; i< op_array->last_brk_cont; i++) {
-                       zend_access_path(cfg->loop_start[i], ctx);
-                       zend_access_path(cfg->loop_cont[i], ctx);
-                       zend_access_path(cfg->loop_brk[i], ctx);
-               }
-       }
-
        /* Add exception paths */
        if (op_array->last_try_catch) {
                int i;
@@ -1196,16 +1123,6 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
                op_array->last_try_catch = j;
        }
 
-       /* adjust loop jump targets */
-       if (op_array->last_brk_cont) {
-               int i;
-               for (i = 0; i< op_array->last_brk_cont; i++) {
-                       op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes;
-                       op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes;
-                       op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes;
-               }
-       }
-
     /* adjust jump targets */
        for (cur_block = blocks; cur_block; cur_block = cur_block->next) {
                if (!cur_block->access) {
index 20510b416352da3a46479a1beac362348934a156..9983ff4f6016487ba079061a880ca63dec68d96b 100644 (file)
@@ -44,14 +44,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
        end = op_array->opcodes + op_array->last;
        for (opline = op_array->opcodes; opline < end; opline++) {
 
-               /* GOTO target is unresolved yet. We can't optimize. */
-               if (opline->opcode == ZEND_GOTO &&
-                       Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
-                       /* TODO: in general we can avoid this restriction */
-                       FREE_ALLOCA(shiftlist);
-                       return;
-               }
-
                /* Kill JMP-over-NOP-s */
                if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
                        /* check if there are only NOPs under the branch */
@@ -85,7 +77,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
                for (opline = op_array->opcodes; opline<end; opline++) {
                        switch (opline->opcode) {
                                case ZEND_JMP:
-                               case ZEND_GOTO:
                                case ZEND_FAST_CALL:
                                case ZEND_DECLARE_ANON_CLASS:
                                case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -117,13 +108,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
                        }
                }
 
-               /* update brk/cont array */
-               for (j = 0; j < op_array->last_brk_cont; j++) {
-                       op_array->brk_cont_array[j].brk -= shiftlist[op_array->brk_cont_array[j].brk];
-                       op_array->brk_cont_array[j].cont -= shiftlist[op_array->brk_cont_array[j].cont];
-                       op_array->brk_cont_array[j].start -= shiftlist[op_array->brk_cont_array[j].start];
-               }
-
                /* update try/catch array */
                for (j = 0; j < op_array->last_try_catch; j++) {
                        op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
index 6fcdc3e47a6aad21348bd06430d63d0df5467370..766eb2c2d4bc7f973d76a8ae2e89e3946627c6ec 100644 (file)
@@ -624,7 +624,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
                case ZEND_EXIT:
                case ZEND_THROW:
                case ZEND_CATCH:
-               case ZEND_GOTO:
                case ZEND_FAST_CALL:
                case ZEND_FAST_RET:
                case ZEND_JMP:
index dc69d2511ec2e4a0cd5667350721e7bec52cf06f..86b0837137146b5544e45fa8e684bbe1e0c9ff68 100644 (file)
@@ -340,29 +340,11 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
                                 */
                                case ZEND_FREE:
                                case ZEND_CASE: {
-                                       zend_op *m, *n;
-                                       int brk = op_array->last_brk_cont;
-                                       zend_bool in_switch = 0;
-                                       while (brk--) {
-                                               if (op_array->brk_cont_array[brk].start <= (opline - op_array->opcodes) &&
-                                                               op_array->brk_cont_array[brk].brk > (opline - op_array->opcodes)) {
-                                                       in_switch = 1;
-                                                       break;
-                                               }
-                                       }
+                                       zend_op *m = opline;
+                                       zend_op *end = op_array->opcodes + op_array->last;
 
-                                       if (!in_switch) {
-                                               ZEND_ASSERT(opline->opcode == ZEND_FREE);
-                                               MAKE_NOP(opline);
-                                               zval_dtor(val);
-                                               return 1;
-                                       }
-
-                                       m = opline;
-                                       n = op_array->opcodes + op_array->brk_cont_array[brk].brk + 1;
-                                       while (m < n) {
-                                               if (ZEND_OP1_TYPE(m) == type &&
-                                                               ZEND_OP1(m).var == var) {
+                                       while (m < end) {
+                                               if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) {
                                                        if (m->opcode == ZEND_CASE) {
                                                                zval old_val;
                                                                ZVAL_COPY_VALUE(&old_val, val);
@@ -371,6 +353,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
                                                                ZVAL_COPY_VALUE(val, &old_val);
                                                        } else if (m->opcode == ZEND_FREE) {
                                                                MAKE_NOP(m);
+                                                               break;
                                                        } else {
                                                                ZEND_ASSERT(0);
                                                        }
@@ -475,6 +458,17 @@ static void zend_optimize(zend_op_array      *op_array,
        if (ZEND_OPTIMIZER_PASS_11 & OPTIMIZATION_LEVEL) {
                zend_optimizer_compact_literals(op_array, ctx);
        }
+
+       if ((ZEND_OPTIMIZER_PASS_1
+               |ZEND_OPTIMIZER_PASS_2
+               |ZEND_OPTIMIZER_PASS_3
+               |ZEND_OPTIMIZER_PASS_4
+               |ZEND_OPTIMIZER_PASS_5
+               |ZEND_OPTIMIZER_PASS_9
+               |ZEND_OPTIMIZER_PASS_10
+               |ZEND_OPTIMIZER_PASS_11) & OPTIMIZATION_LEVEL) {
+               zend_regenerate_var_liveliness_info(op_array);
+       }
 }
 
 static void zend_accel_optimize(zend_op_array      *op_array,
@@ -494,7 +488,6 @@ static void zend_accel_optimize(zend_op_array      *op_array,
                }
                switch (opline->opcode) {
                        case ZEND_JMP:
-                       case ZEND_GOTO:
                        case ZEND_FAST_CALL:
                        case ZEND_DECLARE_ANON_CLASS:
                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -539,7 +532,6 @@ static void zend_accel_optimize(zend_op_array      *op_array,
                }
                switch (opline->opcode) {
                        case ZEND_JMP:
-                       case ZEND_GOTO:
                        case ZEND_FAST_CALL:
                        case ZEND_DECLARE_ANON_CLASS:
                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
index c2f97ff715a332c6ff30981a1ae684614342da4d..69288c72283bba46c0aa013ba247a6b561c9648a 100644 (file)
@@ -73,9 +73,6 @@ typedef struct _zend_cfg {
        zend_code_block    *blocks;
        zend_code_block   **try;
        zend_code_block   **catch;
-       zend_code_block   **loop_start;
-       zend_code_block   **loop_cont;
-       zend_code_block   **loop_brk;
        zend_op           **Tsource;
        char               *same_t;
 } zend_cfg;
index 0781b91c9d3d8d1cf9294d732e0c56bd65d1b9e8..54e2ab639ad1e5ba77a4d6fbbfbb4c7991544e02 100644 (file)
@@ -386,7 +386,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array            *op_arra
 # if ZEND_USE_ABS_JMP_ADDR
                        switch (opline->opcode) {
                                case ZEND_JMP:
-                               case ZEND_GOTO:
                                case ZEND_FAST_CALL:
                                case ZEND_DECLARE_ANON_CLASS:
                                case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -459,11 +458,11 @@ static void zend_file_cache_serialize_op_array(zend_op_array            *op_arra
 
                SERIALIZE_STR(op_array->function_name);
                SERIALIZE_STR(op_array->filename);
-               SERIALIZE_PTR(op_array->brk_cont_array);
                SERIALIZE_PTR(op_array->scope);
                SERIALIZE_STR(op_array->doc_comment);
                SERIALIZE_PTR(op_array->try_catch_array);
                SERIALIZE_PTR(op_array->prototype);
+               SERIALIZE_PTR(op_array->T_liveliness);
        }
 }
 
@@ -913,7 +912,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array           *op_arr
 # if ZEND_USE_ABS_JMP_ADDR
                        switch (opline->opcode) {
                                case ZEND_JMP:
-                               case ZEND_GOTO:
                                case ZEND_FAST_CALL:
                                case ZEND_DECLARE_ANON_CLASS:
                                case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -982,11 +980,11 @@ static void zend_file_cache_unserialize_op_array(zend_op_array           *op_arr
 
                UNSERIALIZE_STR(op_array->function_name);
                UNSERIALIZE_STR(op_array->filename);
-               UNSERIALIZE_PTR(op_array->brk_cont_array);
                UNSERIALIZE_PTR(op_array->scope);
                UNSERIALIZE_STR(op_array->doc_comment);
                UNSERIALIZE_PTR(op_array->try_catch_array);
                UNSERIALIZE_PTR(op_array->prototype);
+               UNSERIALIZE_PTR(op_array->T_liveliness);
        }
 }
 
index bbcb20713b0f18e1bf2eafcef23e724af2b900b5..0f789090cdc74272cc122deff3b077c3aa70c416 100644 (file)
@@ -501,7 +501,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                                /* fix jumps to point to new array */
                                switch (opline->opcode) {
                                        case ZEND_JMP:
-                                       case ZEND_GOTO:
                                        case ZEND_FAST_CALL:
                                        case ZEND_DECLARE_ANON_CLASS:
                                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
@@ -590,10 +589,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                op_array->arg_info = arg_info;
        }
 
-       if (op_array->brk_cont_array) {
-               zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
-       }
-
        if (op_array->scope) {
                op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
        }
@@ -618,6 +613,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
                zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
        }
 
+       if (op_array->T_liveliness) {
+               zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]);
+       }
+
        if (op_array->vars) {
                if (already_stored) {
                        persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars);
index d78cc592591f10cf5b5491011a1ca8fe3aa34e1d..9265ba5ce9c237a91938c9f0776d9a825612d6fe 100644 (file)
@@ -229,10 +229,6 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
                }
        }
 
-       if (op_array->brk_cont_array) {
-               ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
-       }
-
        if (ZCG(accel_directives).save_comments && op_array->doc_comment) {
                ADD_STRING(op_array->doc_comment);
        }
@@ -241,6 +237,10 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array)
                ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
        }
 
+       if (op_array->T_liveliness) {
+               ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]);
+       }
+
        if (op_array->vars) {
                int i;
 
index 3af52f17cdaccf94dad11f84db53c6bdab19bfbe..5b93755bf91363090b03ec51acb5894302f9d19c 100644 (file)
@@ -24,6 +24,7 @@
 #include "phpdbg_opcode.h"
 #include "phpdbg_utils.h"
 #include "ext/standard/php_string.h"
+#include "zend_smart_str.h"
 
 ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
 
@@ -56,7 +57,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
        /* OP1 */
        switch (op->opcode) {
        case ZEND_JMP:
-       case ZEND_GOTO:
        case ZEND_FAST_CALL:
                asprintf(&decode[1], "J%ld", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
                break;
@@ -114,6 +114,44 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
                break;
        }
 
+#if 1
+       if (ops->T_liveliness) {
+               uint32_t *var = ops->T_liveliness + (op - ops->opcodes);
+
+               if (*var != (uint32_t)-1) {
+                       smart_str str = {0};
+
+                       var = ops->T_liveliness + (*var);
+                       smart_str_appends(&str, "; [@");
+                       smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
+                       while (*(++var) != (uint32_t)-1) {
+                               smart_str_appends(&str, ", @");
+                               smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var);
+                       }
+                       smart_str_appendc(&str, ']');
+                       smart_str_0(&str);
+
+                       asprintf(&decode[0],
+                               "%-20s %-20s %-20s%-20s",
+                               decode[1] ? decode[1] : "",
+                               decode[2] ? decode[2] : "",
+                               decode[3] ? decode[3] : "",
+                               ZSTR_VAL(str.s));
+
+                       smart_str_free(&str);
+
+                       if (decode[1])
+                               free(decode[1]);
+                       if (decode[2])
+                               free(decode[2]);
+                       if (decode[3])
+                               free(decode[3]);
+
+                       return decode[0];
+               }
+       }
+#endif
+
        asprintf(&decode[0],
                "%-20s %-20s %-20s",
                decode[1] ? decode[1] : "",