summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikic@php.net>2016-11-12 18:48:07 +0100
committerNikita Popov <nikic@php.net>2016-11-12 18:49:41 +0100
commit5f4f83220a6a33ad3ec56f36ca65cc641aae4445 (patch)
treee383760724af6d5c75b2b6ca5a3882b6312121da
parent2d865dfd358eb61174c2cedd9ed18d1e0872b840 (diff)
downloadphp-git-5f4f83220a6a33ad3ec56f36ca65cc641aae4445.tar.gz
Don't inline if function has ref arguments
Otherwise we end up leaving opcodes like FETCH_DIM_W behind. The test case demonstrates a leak in particular.
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c22
-rw-r--r--ext/opcache/tests/wrong_inlining_005.phpt22
2 files changed, 38 insertions, 6 deletions
diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
index f9a42db79e..3480a9a6ee 100644
--- a/ext/opcache/Optimizer/optimize_func_calls.c
+++ b/ext/opcache/Optimizer/optimize_func_calls.c
@@ -71,8 +71,6 @@ static void zend_delete_call_instructions(zend_op *opline)
break;
case ZEND_SEND_VAL:
case ZEND_SEND_VAR:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_REF:
if (call == 0) {
if (opline->op1_type == IS_CONST) {
MAKE_NOP(opline);
@@ -102,6 +100,8 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o
zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
if (ret_opline->op1_type == IS_CONST) {
+ uint32_t i, num_args = func->op_array.num_args;
+ num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
/* TODO: we can't inlne methods, because $this may be used
@@ -109,17 +109,27 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o
*/
return;
}
+
+ for (i = 0; i < num_args; i++) {
+ /* Don't inline functions with by-reference arguments. This would require
+ * correct handling of INDIRECT arguments. */
+ if (func->op_array.arg_info[i].pass_by_reference) {
+ return;
+ }
+ }
+
if (fcall->extended_value < func->op_array.num_args) {
/* don't inline funcions with named constants in default arguments */
- uint32_t n = fcall->extended_value;
+ i = fcall->extended_value;
do {
- if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[n].op2))) {
+ if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) {
return;
}
- n++;
- } while (n < func->op_array.num_args);
+ i++;
+ } while (i < func->op_array.num_args);
}
+
if (RETURN_VALUE_USED(opline)) {
zval zv;
diff --git a/ext/opcache/tests/wrong_inlining_005.phpt b/ext/opcache/tests/wrong_inlining_005.phpt
new file mode 100644
index 0000000000..b34cd1b12f
--- /dev/null
+++ b/ext/opcache/tests/wrong_inlining_005.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Inlining of functions with ref arguments
+--FILE--
+<?php
+
+function by_ref(&$var)
+{
+}
+function &get_array() {
+ $array = [new stdClass];
+ return $array;
+}
+function test()
+{
+ by_ref(get_array()[0]);
+ print "ok!\n";
+}
+test();
+
+?>
+--EXPECT--
+ok!