From 08567145763f25aae3882f682d41d1b50cd9e666 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 26 Aug 2013 19:06:36 +0200 Subject: [PATCH] Always pass return_value_ptr to internal functions Previous some places passed return_value_ptr only if the function returned by reference. Now return_value_ptr is always set, even for functions returning by-value. This allows you to return zvals without copying their contents. For this purpose two new macros RETVAL_ZVAL_FAST and RETURN_ZVAL_FAST are added: RETVAL_ZVAL_FAST(zv); /* Analog to RETVAL_ZVAL(zv, 1, 0) */ RETURN_ZVAL_FAST(zv); /* Analog to RETURN_ZVAL(zv, 1, 0) */ These macros behave similarly to the non-FAST versions with copy=1 and dtor=0, with the difference that the FAST versions will try return the zval without copying by utilizing return_value_ptr. --- UPGRADING.INTERNALS | 15 +++++++++++++++ Zend/zend_API.h | 12 ++++++++++++ Zend/zend_execute.c | 16 +++++++++------- Zend/zend_execute_API.c | 4 ++-- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 2 +- 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 566f310998..7cb5539fc1 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -4,6 +4,7 @@ UPGRADE NOTES - PHP X.Y 1. Internal API changes a. Addition of do_operation and compare object handlers + b. return_value_ptr now always available, RETVAL_ZVAL_FAST macros 2. Build system changes a. Unix build system changes @@ -43,6 +44,20 @@ UPGRADE NOTES - PHP X.Y Further docs in the RFC: https://wiki.php.net/rfc/operator_overloading_gmp + b. return_value_ptr now always available, RETVAL_ZVAL_FAST macros + + The return_value_ptr argument to internal functions is now always set. + Previously it was only available for functions returning by-reference. + return_value_ptr can now be used to return zvals without copying them. + For this purpose two new macros are provided: + + RETVAL_ZVAL_FAST(zv); /* analog to RETVAL_ZVAL(zv, 1, 0) */ + RETURN_ZVAL_FAST(zv); /* analog to RETURN_ZVAL(zv, 1, 0) */ + + The macros behave similarly to the non-FAST variants with copy=1 and + dtor=0, but will try to return the zval without making a copy by utilizing + return_value_ptr. + ======================== 2. Build system changes ======================== diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 1a7c66e906..16e766d8a5 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -636,6 +636,18 @@ END_EXTERN_C() #define RETURN_FALSE { RETVAL_FALSE; return; } #define RETURN_TRUE { RETVAL_TRUE; return; } +#define RETVAL_ZVAL_FAST(z) do { \ + zval *_z = (z); \ + if (Z_ISREF_P(_z)) { \ + RETVAL_ZVAL(_z, 1, 0); \ + } else { \ + zval_ptr_dtor(&return_value); \ + Z_ADDREF_P(_z); \ + *return_value_ptr = _z; \ + } \ +} while (0) +#define RETURN_ZVAL_FAST(z) { RETVAL_ZVAL_FAST(z); return; } + #define SET_VAR_STRING(n, v) { \ { \ zval *var; \ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 3c3dd8e3b0..048c1fc184 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1487,15 +1487,17 @@ ZEND_API opcode_handler_t *zend_opcode_handlers; ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, zend_fcall_info *fci, int return_value_used TSRMLS_DC) { - if(fci != NULL) { - ((zend_internal_function *) execute_data_ptr->function_state.function)->handler(fci->param_count, - *fci->retval_ptr_ptr, fci->retval_ptr_ptr, fci->object_ptr, 1 TSRMLS_CC); - + if (fci != NULL) { + execute_data_ptr->function_state.function->internal_function.handler( + fci->param_count, *fci->retval_ptr_ptr, fci->retval_ptr_ptr, + fci->object_ptr, 1 TSRMLS_CC + ); } else { zval **return_value_ptr = &EX_TMP_VAR(execute_data_ptr, execute_data_ptr->opline->result.var)->var.ptr; - ((zend_internal_function *) execute_data_ptr->function_state.function)->handler(execute_data_ptr->opline->extended_value, *return_value_ptr, - (execute_data_ptr->function_state.function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)?return_value_ptr:NULL, - execute_data_ptr->object, return_value_used TSRMLS_CC); + execute_data_ptr->function_state.function->internal_function.handler( + execute_data_ptr->opline->extended_value, *return_value_ptr, return_value_ptr, + execute_data_ptr->object, return_value_used TSRMLS_CC + ); } } diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 83c2217984..0b29086a0d 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -952,9 +952,9 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS if (EX(function_state).function->common.scope) { EG(scope) = EX(function_state).function->common.scope; } - if(EXPECTED(zend_execute_internal == NULL)) { + if (EXPECTED(zend_execute_internal == NULL)) { /* saves one function call if zend_execute_internal is not used */ - ((zend_internal_function *) EX(function_state).function)->handler(fci->param_count, *fci->retval_ptr_ptr, fci->retval_ptr_ptr, fci->object_ptr, 1 TSRMLS_CC); + EX(function_state).function->internal_function.handler(fci->param_count, *fci->retval_ptr_ptr, fci->retval_ptr_ptr, fci->object_ptr, 1 TSRMLS_CC); } else { zend_execute_internal(&execute_data, fci, 1 TSRMLS_CC); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7e4f7a897d..4ab2129235 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1990,7 +1990,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ - fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); + fbc->internal_function.handler(opline->extended_value, ret->var.ptr, &ret->var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); } else { zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5f2c8055a0..0ea5fc4859 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -551,7 +551,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR if (!zend_execute_internal) { /* saves one function call if zend_execute_internal is not used */ - fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); + fbc->internal_function.handler(opline->extended_value, ret->var.ptr, &ret->var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); } else { zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC); } -- 2.40.0