Skip to content

Commit fe343fa

Browse files
committed
Allocate property type lists on arena
Arg/return types are immutable, while property types are not, so allocate them on arena, otherwise we run into opcache issues. Might need adjustments in some more places to handle arena vs non-arena.
1 parent 2801c1d commit fe343fa

File tree

6 files changed

+53
-13
lines changed

6 files changed

+53
-13
lines changed

Zend/zend_compile.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5424,7 +5424,8 @@ static zend_bool zend_type_contains_traversable(zend_type type) {
54245424

54255425
// TODO: Ideally we'd canonicalize "iterable" into "array|Traversable" and essentially
54265426
// treat it as a built-in type alias.
5427-
static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null) /* {{{ */
5427+
static zend_type zend_compile_typename(
5428+
zend_ast *ast, zend_bool force_allow_null, zend_bool use_arena) /* {{{ */
54285429
{
54295430
zend_bool allow_null = force_allow_null;
54305431
zend_type type = ZEND_TYPE_INIT_NONE(0);
@@ -5458,16 +5459,27 @@ static zend_type zend_compile_typename(zend_ast *ast, zend_bool force_allow_null
54585459
if (ZEND_TYPE_HAS_LIST(type)) {
54595460
/* Add name to existing name list. */
54605461
zend_type_list *old_list = ZEND_TYPE_LIST(type);
5461-
list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
5462+
if (use_arena) {
5463+
// TODO: Add a zend_arena_realloc API?
5464+
list = zend_arena_alloc(
5465+
&CG(arena), ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
5466+
memcpy(list, old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types));
5467+
} else {
5468+
list = erealloc(old_list, ZEND_TYPE_LIST_SIZE(old_list->num_types + 1));
5469+
}
54625470
list->types[list->num_types++] = ZEND_TYPE_NAME(single_type);
54635471
} else {
54645472
/* Switch from single name to name list. */
5465-
list = emalloc(ZEND_TYPE_LIST_SIZE(2));
5473+
size_t size = ZEND_TYPE_LIST_SIZE(2);
5474+
list = use_arena ? zend_arena_alloc(&CG(arena), size) : emalloc(size);
54665475
list->num_types = 2;
54675476
list->types[0] = ZEND_TYPE_NAME(type);
54685477
list->types[1] = ZEND_TYPE_NAME(single_type);
54695478
}
54705479
ZEND_TYPE_SET_LIST(type, list);
5480+
if (use_arena) {
5481+
ZEND_TYPE_FULL_MASK(type) |= _ZEND_TYPE_ARENA_BIT;
5482+
}
54715483

54725484
/* Check for trivially redundant class types */
54735485
for (size_t i = 0; i < list->num_types - 1; i++) {
@@ -5557,7 +5569,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
55575569
/* Use op_array->arg_info[-1] for return type */
55585570
arg_infos = safe_emalloc(sizeof(zend_arg_info), list->children + 1, 0);
55595571
arg_infos->name = NULL;
5560-
arg_infos->type = zend_compile_typename(return_type_ast, 0);
5572+
arg_infos->type = zend_compile_typename(
5573+
return_type_ast, /* force_allow_null */ 0, /* use_arena */ 0);
55615574
ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS(
55625575
(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0);
55635576
arg_infos++;
@@ -5633,7 +5646,8 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */
56335646
uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
56345647

56355648
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
5636-
arg_info->type = zend_compile_typename(type_ast, default_type == IS_NULL);
5649+
arg_info->type = zend_compile_typename(
5650+
type_ast, default_type == IS_NULL, /* use_arena */ 0);
56375651

56385652
if (ZEND_TYPE_FULL_MASK(arg_info->type) & MAY_BE_VOID) {
56395653
zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
@@ -6162,7 +6176,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /
61626176
zend_type type = ZEND_TYPE_INIT_NONE(0);
61636177

61646178
if (type_ast) {
6165-
type = zend_compile_typename(type_ast, 0);
6179+
type = zend_compile_typename(type_ast, /* force_allow_null */ 0, /* use_arena */ 1);
61666180

61676181
if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_CALLABLE)) {
61686182
zend_string *str = zend_type_to_string(type);

Zend/zend_opcode.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ static void zend_always_inline zend_type_release(zend_type *type) {
110110
zend_string_release(ZEND_TYPE_LIST_GET_NAME(entry));
111111
}
112112
} ZEND_TYPE_LIST_FOREACH_END();
113-
efree(ZEND_TYPE_LIST(*type));
113+
if (!ZEND_TYPE_USES_ARENA(*type)) {
114+
efree(ZEND_TYPE_LIST(*type));
115+
}
114116
} else if (ZEND_TYPE_HAS_NAME(*type)) {
115117
zend_string_release(ZEND_TYPE_NAME(*type));
116118
}
@@ -140,8 +142,8 @@ ZEND_API void zend_function_dtor(zval *zv)
140142

141143
if (function->type == ZEND_USER_FUNCTION) {
142144
ZEND_ASSERT(function->common.function_name);
143-
destroy_op_array(&function->op_array);
144-
/* op_arrays are allocated on arena, so we don't have to free them */
145+
destroy_op_array(&function->op_array);
146+
/* op_arrays are allocated on arena, so we don't have to free them */
145147
} else {
146148
ZEND_ASSERT(function->type == ZEND_INTERNAL_FUNCTION);
147149
ZEND_ASSERT(function->common.function_name);

Zend/zend_types.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,12 @@ typedef struct {
139139
#define _ZEND_TYPE_MASK ((1u << 24) - 1)
140140
#define _ZEND_TYPE_MAY_BE_MASK ((1u << (IS_VOID+1)) - 1)
141141
/* Only one of these bits may be set. */
142-
#define _ZEND_TYPE_LIST_BIT (1u << 21)
143-
#define _ZEND_TYPE_CE_BIT (1u << 22)
144142
#define _ZEND_TYPE_NAME_BIT (1u << 23)
143+
#define _ZEND_TYPE_CE_BIT (1u << 22)
144+
#define _ZEND_TYPE_LIST_BIT (1u << 21)
145145
#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_CE_BIT|_ZEND_TYPE_NAME_BIT)
146+
/* Whether the type list is arena allocated */
147+
#define _ZEND_TYPE_ARENA_BIT (1u << 20)
146148
/* Must have same value as MAY_BE_NULL */
147149
#define _ZEND_TYPE_NULLABLE_BIT 0x2
148150

@@ -161,6 +163,9 @@ typedef struct {
161163
#define ZEND_TYPE_HAS_LIST(t) \
162164
((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0)
163165

166+
#define ZEND_TYPE_USES_ARENA(t) \
167+
((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0)
168+
164169
#define ZEND_TYPE_IS_ONLY_MASK(t) \
165170
(ZEND_TYPE_IS_SET(t) && (t).ptr == NULL)
166171

ext/opcache/zend_accelerator_util_funcs.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ static void zend_hash_clone_prop_info(HashTable *ht)
234234
}
235235

236236
if (ZEND_TYPE_HAS_LIST(prop_info->type)) {
237+
zend_type_list *list = ZEND_TYPE_LIST(prop_info->type);
238+
ZEND_ASSERT(IN_ARENA(list));
239+
list = ARENA_REALLOC(list);
240+
ZEND_TYPE_SET_PTR(prop_info->type, list);
241+
237242
void **entry;
238243
ZEND_TYPE_LIST_FOREACH_PTR(ZEND_TYPE_LIST(prop_info->type), entry) {
239244
if (ZEND_TYPE_LIST_IS_CE(*entry)) {

ext/opcache/zend_persist.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,17 @@ static void zend_persist_type(zend_type *type) {
262262
if (ZEND_TYPE_HAS_LIST(*type)) {
263263
void **entry;
264264
zend_type_list *list = ZEND_TYPE_LIST(*type);
265-
list = zend_shared_memdup_put_free(list, ZEND_TYPE_LIST_SIZE(list->num_types));
265+
if (ZEND_TYPE_USES_ARENA(*type)) {
266+
if (!ZCG(is_immutable_class)) {
267+
list = zend_shared_memdup_arena_put(list, ZEND_TYPE_LIST_SIZE(list->num_types));
268+
} else {
269+
/* Moved from arena to SHM because type list was fully resolved. */
270+
list = zend_shared_memdup_put(list, ZEND_TYPE_LIST_SIZE(list->num_types));
271+
ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT;
272+
}
273+
} else {
274+
list = zend_shared_memdup_put_free(list, ZEND_TYPE_LIST_SIZE(list->num_types));
275+
}
266276
ZEND_TYPE_SET_PTR(*type, list);
267277

268278
ZEND_TYPE_LIST_FOREACH_PTR(list, entry) {

ext/opcache/zend_persist_calc.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,11 @@ static void zend_persist_type_calc(zend_type *type)
152152
{
153153
if (ZEND_TYPE_HAS_LIST(*type)) {
154154
void **entry;
155-
ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
155+
if (ZEND_TYPE_USES_ARENA(*type) && !ZCG(is_immutable_class)) {
156+
ADD_ARENA_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
157+
} else {
158+
ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types));
159+
}
156160
ZEND_TYPE_LIST_FOREACH_PTR(ZEND_TYPE_LIST(*type), entry) {
157161
zend_string *type_name = ZEND_TYPE_LIST_GET_NAME(*entry);
158162
ADD_INTERNED_STRING(type_name);

0 commit comments

Comments
 (0)