Compare commits

...
Author SHA1 Message Date
Anatol Belski ad8a73dd55 Revert "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)"
This reverts commit 5ee8413259.
2015-07-10 12:06:56 +02:00
Anatol Belski ceefc0947a Revert "Tuned off dubugging of live ranges"
This reverts commit 404dc93d35.
2015-07-10 12:06:33 +02:00
Anatol Belski 2928e412e6 Revert "Remove loop_var_stack"
This reverts commit b3a4c05071.
2015-07-10 12:06:06 +02:00
Anatol Belski 770571bce9 Revert "ZEND_SEPARATE reuses temporaries"
This reverts commit dfe6fdaa72.
2015-07-10 12:05:43 +02:00
Anatol Belski 6cdf477254 Revert "Add assertion in liveliness computation"
This reverts commit 62062dcd0e.
2015-07-10 12:05:35 +02:00
Anatol Belski 5784b8f8e6 Revert "Fixed invalid live-range detection"
This reverts commit 724e98498a.
2015-07-10 12:05:26 +02:00
Anatol Belski ee573eba67 fixed NEWS 2015-07-09 17:34:49 +02:00
Dmitry Stogov 724e98498a Fixed invalid live-range detection 2015-07-09 17:26:01 +02:00
Nikita Popov 62062dcd0e Add assertion in liveliness computation
Assert that a live-range does not start, while one for the same
variable is already active.

To satisfy this constraint, explicitly exclude writes from JMP_SET
and COALESCE. They were previously also handled correctly, because
their result variable was only killed after the second write to it
after the false branch. This just makes it more explicit.
2015-07-09 17:25:42 +02:00
Nikita Popov dfe6fdaa72 ZEND_SEPARATE reuses temporaries 2015-07-09 17:25:26 +02:00
Anatol Belski 8d09c84bd3 update NEWS 2015-07-08 01:35:42 +02:00
Anatol Belski 955303ddc8 bump the versions 2015-07-08 01:20:10 +02:00
Anatol Belski 2a0e037c0d Merge branch 'master' into PHP-7.0.0
* master: (282 commits)
  Remove loop_var_stack
  SQLite3::open/__construct has 2 optional args
  Better fix for bug #69958
  Better fix for bug #69958
  Fix bug #69669 (mysqlnd is vulnerable to BACKRONYM)
  Fix bug #69923 - Buffer overflow and stack smashing error in phar_fix_filepath
  Fix bug #69958 - Segfault in Phar::convertToData on invalid file
  Use NULL where possible for exception class
  Allow NULL for exception_ce
  fix C89 compat
  Better fix for bug #69958
  update news
  Fix bug #69669 (mysqlnd is vulnerable to BACKRONYM)
  Fix bug #69923 - Buffer overflow and stack smashing error in phar_fix_filepath
  Fix bug #69958 - Segfault in Phar::convertToData on invalid file
  fix C89 compat
  added info about the user space stream context for pipes
  mention pcre.jit in the supplied php.ini-*
  Fix test related to c22da81
  add missing second argument for ucfirst to the proto
  ...
2015-07-08 01:17:19 +02:00
Anatol Belski 9876b2c2cf sync NEWS 2015-06-23 19:33:33 +02:00
Anatol Belski 5ea2b70645 bump version 2015-06-23 19:32:58 +02:00
Anatol Belski 29711264a8 Merge branch 'master' into PHP-7.0.0
* master: (239 commits)
  Moved NEWS entry for bug #69864 to proper category
  Moved NEWS entry for bug #69864 to proper category
  Moved NEWS entry for bug #69864 to proper category
  updated NEWS
  updated NEWS
  Fixed two errors in LDAP tests
  Fixed two errors in LDAP tests
  Revert "Simplify php_implode(). Fill the resulting string in direct order." The patch slightly improved performance of string imploding, but reduced speed of integers imploding.
  Encapsulate FastCGI implementation details. Previously fcgi_request defined in main/fastcgi.h might be treated differently in different files, because of different behavior of #ifdef TCP_NODELAY. This leaded to stack memory corruption and unpredictable crashes.
  Fixed bug #69905 (null ptr deref and segfault in ZEND_FETCH_DIM_RW_SPEC_VAR_UNUSED_HANDLER)
  updated NEWS
  Fixed bug #69864 (Segfault in preg_replace_callback)
  Simplify php_implode(). Fill the resulting string in direct order.
  bump API version to 6.8
  bump API version to 6.8
  bump API version to 6.8
  Drop duplicate object-to-type notices
  Added comments
  Drop superfluous __toString handling code
  This macro is defined in zend_virtual_cwd.h, avoid redef
  ...
2015-06-23 18:49:42 +02:00
Ferenc Kovacs dbf30365aa Merge remote-tracking branch 'origin/master' into PHP-7.0.0
* origin/master:
  add missing NEWS entries
  add missing NEWS entries
  Fixed bug #69646 (OS command injection vulnerability in escapeshellarg)
  add NEWS
  Fixed bug #68776
  fix test
  update NEWS
  fix typo
  Fix bug #69646	OS command injection vulnerability in escapeshellarg
  Fix #69719 - more checks for nulls in paths
  fix test description
  Fixed Buf #68812 Unchecked return value.
2015-06-10 10:49:51 +02:00
Ferenc Kovacs 9cb8cb4797 Merge branch 'master' into PHP-7.0.0
* master:
  fix hard-coded path in test
2015-06-09 16:42:24 +02:00
Ferenc Kovacs 8a884d628b 7.0.0alpha1 2015-06-09 14:24:39 +02:00
28 changed files with 445 additions and 711 deletions

No files matched your search

+4 -2
View File
@@ -100,6 +100,10 @@ PHP NEWS
- GD:
. Fixed bug #61221 (imagegammacorrect function loses alpha channel). (cmb)
- JSON
. Fixed bug #68546 (json_decode() Fatal error: Cannot access property
started with '\0'). (Jakub Zelenka)
- mysqlnd:
. Fixed Bug #69796 (mysqli_stmt::fetch doesn't assign null values to
bound variables). (Laruence)
@@ -269,8 +273,6 @@ PHP NEWS
(JSON extension includes a problematic license statement). (Jakub Zelenka)
. Fixed bug #68938 (json_decode() decodes empty string without error).
(jeremy at bat-country dot us)
. Fixed bug #68546 (json_decode() Fatal error: Cannot access property
started with '\0'). (Jakub Zelenka)
- LDAP
. Fixed bug #47222 (Implement LDAP_OPT_DIAGNOSTIC_MESSAGE). (Andreas Heigl)
-29
View File
@@ -1,29 +0,0 @@
--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
-23
View File
@@ -1,23 +0,0 @@
--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==
-32
View File
@@ -1,32 +0,0 @@
--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==
-19
View File
@@ -1,19 +0,0 @@
--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==
-44
View File
@@ -1,44 +0,0 @@
--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==
-48
View File
@@ -1,48 +0,0 @@
--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==
+3
View File
@@ -1045,6 +1045,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
zval orig_user_error_handler;
zend_bool in_compilation;
zend_class_entry *saved_class_entry;
zend_stack loop_var_stack;
zend_stack delayed_oplines_stack;
zend_array *symbol_table;
@@ -1211,6 +1212,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
if (in_compilation) {
saved_class_entry = CG(active_class_entry);
CG(active_class_entry) = NULL;
SAVE_STACK(loop_var_stack);
SAVE_STACK(delayed_oplines_stack);
CG(in_compilation) = 0;
}
@@ -1229,6 +1231,7 @@ static void zend_error_va_list(int type, const char *format, va_list args)
if (in_compilation) {
CG(active_class_entry) = saved_class_entry;
RESTORE_STACK(loop_var_stack);
RESTORE_STACK(delayed_oplines_stack);
CG(in_compilation) = 1;
}
+122 -123
View File
@@ -32,7 +32,6 @@
#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; \
@@ -211,21 +210,16 @@ 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);
@@ -289,6 +283,7 @@ void zend_file_context_end(zend_file_context *prev_context) /* {{{ */
void zend_init_compiler_data_structures(void) /* {{{ */
{
zend_stack_init(&CG(loop_var_stack), sizeof(znode));
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
CG(active_class_entry) = NULL;
CG(in_compilation) = 0;
@@ -321,6 +316,7 @@ void init_compiler(void) /* {{{ */
void shutdown_compiler(void) /* {{{ */
{
zend_stack_destroy(&CG(loop_var_stack));
zend_stack_destroy(&CG(delayed_oplines_stack));
zend_hash_destroy(&CG(filenames_table));
zend_hash_destroy(&CG(const_filenames));
@@ -566,13 +562,17 @@ 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(context).last_brk_cont;
CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont;
brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
brk_cont_element->parent = parent;
if (loop_var && (loop_var->op_type & (IS_TMP_VAR|IS_VAR))) {
brk_cont_element->loop_var = *loop_var;
if (loop_var) {
zend_stack_push(&CG(loop_var_stack), loop_var);
brk_cont_element->start = get_next_op_number(CG(active_op_array));
} else {
brk_cont_element->loop_var.op_type = IS_UNUSED;
/* The start field is used to free temporary variables in case of exceptions.
* We won't try to free something of we don't have loop variable. */
brk_cont_element->start = -1;
}
}
/* }}} */
@@ -580,10 +580,14 @@ 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(context).brk_cont_array[CG(context).current_brk_cont];
= &CG(active_op_array)->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;
if (brk_cont_element->start >= 0) {
zend_stack_del_top(&CG(loop_var_stack));
}
}
/* }}} */
@@ -870,17 +874,81 @@ 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 void generate_free_loop_var(znode *var) /* {{{ */
static int generate_free_loop_var(znode *var) /* {{{ */
{
if (var->op_type != IS_UNUSED) {
zend_op *opline = get_next_op(CG(active_op_array));
switch (var->op_type) {
case IS_UNUSED:
/* Stack separator on function boundary, stop applying */
return 1;
case IS_VAR:
case IS_TMP_VAR:
{
zend_op *opline = get_next_op(CG(active_op_array));
opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
SET_NODE(opline->op1, var);
SET_UNUSED(opline->op2);
opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
SET_NODE(opline->op1, var);
SET_UNUSED(opline->op2);
}
}
return 0;
}
/* }}} */
@@ -3443,12 +3511,7 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
static void zend_free_foreach_and_switch_variables(void) /* {{{ */
{
int array_offset = CG(context).current_brk_cont;
while (array_offset != -1) {
zend_brk_cont_element *brk_cont = &CG(context).brk_cont_array[array_offset];
generate_free_loop_var(&brk_cont->loop_var);
array_offset = brk_cont->parent;
}
zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
}
/* }}} */
@@ -3550,6 +3613,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
} else {
int array_offset = CG(context).current_brk_cont;
zend_long nest_level = depth;
znode *loop_var = zend_stack_top(&CG(loop_var_stack));
do {
if (array_offset == -1) {
@@ -3558,11 +3622,12 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
depth, depth == 1 ? "" : "s");
}
if (nest_level > 1) {
generate_free_loop_var(&CG(context).brk_cont_array[array_offset].loop_var);
if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) {
generate_free_loop_var(loop_var);
loop_var--;
}
array_offset = CG(context).brk_cont_array[array_offset].parent;
array_offset = CG(active_op_array)->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);
@@ -3571,108 +3636,16 @@ 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;
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].loop_var.op_type != IS_UNUSED) {
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;
}
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].loop_var.op_type != IS_UNUSED) {
if (pass2_opline) {
free_vars++;
} else {
generate_free_loop_var(&CG(context).brk_cont_array[current].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].loop_var.op_type != IS_UNUSED) {
zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk];
(pass2_opline - free_vars)->opcode = brk_opline->opcode;
(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);
zend_resolve_goto_label(CG(active_op_array), &label_node, NULL);
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);
}
/* }}} */
@@ -4768,6 +4741,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
opline_ext->lineno = decl->start_lineno;
}
{
/* Push a separator to the loop variable stack */
znode dummy_var;
dummy_var.op_type = IS_UNUSED;
zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
}
zend_compile_params(params_ast, return_type_ast);
if (uses_ast) {
zend_compile_closure_uses(uses_ast);
@@ -4785,6 +4766,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
pass_two(CG(active_op_array));
zend_oparray_context_end(&orig_oparray_context);
/* Pop the loop variable stack separator */
zend_stack_del_top(&CG(loop_var_stack));
CG(active_op_array) = orig_op_array;
}
/* }}} */
@@ -6301,7 +6285,10 @@ 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) {
@@ -6312,7 +6299,15 @@ 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;
}
/* }}} */
@@ -6640,6 +6635,10 @@ 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;
+27 -35
View File
@@ -109,36 +109,15 @@ typedef struct _zend_declarables {
zend_long ticks;
} zend_declarables;
typedef struct _zend_brk_cont_element {
int cont;
int brk;
int parent;
znode loop_var;
} 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;
@@ -184,6 +163,26 @@ 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
@@ -329,11 +328,6 @@ 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;
@@ -357,12 +351,10 @@ 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 */
@@ -377,6 +369,9 @@ struct _zend_op_array {
int last_literal;
zval *literals;
int cache_size;
void **run_time_cache;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
@@ -717,7 +712,7 @@ void zend_do_extended_fcall_end(void);
void zend_verify_namespace(void);
void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline);
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2);
ZEND_API void function_add_ref(zend_function *function);
@@ -732,8 +727,6 @@ 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);
@@ -958,7 +951,6 @@ 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
+71 -60
View File
@@ -1939,6 +1939,31 @@ 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() \
@@ -2359,66 +2384,7 @@ 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) /* {{{ */
{
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);
}
int i;
if (UNEXPECTED(EX(call))) {
zend_execute_data *call = EX(call);
zend_op *opline = EX(func)->op_array.opcodes + op_num;
@@ -2532,6 +2498,51 @@ 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));
}
}
}
}
}
}
/* }}} */
+2
View File
@@ -68,6 +68,8 @@ typedef struct _zend_ini_entry zend_ini_entry;
struct _zend_compiler_globals {
zend_stack loop_var_stack;
zend_class_entry *active_class_entry;
zend_string *compiled_filename;
+15 -222
View File
@@ -65,7 +65,6 @@ 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();
@@ -78,7 +77,9 @@ 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;
@@ -382,12 +383,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);
}
@@ -446,9 +447,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)
{
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];
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];
}
static void zend_update_extended_info(zend_op_array *op_array)
@@ -575,7 +576,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;
@@ -671,7 +672,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 = &CG(context).brk_cont_array[array_offset];
jmp_to = &op_array->brk_cont_array[array_offset];
if (nest_levels > 1) {
array_offset = jmp_to->parent;
}
@@ -699,8 +700,11 @@ 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, NULL, opline);
zend_resolve_goto_label(op_array, opline, 1);
opline->op2.constant = num;
}
/* break omitted intentionally */
case ZEND_JMP:
@@ -747,9 +751,6 @@ 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) {
@@ -786,7 +787,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, NULL, opline);
zend_resolve_goto_label(op_array, opline, 1);
}
/* break omitted intentionally */
case ZEND_JMP:
@@ -839,214 +840,6 @@ 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));
+25
View File
@@ -4866,6 +4866,31 @@ 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
+30 -5
View File
@@ -2225,6 +2225,31 @@ 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
@@ -48397,27 +48422,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_NULL_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,
+1 -1
View File
@@ -122,7 +122,7 @@ const char *zend_vm_opcodes_map[173] = {
"ZEND_FETCH_OBJ_UNSET",
"ZEND_FETCH_LIST",
"ZEND_FETCH_CONSTANT",
NULL,
"ZEND_GOTO",
"ZEND_EXT_STMT",
"ZEND_EXT_FCALL_BEGIN",
"ZEND_EXT_FCALL_END",
+1
View File
@@ -130,6 +130,7 @@ 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
+1 -1
View File
@@ -120,7 +120,7 @@ int zend_sprintf(char *buffer, const char *format, ...);
PHP_MAJOR_VERSION=7
PHP_MINOR_VERSION=0
PHP_RELEASE_VERSION=0
PHP_EXTRA_VERSION="-dev"
PHP_EXTRA_VERSION="beta1"
PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION"
PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION`
+83
View File
@@ -123,6 +123,10 @@ 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) {
@@ -193,6 +197,65 @@ 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;
@@ -460,6 +523,16 @@ 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;
@@ -1123,6 +1196,16 @@ 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) {
+16
View File
@@ -44,6 +44,14 @@ 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 */
@@ -77,6 +85,7 @@ 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:
@@ -108,6 +117,13 @@ 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];
+1
View File
@@ -624,6 +624,7 @@ 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:
+24 -16
View File
@@ -340,11 +340,29 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
*/
case ZEND_FREE:
case ZEND_CASE: {
zend_op *m = opline;
zend_op *end = op_array->opcodes + op_array->last;
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;
}
}
while (m < end) {
if (ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var) {
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) {
if (m->opcode == ZEND_CASE) {
zval old_val;
ZVAL_COPY_VALUE(&old_val, val);
@@ -353,7 +371,6 @@ 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);
}
@@ -458,17 +475,6 @@ 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,
@@ -488,6 +494,7 @@ 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:
@@ -532,6 +539,7 @@ 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:
@@ -73,6 +73,9 @@ 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;
+4 -2
View File
@@ -386,6 +386,7 @@ 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:
@@ -458,11 +459,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);
}
}
@@ -912,6 +913,7 @@ 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:
@@ -980,11 +982,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);
}
}
+5 -4
View File
@@ -501,6 +501,7 @@ 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:
@@ -589,6 +590,10 @@ 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);
}
@@ -613,10 +618,6 @@ 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);
+4 -4
View File
@@ -229,6 +229,10 @@ 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);
}
@@ -237,10 +241,6 @@ 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;
+2 -2
View File
@@ -3,6 +3,6 @@
#define PHP_MAJOR_VERSION 7
#define PHP_MINOR_VERSION 0
#define PHP_RELEASE_VERSION 0
#define PHP_EXTRA_VERSION "-dev"
#define PHP_VERSION "7.0.0-dev"
#define PHP_EXTRA_VERSION "beta1"
#define PHP_VERSION "7.0.0beta1"
#define PHP_VERSION_ID 70000
+1 -39
View File
@@ -24,7 +24,6 @@
#include "phpdbg_opcode.h"
#include "phpdbg_utils.h"
#include "ext/standard/php_string.h"
#include "zend_smart_str.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
@@ -57,6 +56,7 @@ 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,44 +114,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
break;
}
#if 0
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] : "",