From: Dmitry Stogov <dmitry@zend.com>
Date: Fri, 26 Dec 2014 19:34:44 +0000 (+0300)
Subject: Optimized destruction of extra arguments passed to user functions.
X-Git-Tag: PRE_PHP7_REMOVALS~35^2~5
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ab0b15b8ff90a292dcf0af2eb95d426ba4662095;p=php

Optimized destruction of extra arguments passed to user functions.
If no refcounted arguments are passed, then destruction code is not triggered at all.
(Full rebuild required)
---

diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index f56005450c..7909e9800a 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -412,8 +412,9 @@ struct _zend_execute_data {
 #define ZEND_CALL_CODE               (1 << 0)
 #define ZEND_CALL_NESTED             (0 << 1)
 #define ZEND_CALL_TOP                (1 << 1)
-#define ZEND_CALL_CTOR               (1 << 2)
-#define ZEND_CALL_CTOR_RESULT_UNUSED (1 << 3)
+#define ZEND_CALL_FREE_EXTRA_ARGS    (1 << 2) /* equal to IS_TYPE_REFCOUNTED */
+#define ZEND_CALL_CTOR               (1 << 3)
+#define ZEND_CALL_CTOR_RESULT_UNUSED (1 << 4)
 
 #define ZEND_CALL_INFO(call) \
 	(Z_TYPE_INFO((call)->This) >> 24)
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 35dd10feb2..3c01b28c48 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1617,6 +1617,7 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu
 	num_args = EX_NUM_ARGS();
 	if (UNEXPECTED(num_args > first_extra_arg)) {
 		zval *end, *src, *dst;
+		uint32_t type_flags = 0;
 
 		if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
 			/* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
@@ -1629,12 +1630,19 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu
 		dst = src + (op_array->last_var + op_array->T - first_extra_arg);
 		if (EXPECTED(src != dst)) {
 			do {
+				type_flags |= Z_TYPE_INFO_P(src);
 				ZVAL_COPY_VALUE(dst, src);
 				ZVAL_UNDEF(src);
 				src--;
 				dst--;
 			} while (src != end);
+		} else {
+			do {
+				type_flags |= Z_TYPE_INFO_P(src);
+				src--;
+			} while (src != end);
 		}
+		ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED));
 	} else if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
 		/* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
 		EX(opline) += num_args;
@@ -1709,6 +1717,7 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
 		num_args = EX_NUM_ARGS();
 		if (UNEXPECTED(num_args > first_extra_arg)) {
 			zval *end, *src, *dst;
+			uint32_t type_flags = 0;
 
 			if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
 				/* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
@@ -1721,12 +1730,19 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
 			dst = src + (op_array->last_var + op_array->T - first_extra_arg);
 			if (EXPECTED(src != dst)) {
 				do {
+					type_flags |= Z_TYPE_INFO_P(src);
 					ZVAL_COPY_VALUE(dst, src);
 					ZVAL_UNDEF(src);
 					src--;
 					dst--;
 				} while (src != end);
+			} else {
+				do {
+					type_flags |= Z_TYPE_INFO_P(src);
+					src--;
+				} while (src != end);
 			}
+			ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED));
 		} else if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) {
 			/* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */
 			EX(opline) += num_args;
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 9197e4c9e5..a94299fd06 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -176,11 +176,9 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint3
 
 static zend_always_inline void zend_vm_stack_free_extra_args(zend_execute_data *call)
 {
-	uint32_t first_extra_arg = call->func->op_array.num_args;
-
- 	if (UNEXPECTED(ZEND_CALL_NUM_ARGS(call) > first_extra_arg)) {
- 		zval *end = ZEND_CALL_VAR_NUM(call, call->func->op_array.last_var + call->func->op_array.T);
- 		zval *p = end + (ZEND_CALL_NUM_ARGS(call) - first_extra_arg);
+	if (ZEND_CALL_INFO(call) & ZEND_CALL_FREE_EXTRA_ARGS) {
+		zval *end = ZEND_CALL_VAR_NUM(call, call->func->op_array.last_var + call->func->op_array.T);
+ 		zval *p = end + (ZEND_CALL_NUM_ARGS(call) - call->func->op_array.num_args);
 		do {
 			p--;
 			zval_ptr_dtor_nogc(p);
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 1f7978b688..28885db75a 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -264,7 +264,7 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 #define Z_COUNTED_P(zval_p)			Z_COUNTED(*(zval_p))
 
 #define Z_TYPE_FLAGS_SHIFT			8
-#define Z_CONST_FLAGS_SHIFT			8
+#define Z_CONST_FLAGS_SHIFT			16
 
 #define GC_REFCOUNT(p)				((zend_refcounted*)(p))->refcount
 #define GC_TYPE(p)					((zend_refcounted*)(p))->u.v.type
@@ -286,10 +286,10 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 
 /* zval.u1.v.type_flags */
 #define IS_TYPE_CONSTANT			(1<<0)
-#define IS_TYPE_REFCOUNTED			(1<<1)
-#define IS_TYPE_COLLECTABLE			(1<<2)
-#define IS_TYPE_COPYABLE			(1<<3)
-#define IS_TYPE_IMMUTABLE			(1<<4)
+#define IS_TYPE_IMMUTABLE			(1<<1)
+#define IS_TYPE_REFCOUNTED			(1<<2)
+#define IS_TYPE_COLLECTABLE			(1<<3)
+#define IS_TYPE_COPYABLE			(1<<4)
 
 /* extended types */
 #define IS_INTERNED_STRING_EX		IS_STRING