diff options
author | Nikita Popov <nikic@php.net> | 2016-11-12 18:48:07 +0100 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2016-11-12 18:49:41 +0100 |
commit | 5f4f83220a6a33ad3ec56f36ca65cc641aae4445 (patch) | |
tree | e383760724af6d5c75b2b6ca5a3882b6312121da | |
parent | 2d865dfd358eb61174c2cedd9ed18d1e0872b840 (diff) | |
download | php-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.c | 22 | ||||
-rw-r--r-- | ext/opcache/tests/wrong_inlining_005.phpt | 22 |
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! |