Skip to content

Commit 0bac785

Browse files
committed
Optimize return type checking
Split off the fast-path case and avoid redundant checks.
1 parent 990b556 commit 0bac785

File tree

3 files changed

+177
-105
lines changed

3 files changed

+177
-105
lines changed

Zend/zend_execute.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,24 +1027,11 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
10271027
return zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES());
10281028
}
10291029

1030-
1031-
static zend_always_inline zend_bool zend_check_type(
1032-
zend_type type, zval *arg, void **cache_slot, zend_class_entry *scope,
1030+
static zend_always_inline zend_bool zend_check_type_slow(
1031+
zend_type type, zval *arg, zend_reference *ref, void **cache_slot, zend_class_entry *scope,
10331032
zend_bool is_return_type, zend_bool is_internal)
10341033
{
1035-
zend_reference *ref = NULL;
10361034
uint32_t type_mask;
1037-
ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
1038-
1039-
if (UNEXPECTED(Z_ISREF_P(arg))) {
1040-
ref = Z_REF_P(arg);
1041-
arg = Z_REFVAL_P(arg);
1042-
}
1043-
1044-
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) {
1045-
return 1;
1046-
}
1047-
10481035
if (ZEND_TYPE_HAS_CLASS(type) && Z_TYPE_P(arg) == IS_OBJECT) {
10491036
zend_class_entry *ce;
10501037
if (ZEND_TYPE_HAS_LIST(type)) {
@@ -1109,6 +1096,25 @@ static zend_always_inline zend_bool zend_check_type(
11091096
* because this case is already checked at compile-time. */
11101097
}
11111098

1099+
static zend_always_inline zend_bool zend_check_type(
1100+
zend_type type, zval *arg, void **cache_slot, zend_class_entry *scope,
1101+
zend_bool is_return_type, zend_bool is_internal)
1102+
{
1103+
zend_reference *ref = NULL;
1104+
ZEND_ASSERT(ZEND_TYPE_IS_SET(type));
1105+
1106+
if (UNEXPECTED(Z_ISREF_P(arg))) {
1107+
ref = Z_REF_P(arg);
1108+
arg = Z_REFVAL_P(arg);
1109+
}
1110+
1111+
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) {
1112+
return 1;
1113+
}
1114+
1115+
return zend_check_type_slow(type, arg, ref, cache_slot, scope, is_return_type, is_internal);
1116+
}
1117+
11121118
static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
11131119
{
11141120
zend_arg_info *cur_arg_info;

Zend/zend_vm_def.h

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4098,15 +4098,15 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
40984098
{
40994099
USE_OPLINE
41004100

4101-
SAVE_OPLINE();
41024101
if (OP1_TYPE == IS_UNUSED) {
4102+
SAVE_OPLINE();
41034103
zend_verify_missing_return_type(EX(func), CACHE_ADDR(opline->op2.num));
4104+
HANDLE_EXCEPTION();
41044105
} else {
41054106
/* prevents "undefined variable opline" errors */
41064107
#if !ZEND_VM_SPEC || (OP1_TYPE != IS_UNUSED)
41074108
zval *retval_ref, *retval_ptr;
41084109
zend_arg_info *ret_info = EX(func)->common.arg_info - 1;
4109-
41104110
retval_ref = retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
41114111

41124112
if (OP1_TYPE == IS_CONST) {
@@ -4121,24 +4121,35 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
41214121
ZVAL_DEREF(retval_ptr);
41224122
}
41234123

4124-
if (UNEXPECTED((ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_ANY)
4125-
&& !ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr))
4126-
&& !(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)
4127-
&& retval_ref != retval_ptr)
4128-
) {
4129-
/* A cast might happen - unwrap the reference if this is a by-value return */
4130-
if (Z_REFCOUNT_P(retval_ref) == 1) {
4131-
ZVAL_UNREF(retval_ref);
4124+
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(retval_ptr)))) {
4125+
ZEND_VM_NEXT_OPCODE();
4126+
}
4127+
4128+
zend_reference *ref = NULL;
4129+
void *cache_slot = CACHE_ADDR(opline->op2.num);
4130+
if (UNEXPECTED(retval_ref != retval_ptr)) {
4131+
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
4132+
ref = Z_REF_P(retval_ref);
41324133
} else {
4133-
Z_DELREF_P(retval_ref);
4134-
ZVAL_COPY(retval_ref, retval_ptr);
4134+
/* A cast might happen - unwrap the reference if this is a by-value return */
4135+
if (Z_REFCOUNT_P(retval_ref) == 1) {
4136+
ZVAL_UNREF(retval_ref);
4137+
} else {
4138+
Z_DELREF_P(retval_ref);
4139+
ZVAL_COPY(retval_ref, retval_ptr);
4140+
}
4141+
retval_ptr = retval_ref;
41354142
}
4136-
retval_ptr = retval_ref;
41374143
}
4138-
zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
4144+
4145+
SAVE_OPLINE();
4146+
if (UNEXPECTED(!zend_check_type_slow(ret_info->type, retval_ptr, ref, cache_slot, NULL, 1, 0))) {
4147+
zend_verify_return_error(EX(func), cache_slot, retval_ptr);
4148+
HANDLE_EXCEPTION();
4149+
}
4150+
ZEND_VM_NEXT_OPCODE();
41394151
#endif
41404152
}
4141-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
41424153
}
41434154

41444155
ZEND_VM_INLINE_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY)

0 commit comments

Comments
 (0)