diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-07-07 15:41:12 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-07-07 15:41:12 +0300 |
commit | 5ee841325901a4b040cfea56292a24702fe224d9 (patch) | |
tree | 5b664b68cb7334554b021bda0a2b9637f8093650 | |
parent | 32677f568bf5281167efacb1bb942ef671ae6504 (diff) | |
download | php-git-5ee841325901a4b040cfea56292a24702fe224d9.tar.gz |
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)
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))
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | Zend/tests/jump15.phpt | 29 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_001.phpt | 23 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_002.phpt | 32 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_003.phpt | 19 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_004.phpt | 44 | ||||
-rw-r--r-- | Zend/tests/temporary_cleaning_005.phpt | 48 | ||||
-rw-r--r-- | Zend/zend_compile.c | 203 | ||||
-rw-r--r-- | Zend/zend_compile.h | 62 | ||||
-rw-r--r-- | Zend/zend_execute.c | 131 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 237 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 25 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 35 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.c | 2 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.h | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 83 | ||||
-rw-r--r-- | ext/opcache/Optimizer/nop_removal.c | 16 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass1_5.c | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 40 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer_internal.h | 3 | ||||
-rw-r--r-- | ext/opcache/zend_file_cache.c | 6 | ||||
-rw-r--r-- | ext/opcache/zend_persist.c | 9 | ||||
-rw-r--r-- | ext/opcache/zend_persist_calc.c | 8 | ||||
-rw-r--r-- | sapi/phpdbg/phpdbg_opcode.c | 40 |
24 files changed, 712 insertions, 390 deletions
@@ -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 index 0000000000..456d27785d --- /dev/null +++ b/Zend/tests/jump15.phpt @@ -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 index 0000000000..f2ccbb35b8 --- /dev/null +++ b/Zend/tests/temporary_cleaning_001.phpt @@ -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 index 0000000000..bea54e7f77 --- /dev/null +++ b/Zend/tests/temporary_cleaning_002.phpt @@ -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 index 0000000000..acff8c85f0 --- /dev/null +++ b/Zend/tests/temporary_cleaning_003.phpt @@ -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 index 0000000000..b8a02516b0 --- /dev/null +++ b/Zend/tests/temporary_cleaning_004.phpt @@ -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 index 0000000000..e8c7febe0b --- /dev/null +++ b/Zend/tests/temporary_cleaning_005.phpt @@ -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== diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f8efaf83c5..e655e8480c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a3bc17f425..993a19fb6b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -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 diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 3bd1917539..da150e29ff 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -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)); - } - } - } - } - } } /* }}} */ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a971a5e900..8f3ecddbda 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -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)); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7bfec961cb..ea2c51a29c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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 diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index eebfc0f1e7..8dfa511ed9 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 705ab9cd29..1f26f0439e 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -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", diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index f6de5b1b57..bb2d7717bd 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -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 diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index e1e07ea01b..dfe0e4baef 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -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) { diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index 20510b4163..9983ff4f60 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -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]; diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 6fcdc3e47a..766eb2c2d4 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -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: diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index dc69d2511e..86b0837137 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -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: diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index c2f97ff715..69288c7228 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -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; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 0781b91c9d..54e2ab639a 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -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); } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index bbcb20713b..0f789090cd 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -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); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index d78cc59259..9265ba5ce9 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -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; diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c index 3af52f17cd..5b93755bf9 100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@ -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] : "", |