Skip to content

Commit a32f491

Browse files
authored
Remove cache slot from ZEND_VERIFY_TYPE and arg RECV opcodes (#18258)
1 parent bd43334 commit a32f491

10 files changed

+57
-190
lines changed

Diff for: UPGRADING.INTERNALS

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES
2525
char* instead of zend_string*-based smart strings.
2626
. Added php_build_provider() to retrieve the value of PHP_BUILD_PROVIDER at
2727
runtime.
28+
. Removed the cache_slot argument of zend_check_user_type_slow() because
29+
now it only relies on the CE cache.
2830

2931
========================
3032
2. Build system changes

Diff for: Zend/Optimizer/compact_literals.c

-64
Original file line numberDiff line numberDiff line change
@@ -43,50 +43,6 @@ typedef struct _literal_info {
4343
info[n].num_related = (related); \
4444
} while (0)
4545

46-
static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
47-
{
48-
zend_arg_info *arg_info;
49-
if (arg_num > 0) {
50-
if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
51-
return 0;
52-
}
53-
if (EXPECTED(arg_num <= op_array->num_args)) {
54-
arg_info = &op_array->arg_info[arg_num-1];
55-
} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
56-
arg_info = &op_array->arg_info[op_array->num_args];
57-
} else {
58-
return 0;
59-
}
60-
} else {
61-
arg_info = op_array->arg_info - 1;
62-
}
63-
64-
if (ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
65-
if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
66-
/* Intersection types cannot have nested list types */
67-
if (ZEND_TYPE_IS_INTERSECTION(arg_info->type)) {
68-
return ZEND_TYPE_LIST(arg_info->type)->num_types;
69-
}
70-
ZEND_ASSERT(ZEND_TYPE_IS_UNION(arg_info->type));
71-
size_t count = 0;
72-
const zend_type *list_type;
73-
74-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(arg_info->type), list_type) {
75-
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
76-
count += ZEND_TYPE_LIST(*list_type)->num_types;
77-
} else {
78-
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
79-
count += 1;
80-
}
81-
} ZEND_TYPE_LIST_FOREACH_END();
82-
return count;
83-
}
84-
return 1;
85-
}
86-
87-
return 0;
88-
}
89-
9046
static uint32_t add_static_slot(HashTable *hash,
9147
zend_op_array *op_array,
9248
uint32_t op1,
@@ -504,26 +460,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
504460
opline->op2.constant = map[opline->op2.constant];
505461
}
506462
switch (opline->opcode) {
507-
case ZEND_RECV_INIT:
508-
case ZEND_RECV:
509-
case ZEND_RECV_VARIADIC:
510-
{
511-
size_t num_classes = type_num_classes(op_array, opline->op1.num);
512-
if (num_classes) {
513-
opline->extended_value = cache_size;
514-
cache_size += num_classes * sizeof(void *);
515-
}
516-
break;
517-
}
518-
case ZEND_VERIFY_RETURN_TYPE:
519-
{
520-
size_t num_classes = type_num_classes(op_array, 0);
521-
if (num_classes) {
522-
opline->op2.num = cache_size;
523-
cache_size += num_classes * sizeof(void *);
524-
}
525-
break;
526-
}
527463
case ZEND_ASSIGN_STATIC_PROP_OP:
528464
if (opline->op1_type == IS_CONST) {
529465
// op1 static property

Diff for: Zend/zend_compile.c

-35
Original file line numberDiff line numberDiff line change
@@ -2620,33 +2620,6 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */
26202620
}
26212621
/* }}} */
26222622

2623-
/* Remember to update type_num_classes() in compact_literals.c when changing this function */
2624-
static size_t zend_type_get_num_classes(const zend_type type) {
2625-
if (!ZEND_TYPE_IS_COMPLEX(type)) {
2626-
return 0;
2627-
}
2628-
if (ZEND_TYPE_HAS_LIST(type)) {
2629-
/* Intersection types cannot have nested list types */
2630-
if (ZEND_TYPE_IS_INTERSECTION(type)) {
2631-
return ZEND_TYPE_LIST(type)->num_types;
2632-
}
2633-
ZEND_ASSERT(ZEND_TYPE_IS_UNION(type));
2634-
size_t count = 0;
2635-
const zend_type *list_type;
2636-
2637-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
2638-
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
2639-
count += ZEND_TYPE_LIST(*list_type)->num_types;
2640-
} else {
2641-
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
2642-
count += 1;
2643-
}
2644-
} ZEND_TYPE_LIST_FOREACH_END();
2645-
return count;
2646-
}
2647-
return 1;
2648-
}
2649-
26502623
static void zend_emit_return_type_check(
26512624
znode *expr, zend_arg_info *return_info, bool implicit) /* {{{ */
26522625
{
@@ -2708,8 +2681,6 @@ static void zend_emit_return_type_check(
27082681
opline->result_type = expr->op_type = IS_TMP_VAR;
27092682
opline->result.var = expr->u.op.var = get_temporary_variable();
27102683
}
2711-
2712-
opline->op2.num = zend_alloc_cache_slots(zend_type_get_num_classes(return_info->type));
27132684
}
27142685
}
27152686
/* }}} */
@@ -7754,12 +7725,6 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
77547725
SET_NODE(opline->result, &var_node);
77557726
opline->op1.num = i + 1;
77567727

7757-
if (type_ast) {
7758-
/* Allocate cache slot to speed-up run-time class resolution */
7759-
opline->extended_value =
7760-
zend_alloc_cache_slots(zend_type_get_num_classes(arg_info->type));
7761-
}
7762-
77637728
uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0)
77647729
| (is_promoted ? _ZEND_IS_PROMOTED_BIT : 0);
77657730
ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags;

Diff for: Zend/zend_execute.c

+26-54
Original file line numberDiff line numberDiff line change
@@ -1099,23 +1099,9 @@ static zend_always_inline bool zend_value_instanceof_static(const zval *zv) {
10991099
return instanceof_function(Z_OBJCE_P(zv), called_scope);
11001100
}
11011101

1102-
/* The cache_slot may only be NULL in debug builds, where arginfo verification of
1103-
* internal functions is enabled. Avoid unnecessary checks in release builds. */
1104-
#if ZEND_DEBUG
1105-
# define HAVE_CACHE_SLOT (cache_slot != NULL)
1106-
#else
1107-
# define HAVE_CACHE_SLOT 1
1108-
#endif
1109-
1110-
#define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;}
1111-
1112-
static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot(
1113-
void **cache_slot, const zend_type *type)
1102+
static zend_always_inline zend_class_entry *zend_fetch_ce_from_type(
1103+
const zend_type *type)
11141104
{
1115-
if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) {
1116-
return (zend_class_entry *) *cache_slot;
1117-
}
1118-
11191105
zend_string *name = ZEND_TYPE_NAME(*type);
11201106
zend_class_entry *ce;
11211107
if (ZSTR_HAS_CE_CACHE(name)) {
@@ -1134,68 +1120,54 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot(
11341120
return NULL;
11351121
}
11361122
}
1137-
if (HAVE_CACHE_SLOT) {
1138-
*cache_slot = (void *) ce;
1139-
}
11401123
return ce;
11411124
}
11421125

1143-
static bool zend_check_intersection_type_from_cache_slot(
1126+
static bool zend_check_intersection_type_from_list(
11441127
const zend_type_list *intersection_type_list,
1145-
const zend_class_entry *arg_ce,
1146-
void ***cache_slot_ptr)
1128+
zend_class_entry *arg_ce)
11471129
{
1148-
void **cache_slot = *cache_slot_ptr;
1130+
zend_class_entry *ce;
11491131
const zend_type *list_type;
1150-
bool status = true;
11511132
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
1152-
/* Only check classes if the type might be valid */
1153-
if (status) {
1154-
zend_class_entry *ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
1155-
/* If type is not an instance of one of the types taking part in the
1156-
* intersection it cannot be a valid instance of the whole intersection type. */
1157-
if (!ce || !instanceof_function(arg_ce, ce)) {
1158-
status = false;
1159-
}
1133+
ce = zend_fetch_ce_from_type(list_type);
1134+
/* If type is not an instance of one of the types taking part in the
1135+
* intersection it cannot be a valid instance of the whole intersection type. */
1136+
if (!ce || !instanceof_function(arg_ce, ce)) {
1137+
return false;
11601138
}
1161-
PROGRESS_CACHE_SLOT();
11621139
} ZEND_TYPE_LIST_FOREACH_END();
1163-
if (HAVE_CACHE_SLOT) {
1164-
*cache_slot_ptr = cache_slot;
1165-
}
1166-
return status;
1140+
return true;
11671141
}
11681142

11691143
static zend_always_inline bool zend_check_type_slow(
1170-
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot,
1144+
const zend_type *type, zval *arg, const zend_reference *ref,
11711145
bool is_return_type, bool is_internal)
11721146
{
11731147
if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
11741148
zend_class_entry *ce;
11751149
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
11761150
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
1177-
return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot);
1151+
return zend_check_intersection_type_from_list(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg));
11781152
} else {
11791153
const zend_type *list_type;
11801154
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
11811155
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
1182-
if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) {
1156+
if (zend_check_intersection_type_from_list(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg))) {
11831157
return true;
11841158
}
1185-
/* The cache_slot is progressed in zend_check_intersection_type_from_cache_slot() */
11861159
} else {
11871160
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
1188-
ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type);
1161+
ce = zend_fetch_ce_from_type(list_type);
11891162
/* Instance of a single type part of a union is sufficient to pass the type check */
11901163
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
11911164
return true;
11921165
}
1193-
PROGRESS_CACHE_SLOT();
11941166
}
11951167
} ZEND_TYPE_LIST_FOREACH_END();
11961168
}
11971169
} else {
1198-
ce = zend_fetch_ce_from_cache_slot(cache_slot, type);
1170+
ce = zend_fetch_ce_from_type(type);
11991171
/* If we have a CE we check if it satisfies the type constraint,
12001172
* otherwise it will check if a standard type satisfies it. */
12011173
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
@@ -1232,7 +1204,7 @@ static zend_always_inline bool zend_check_type_slow(
12321204
}
12331205

12341206
static zend_always_inline bool zend_check_type(
1235-
const zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope,
1207+
const zend_type *type, zval *arg, zend_class_entry *scope,
12361208
bool is_return_type, bool is_internal)
12371209
{
12381210
const zend_reference *ref = NULL;
@@ -1247,25 +1219,25 @@ static zend_always_inline bool zend_check_type(
12471219
return 1;
12481220
}
12491221

1250-
return zend_check_type_slow(type, arg, ref, cache_slot, is_return_type, is_internal);
1222+
return zend_check_type_slow(type, arg, ref, is_return_type, is_internal);
12511223
}
12521224

12531225
ZEND_API bool zend_check_user_type_slow(
1254-
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type)
1226+
const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type)
12551227
{
12561228
return zend_check_type_slow(
1257-
type, arg, ref, cache_slot, is_return_type, /* is_internal */ false);
1229+
type, arg, ref, is_return_type, /* is_internal */ false);
12581230
}
12591231

1260-
static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
1232+
static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg)
12611233
{
12621234
const zend_arg_info *cur_arg_info;
12631235

12641236
ZEND_ASSERT(arg_num <= zf->common.num_args);
12651237
cur_arg_info = &zf->common.arg_info[arg_num-1];
12661238

12671239
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
1268-
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
1240+
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, zf->common.scope, 0, 0))) {
12691241
zend_verify_arg_error(zf, cur_arg_info, arg_num, arg);
12701242
return 0;
12711243
}
@@ -1274,10 +1246,10 @@ static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf
12741246
}
12751247

12761248
static zend_always_inline bool zend_verify_variadic_arg_type(
1277-
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot)
1249+
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg)
12781250
{
12791251
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
1280-
if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
1252+
if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, zf->common.scope, 0, 0))) {
12811253
zend_verify_arg_error(zf, arg_info, arg_num, arg);
12821254
return 0;
12831255
}
@@ -1302,7 +1274,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
13021274
}
13031275

13041276
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
1305-
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, /* cache_slot */ NULL, fbc->common.scope, 0, /* is_internal */ 1))) {
1277+
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, fbc->common.scope, 0, /* is_internal */ 1))) {
13061278
return 0;
13071279
}
13081280
arg++;
@@ -1508,7 +1480,7 @@ ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *re
15081480
return 1;
15091481
}
15101482

1511-
if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, /* cache_slot */ NULL, NULL, 1, /* is_internal */ 1))) {
1483+
if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, NULL, 1, /* is_internal */ 1))) {
15121484
zend_verify_internal_return_error(zf, ret);
15131485
return 0;
15141486
}

Diff for: Zend/zend_execute.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ ZEND_API ZEND_COLD void zend_verify_never_error(
106106
const zend_function *zf);
107107
ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref);
108108
ZEND_API bool zend_check_user_type_slow(
109-
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type);
109+
const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type);
110110

111111
#if ZEND_DEBUG
112112
ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call);

0 commit comments

Comments
 (0)