summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-12-12 09:39:52 +0100
committerNikita Popov <nikita.ppv@gmail.com>2019-12-12 09:39:52 +0100
commit2d03b638dcde1d41d8a35b13e2fb6ca64c745f16 (patch)
tree62f8b04b4cbfda07d06ca497eb168c8440968280
parent86aac3eed24ddc8ace3661ea1c3b50aa3d45f883 (diff)
downloadphp-git-2d03b638dcde1d41d8a35b13e2fb6ca64c745f16.tar.gz
Fix handling of non-final loop var free in sccp
We only need to preserve the FE_FREE that marks the end of the loop range. Skip FE_FREEs with the FREE_ON_RETURN flag.
-rw-r--r--ext/opcache/Optimizer/scdf.c3
-rw-r--r--ext/opcache/Optimizer/zend_cfg.c4
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c3
-rw-r--r--ext/opcache/Optimizer/zend_optimizer_internal.h5
-rw-r--r--ext/opcache/tests/sccp_loop_var_free.phpt18
5 files changed, 26 insertions, 7 deletions
diff --git a/ext/opcache/Optimizer/scdf.c b/ext/opcache/Optimizer/scdf.c
index 1c7cbc7e55..aa7ea3a1a2 100644
--- a/ext/opcache/Optimizer/scdf.c
+++ b/ext/opcache/Optimizer/scdf.c
@@ -195,8 +195,7 @@ static zend_bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx)
}
for (i = block->start; i < block->start + block->len; i++) {
zend_op *opline = &op_array->opcodes[i];
- if (opline->opcode == ZEND_FE_FREE ||
- (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH)) {
+ if (zend_optimizer_is_loop_var_free(opline)) {
int ssa_var = scdf->ssa->ops[i].op1_use;
if (ssa_var >= 0) {
int op_num = scdf->ssa->vars[ssa_var].definition;
diff --git a/ext/opcache/Optimizer/zend_cfg.c b/ext/opcache/Optimizer/zend_cfg.c
index 5e0f137024..66c15be311 100644
--- a/ext/opcache/Optimizer/zend_cfg.c
+++ b/ext/opcache/Optimizer/zend_cfg.c
@@ -208,9 +208,7 @@ static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *
for (j = b->start; j < b->start + b->len; j++) {
zend_op *opline = &op_array->opcodes[j];
- if (opline->opcode == ZEND_FE_FREE ||
- (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH)
- ) {
+ if (zend_optimizer_is_loop_var_free(opline)) {
zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline);
if (def_opline) {
uint32_t def_block = block_map[def_opline - op_array->opcodes];
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c
index a922d0f597..0b4181c051 100644
--- a/ext/opcache/Optimizer/zend_optimizer.c
+++ b/ext/opcache/Optimizer/zend_optimizer.c
@@ -918,8 +918,7 @@ uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args)
zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) {
uint32_t var = free_opline->op1.var;
- ZEND_ASSERT(free_opline->opcode == ZEND_FE_FREE ||
- (free_opline->opcode == ZEND_FREE && free_opline->extended_value == ZEND_FREE_SWITCH));
+ ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline));
while (--free_opline >= op_array->opcodes) {
if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) {
diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h
index 9ab18f6398..df7be73d4f 100644
--- a/ext/opcache/Optimizer/zend_optimizer_internal.h
+++ b/ext/opcache/Optimizer/zend_optimizer_internal.h
@@ -71,6 +71,11 @@ typedef struct _zend_optimizer_ctx {
target = src; \
} while (0)
+static inline zend_bool zend_optimizer_is_loop_var_free(const zend_op *opline) {
+ return (opline->opcode == ZEND_FE_FREE && opline->extended_value != ZEND_FREE_ON_RETURN)
+ || (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH);
+}
+
int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv);
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy);
void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value);
diff --git a/ext/opcache/tests/sccp_loop_var_free.phpt b/ext/opcache/tests/sccp_loop_var_free.phpt
new file mode 100644
index 0000000000..5166823b0b
--- /dev/null
+++ b/ext/opcache/tests/sccp_loop_var_free.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Check that SCCP correctly handles non-terminating frees of loop variables
+--FILE--
+<?php
+function test() {
+ $arr = [];
+ foreach ($arr as $item) {
+ if (!empty($result)) {
+ return $result;
+ }
+ }
+ return 2;
+}
+
+var_dump(test());
+?>
+--EXPECT--
+int(2)