]> granicus.if.org Git - php/commitdiff
Fix handling of non-final loop var free in sccp
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 12 Dec 2019 08:39:52 +0000 (09:39 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 12 Dec 2019 08:39:52 +0000 (09:39 +0100)
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.

ext/opcache/Optimizer/scdf.c
ext/opcache/Optimizer/zend_cfg.c
ext/opcache/Optimizer/zend_optimizer.c
ext/opcache/Optimizer/zend_optimizer_internal.h
ext/opcache/tests/sccp_loop_var_free.phpt [new file with mode: 0644]

index 1c7cbc7e55e1084b381a60a67d3335818f6c1fb6..aa7ea3a1a2cc24c70dd480e459c6ef79cc8724a7 100644 (file)
@@ -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;
index 5e0f1370246df184804bb673189ce6b899a48afe..66c15be3113ac1300f6827c48bbe0cddae75ac67 100644 (file)
@@ -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];
index a922d0f5974c8f1f89b3ea73d455fb6d53223229..0b4181c051dd30dcaa407550c7a9eb332116ed7a 100644 (file)
@@ -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) {
index 9ab18f6398045b7259cc3ca2b904ee14c8e4403a..df7be73d4f53fe6e239e8e1fefa859758804762c 100644 (file)
@@ -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 (file)
index 0000000..5166823
--- /dev/null
@@ -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)