]> granicus.if.org Git - php/commitdiff
Optimize return type checking
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 6 Dec 2019 13:15:58 +0000 (14:15 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 6 Dec 2019 13:17:03 +0000 (14:17 +0100)
Split off the fast-path case and avoid redundant checks.

Zend/zend_execute.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index 153d8e3bc4ea95fd249d749fabad8ea7753b772e..0cd4e105e92b6f44dc488bba87dbee52b52b57ad 100644 (file)
@@ -1027,24 +1027,11 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
        return zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES());
 }
 
-
-static zend_always_inline zend_bool zend_check_type(
-               zend_type type, zval *arg, void **cache_slot, zend_class_entry *scope,
+static zend_always_inline zend_bool zend_check_type_slow(
+               zend_type type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope,
                zend_bool is_return_type, zend_bool is_internal)
 {
-       zend_reference *ref = NULL;
        uint32_t type_mask;
-       ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
-
-       if (UNEXPECTED(Z_ISREF_P(arg))) {
-               ref = Z_REF_P(arg);
-               arg = Z_REFVAL_P(arg);
-       }
-
-       if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) {
-               return 1;
-       }
-
        if (ZEND_TYPE_HAS_CLASS(type) && Z_TYPE_P(arg) == IS_OBJECT) {
                zend_class_entry *ce;
                if (ZEND_TYPE_HAS_LIST(type)) {
@@ -1109,6 +1096,25 @@ builtin_types:
         * because this case is already checked at compile-time. */
 }
 
+static zend_always_inline zend_bool zend_check_type(
+               zend_type type, zval *arg, void **cache_slot, zend_class_entry *scope,
+               zend_bool is_return_type, zend_bool is_internal)
+{
+       zend_reference *ref = NULL;
+       ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
+
+       if (UNEXPECTED(Z_ISREF_P(arg))) {
+               ref = Z_REF_P(arg);
+               arg = Z_REFVAL_P(arg);
+       }
+
+       if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) {
+               return 1;
+       }
+
+       return zend_check_type_slow(type, arg, ref, cache_slot, scope, is_return_type, is_internal);
+}
+
 static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
 {
        zend_arg_info *cur_arg_info;
index c6d37e57a0d9642945659868e02af7de01999a61..eed570121d595704efb59098b9f28e06ff674593 100644 (file)
@@ -4098,15 +4098,15 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
 {
        USE_OPLINE
 
-       SAVE_OPLINE();
        if (OP1_TYPE == IS_UNUSED) {
+               SAVE_OPLINE();
                zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
+               HANDLE_EXCEPTION();
        } else {
 /* prevents "undefined variable opline" errors */
 #if !ZEND_VM_SPEC || (OP1_TYPE != IS_UNUSED)
                zval *retval_ref, *retval_ptr;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
-
                retval_ref = retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
 
                if (OP1_TYPE == IS_CONST) {
@@ -4121,24 +4121,35 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
                        ZVAL_DEREF(retval_ptr);
                }
 
-               if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
-                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
-                       && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                       && retval_ref != retval_ptr)
-               ) {
-                       /* A cast might happen - unwrap the reference if this is a by-value return */
-                       if (Z_REFCOUNT_P(retval_ref) == 1) {
-                               ZVAL_UNREF(retval_ref);
+               if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
+                       ZEND_VM_NEXT_OPCODE();
+               }
+
+               zend_reference *ref = NULL;
+               void *cache_slot = CACHE_ADDR(opline->op2.num);
+               if (UNEXPECTED(retval_ref != retval_ptr)) {
+                       if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+                               ref = Z_REF_P(retval_ref);
                        } else {
-                               Z_DELREF_P(retval_ref);
-                               ZVAL_COPY(retval_ref, retval_ptr);
+                               /* A cast might happen - unwrap the reference if this is a by-value return */
+                               if (Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       Z_DELREF_P(retval_ref);
+                                       ZVAL_COPY(retval_ref, retval_ptr);
+                               }
+                               retval_ptr = retval_ref;
                        }
-                       retval_ptr = retval_ref;
                }
-               zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
+
+               SAVE_OPLINE();
+               if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+                       zend_verify_return_error(EX(func), cache_slot, retval_ptr);
+                       HANDLE_EXCEPTION();
+               }
+               ZEND_VM_NEXT_OPCODE();
 #endif
        }
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
 ZEND_VM_INLINE_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY)
index 7e173dc2f70996275498a73df9e1c1bb666ebf8d..1a50f32c72b69b01810fcc711b4074ca640c66bb 100644 (file)
@@ -8719,15 +8719,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
 {
        USE_OPLINE
 
-       SAVE_OPLINE();
        if (IS_CONST == IS_UNUSED) {
+               SAVE_OPLINE();
                zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
+               HANDLE_EXCEPTION();
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_CONST != IS_UNUSED)
                zval *retval_ref, *retval_ptr;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
-
                retval_ref = retval_ptr = RT_CONSTANT(opline, opline->op1);
 
                if (IS_CONST == IS_CONST) {
@@ -8742,24 +8742,35 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
                        ZVAL_DEREF(retval_ptr);
                }
 
-               if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
-                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
-                       && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                       && retval_ref != retval_ptr)
-               ) {
-                       /* A cast might happen - unwrap the reference if this is a by-value return */
-                       if (Z_REFCOUNT_P(retval_ref) == 1) {
-                               ZVAL_UNREF(retval_ref);
+               if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
+                       ZEND_VM_NEXT_OPCODE();
+               }
+
+               zend_reference *ref = NULL;
+               void *cache_slot = CACHE_ADDR(opline->op2.num);
+               if (UNEXPECTED(retval_ref != retval_ptr)) {
+                       if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+                               ref = Z_REF_P(retval_ref);
                        } else {
-                               Z_DELREF_P(retval_ref);
-                               ZVAL_COPY(retval_ref, retval_ptr);
+                               /* A cast might happen - unwrap the reference if this is a by-value return */
+                               if (Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       Z_DELREF_P(retval_ref);
+                                       ZVAL_COPY(retval_ref, retval_ptr);
+                               }
+                               retval_ptr = retval_ref;
                        }
-                       retval_ptr = retval_ref;
                }
-               zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
+
+               SAVE_OPLINE();
+               if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+                       zend_verify_return_error(EX(func), cache_slot, retval_ptr);
+                       HANDLE_EXCEPTION();
+               }
+               ZEND_VM_NEXT_OPCODE();
 #endif
        }
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -18663,15 +18674,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
 {
        USE_OPLINE
 
-       SAVE_OPLINE();
        if (IS_TMP_VAR == IS_UNUSED) {
+               SAVE_OPLINE();
                zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
+               HANDLE_EXCEPTION();
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_TMP_VAR != IS_UNUSED)
                zval *retval_ref, *retval_ptr;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
-
                retval_ref = retval_ptr = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
 
                if (IS_TMP_VAR == IS_CONST) {
@@ -18686,24 +18697,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
                        ZVAL_DEREF(retval_ptr);
                }
 
-               if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
-                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
-                       && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                       && retval_ref != retval_ptr)
-               ) {
-                       /* A cast might happen - unwrap the reference if this is a by-value return */
-                       if (Z_REFCOUNT_P(retval_ref) == 1) {
-                               ZVAL_UNREF(retval_ref);
+               if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
+                       ZEND_VM_NEXT_OPCODE();
+               }
+
+               zend_reference *ref = NULL;
+               void *cache_slot = CACHE_ADDR(opline->op2.num);
+               if (UNEXPECTED(retval_ref != retval_ptr)) {
+                       if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+                               ref = Z_REF_P(retval_ref);
                        } else {
-                               Z_DELREF_P(retval_ref);
-                               ZVAL_COPY(retval_ref, retval_ptr);
+                               /* A cast might happen - unwrap the reference if this is a by-value return */
+                               if (Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       Z_DELREF_P(retval_ref);
+                                       ZVAL_COPY(retval_ref, retval_ptr);
+                               }
+                               retval_ptr = retval_ref;
                        }
-                       retval_ptr = retval_ref;
                }
-               zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
+
+               SAVE_OPLINE();
+               if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+                       zend_verify_return_error(EX(func), cache_slot, retval_ptr);
+                       HANDLE_EXCEPTION();
+               }
+               ZEND_VM_NEXT_OPCODE();
 #endif
        }
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -26089,15 +26111,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
 {
        USE_OPLINE
 
-       SAVE_OPLINE();
        if (IS_VAR == IS_UNUSED) {
+               SAVE_OPLINE();
                zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
+               HANDLE_EXCEPTION();
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_VAR != IS_UNUSED)
                zval *retval_ref, *retval_ptr;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
-
                retval_ref = retval_ptr = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
 
                if (IS_VAR == IS_CONST) {
@@ -26112,24 +26134,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
                        ZVAL_DEREF(retval_ptr);
                }
 
-               if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
-                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
-                       && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                       && retval_ref != retval_ptr)
-               ) {
-                       /* A cast might happen - unwrap the reference if this is a by-value return */
-                       if (Z_REFCOUNT_P(retval_ref) == 1) {
-                               ZVAL_UNREF(retval_ref);
+               if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
+                       ZEND_VM_NEXT_OPCODE();
+               }
+
+               zend_reference *ref = NULL;
+               void *cache_slot = CACHE_ADDR(opline->op2.num);
+               if (UNEXPECTED(retval_ref != retval_ptr)) {
+                       if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+                               ref = Z_REF_P(retval_ref);
                        } else {
-                               Z_DELREF_P(retval_ref);
-                               ZVAL_COPY(retval_ref, retval_ptr);
+                               /* A cast might happen - unwrap the reference if this is a by-value return */
+                               if (Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       Z_DELREF_P(retval_ref);
+                                       ZVAL_COPY(retval_ref, retval_ptr);
+                               }
+                               retval_ptr = retval_ref;
                        }
-                       retval_ptr = retval_ref;
                }
-               zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
+
+               SAVE_OPLINE();
+               if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+                       zend_verify_return_error(EX(func), cache_slot, retval_ptr);
+                       HANDLE_EXCEPTION();
+               }
+               ZEND_VM_NEXT_OPCODE();
 #endif
        }
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -32710,15 +32743,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
 {
        USE_OPLINE
 
-       SAVE_OPLINE();
        if (IS_UNUSED == IS_UNUSED) {
+               SAVE_OPLINE();
                zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
+               HANDLE_EXCEPTION();
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_UNUSED != IS_UNUSED)
                zval *retval_ref, *retval_ptr;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
-
                retval_ref = retval_ptr = NULL;
 
                if (IS_UNUSED == IS_CONST) {
@@ -32733,24 +32766,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
                        ZVAL_DEREF(retval_ptr);
                }
 
-               if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
-                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
-                       && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                       && retval_ref != retval_ptr)
-               ) {
-                       /* A cast might happen - unwrap the reference if this is a by-value return */
-                       if (Z_REFCOUNT_P(retval_ref) == 1) {
-                               ZVAL_UNREF(retval_ref);
+               if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
+                       ZEND_VM_NEXT_OPCODE();
+               }
+
+               zend_reference *ref = NULL;
+               void *cache_slot = CACHE_ADDR(opline->op2.num);
+               if (UNEXPECTED(retval_ref != retval_ptr)) {
+                       if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+                               ref = Z_REF_P(retval_ref);
                        } else {
-                               Z_DELREF_P(retval_ref);
-                               ZVAL_COPY(retval_ref, retval_ptr);
+                               /* A cast might happen - unwrap the reference if this is a by-value return */
+                               if (Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       Z_DELREF_P(retval_ref);
+                                       ZVAL_COPY(retval_ref, retval_ptr);
+                               }
+                               retval_ptr = retval_ref;
                        }
-                       retval_ptr = retval_ref;
                }
-               zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
+
+               SAVE_OPLINE();
+               if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+                       zend_verify_return_error(EX(func), cache_slot, retval_ptr);
+                       HANDLE_EXCEPTION();
+               }
+               ZEND_VM_NEXT_OPCODE();
 #endif
        }
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -44156,15 +44200,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
 {
        USE_OPLINE
 
-       SAVE_OPLINE();
        if (IS_CV == IS_UNUSED) {
+               SAVE_OPLINE();
                zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
+               HANDLE_EXCEPTION();
        } else {
 /* prevents "undefined variable opline" errors */
 #if 0 || (IS_CV != IS_UNUSED)
                zval *retval_ref, *retval_ptr;
                zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
-
                retval_ref = retval_ptr = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC);
 
                if (IS_CV == IS_CONST) {
@@ -44179,24 +44223,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
                        ZVAL_DEREF(retval_ptr);
                }
 
-               if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
-                       && !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
-                       && !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
-                       && retval_ref != retval_ptr)
-               ) {
-                       /* A cast might happen - unwrap the reference if this is a by-value return */
-                       if (Z_REFCOUNT_P(retval_ref) == 1) {
-                               ZVAL_UNREF(retval_ref);
+               if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
+                       ZEND_VM_NEXT_OPCODE();
+               }
+
+               zend_reference *ref = NULL;
+               void *cache_slot = CACHE_ADDR(opline->op2.num);
+               if (UNEXPECTED(retval_ref != retval_ptr)) {
+                       if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
+                               ref = Z_REF_P(retval_ref);
                        } else {
-                               Z_DELREF_P(retval_ref);
-                               ZVAL_COPY(retval_ref, retval_ptr);
+                               /* A cast might happen - unwrap the reference if this is a by-value return */
+                               if (Z_REFCOUNT_P(retval_ref) == 1) {
+                                       ZVAL_UNREF(retval_ref);
+                               } else {
+                                       Z_DELREF_P(retval_ref);
+                                       ZVAL_COPY(retval_ref, retval_ptr);
+                               }
+                               retval_ptr = retval_ref;
                        }
-                       retval_ptr = retval_ref;
                }
-               zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
+
+               SAVE_OPLINE();
+               if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
+                       zend_verify_return_error(EX(func), cache_slot, retval_ptr);
+                       HANDLE_EXCEPTION();
+               }
+               ZEND_VM_NEXT_OPCODE();
 #endif
        }
-       ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)