Skip to content

Commit 55cc280

Browse files
committed
Backported call frame initialization improvement
1 parent 88a2268 commit 55cc280

File tree

10 files changed

+424
-367
lines changed

10 files changed

+424
-367
lines changed

Zend/zend_compile.h

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,6 @@ union _zend_function {
482482
zend_internal_function internal_function;
483483
};
484484

485-
typedef enum _zend_call_kind {
486-
ZEND_CALL_NESTED_FUNCTION, /* stackless VM call to function */
487-
ZEND_CALL_NESTED_CODE, /* stackless VM call to include/require/eval */
488-
ZEND_CALL_TOP_FUNCTION, /* direct VM call to function from external C code */
489-
ZEND_CALL_TOP_CODE /* direct VM call to "main" code from external C code */
490-
} zend_call_kind;
491-
492485
struct _zend_execute_data {
493486
const zend_op *opline; /* executed opline */
494487
zend_execute_data *call; /* current call */
@@ -502,41 +495,43 @@ struct _zend_execute_data {
502495
#endif
503496
};
504497

505-
#define ZEND_CALL_FUNCTION (0 << 0)
506-
#define ZEND_CALL_CODE (1 << 0)
507-
#define ZEND_CALL_NESTED (0 << 1)
508-
#define ZEND_CALL_TOP (1 << 1)
509-
#define ZEND_CALL_FREE_EXTRA_ARGS (1 << 2)
510-
#define ZEND_CALL_HAS_SYMBOL_TABLE (1 << 4)
511-
#define ZEND_CALL_CLOSURE (1 << 5)
512-
#define ZEND_CALL_RELEASE_THIS (1 << 6)
513-
#define ZEND_CALL_ALLOCATED (1 << 7)
514-
#define ZEND_CALL_GENERATOR (1 << 8)
515-
#define ZEND_CALL_DYNAMIC (1 << 9)
516-
#define ZEND_CALL_FAKE_CLOSURE (1 << 10)
517-
#define ZEND_CALL_SEND_ARG_BY_REF (1 << 11)
518-
519-
#define ZEND_CALL_INFO_SHIFT 16
498+
#define ZEND_CALL_HAS_THIS IS_OBJECT_EX
499+
500+
/* Top 16 bits of Z_TYPE_INFO(EX(This)) are used as call_info flags */
501+
#define ZEND_CALL_FUNCTION (0 << 16)
502+
#define ZEND_CALL_CODE (1 << 16)
503+
#define ZEND_CALL_NESTED (0 << 17)
504+
#define ZEND_CALL_TOP (1 << 17)
505+
#define ZEND_CALL_ALLOCATED (1 << 18)
506+
#define ZEND_CALL_FREE_EXTRA_ARGS (1 << 19)
507+
#define ZEND_CALL_HAS_SYMBOL_TABLE (1 << 20)
508+
#define ZEND_CALL_RELEASE_THIS (1 << 21)
509+
#define ZEND_CALL_CLOSURE (1 << 22)
510+
#define ZEND_CALL_FAKE_CLOSURE (1 << 23)
511+
#define ZEND_CALL_GENERATOR (1 << 24)
512+
#define ZEND_CALL_DYNAMIC (1 << 25)
513+
#define ZEND_CALL_SEND_ARG_BY_REF (1 << 31)
514+
515+
#define ZEND_CALL_NESTED_FUNCTION (ZEND_CALL_FUNCTION | ZEND_CALL_NESTED)
516+
#define ZEND_CALL_NESTED_CODE (ZEND_CALL_CODE | ZEND_CALL_NESTED)
517+
#define ZEND_CALL_TOP_FUNCTION (ZEND_CALL_TOP | ZEND_CALL_FUNCTION)
518+
#define ZEND_CALL_TOP_CODE (ZEND_CALL_CODE | ZEND_CALL_TOP)
520519

521520
#define ZEND_CALL_INFO(call) \
522-
(Z_TYPE_INFO((call)->This) >> ZEND_CALL_INFO_SHIFT)
521+
Z_TYPE_INFO((call)->This)
523522

524523
#define ZEND_CALL_KIND_EX(call_info) \
525524
(call_info & (ZEND_CALL_CODE | ZEND_CALL_TOP))
526525

527526
#define ZEND_CALL_KIND(call) \
528527
ZEND_CALL_KIND_EX(ZEND_CALL_INFO(call))
529528

530-
#define ZEND_SET_CALL_INFO(call, object, info) do { \
531-
Z_TYPE_INFO((call)->This) = ((object) ? IS_OBJECT_EX : IS_UNDEF) | ((info) << ZEND_CALL_INFO_SHIFT); \
532-
} while (0)
533-
534529
#define ZEND_ADD_CALL_FLAG_EX(call_info, flag) do { \
535-
call_info |= ((flag) << ZEND_CALL_INFO_SHIFT); \
530+
call_info |= (flag); \
536531
} while (0)
537532

538533
#define ZEND_DEL_CALL_FLAG_EX(call_info, flag) do { \
539-
call_info &= ~((flag) << ZEND_CALL_INFO_SHIFT); \
534+
call_info &= ~(flag); \
540535
} while (0)
541536

542537
#define ZEND_ADD_CALL_FLAG(call, flag) do { \

Zend/zend_execute.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3855,30 +3855,37 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s
38553855
}
38563856

38573857
return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC,
3858-
fbc, num_args, called_scope, NULL);
3858+
fbc, num_args, called_scope);
38593859
}
38603860
/* }}} */
38613861

38623862
static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval *function, uint32_t num_args) /* {{{ */
38633863
{
38643864
zend_function *fbc;
3865+
void *object_or_called_scope;
38653866
zend_class_entry *called_scope;
38663867
zend_object *object;
38673868
uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
38683869

38693870
if (EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)) &&
38703871
EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)(function, &called_scope, &fbc, &object) == SUCCESS)) {
38713872

3873+
object_or_called_scope = called_scope;
38723874
if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
38733875
/* Delay closure destruction until its invocation */
38743876
GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc));
38753877
call_info |= ZEND_CALL_CLOSURE;
38763878
if (fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) {
38773879
call_info |= ZEND_CALL_FAKE_CLOSURE;
38783880
}
3881+
if (object) {
3882+
call_info |= ZEND_CALL_HAS_THIS;
3883+
object_or_called_scope = object;
3884+
}
38793885
} else if (object) {
3880-
call_info |= ZEND_CALL_RELEASE_THIS;
3886+
call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS;
38813887
GC_ADDREF(object); /* For $this pointer */
3888+
object_or_called_scope = object;
38823889
}
38833890
} else {
38843891
zend_throw_error(NULL, "Function name must be a string");
@@ -3890,15 +3897,14 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval *
38903897
}
38913898

38923899
return zend_vm_stack_push_call_frame(call_info,
3893-
fbc, num_args, called_scope, object);
3900+
fbc, num_args, object_or_called_scope);
38943901
}
38953902
/* }}} */
38963903

38973904
static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_array *function, uint32_t num_args) /* {{{ */
38983905
{
38993906
zend_function *fbc;
3900-
zend_class_entry *called_scope;
3901-
zend_object *object;
3907+
void *object_or_called_scope;
39023908
uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC;
39033909

39043910
if (zend_hash_num_elements(function) == 2) {
@@ -3925,8 +3931,8 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar
39253931
}
39263932

39273933
if (Z_TYPE_P(obj) == IS_STRING) {
3928-
object = NULL;
3929-
called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
3934+
zend_class_entry *called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
3935+
39303936
if (UNEXPECTED(called_scope == NULL)) {
39313937
return NULL;
39323938
}
@@ -3948,9 +3954,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar
39483954
return NULL;
39493955
}
39503956
}
3957+
object_or_called_scope = called_scope;
39513958
} else {
3952-
called_scope = Z_OBJCE_P(obj);
3953-
object = Z_OBJ_P(obj);
3959+
zend_object *object = Z_OBJ_P(obj);
39543960

39553961
fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL);
39563962
if (UNEXPECTED(fbc == NULL)) {
@@ -3961,10 +3967,11 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar
39613967
}
39623968

39633969
if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
3964-
object = NULL;
3970+
object_or_called_scope = object->ce;
39653971
} else {
3966-
call_info |= ZEND_CALL_RELEASE_THIS;
3972+
call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS;
39673973
GC_ADDREF(object); /* For $this pointer */
3974+
object_or_called_scope = object;
39683975
}
39693976
}
39703977
} else {
@@ -3977,7 +3984,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar
39773984
}
39783985

39793986
return zend_vm_stack_push_call_frame(call_info,
3980-
fbc, num_args, called_scope, object);
3987+
fbc, num_args, object_or_called_scope);
39813988
}
39823989
/* }}} */
39833990

Zend/zend_execute.h

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,20 +206,15 @@ ZEND_API void zend_vm_stack_init_ex(size_t page_size);
206206
ZEND_API void zend_vm_stack_destroy(void);
207207
ZEND_API void* zend_vm_stack_extend(size_t size);
208208

209-
static zend_always_inline void zend_vm_init_call_frame(zend_execute_data *call, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
209+
static zend_always_inline void zend_vm_init_call_frame(zend_execute_data *call, uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope)
210210
{
211211
call->func = func;
212-
if (object) {
213-
Z_OBJ(call->This) = object;
214-
ZEND_SET_CALL_INFO(call, 1, call_info);
215-
} else {
216-
Z_CE(call->This) = called_scope;
217-
ZEND_SET_CALL_INFO(call, 0, call_info);
218-
}
212+
Z_PTR(call->This) = object_or_called_scope;
213+
ZEND_CALL_INFO(call) = call_info;
219214
ZEND_CALL_NUM_ARGS(call) = num_args;
220215
}
221216

222-
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
217+
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope)
223218
{
224219
zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top);
225220

@@ -228,11 +223,11 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui
228223
if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
229224
call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
230225
ZEND_ASSERT_VM_STACK_GLOBAL;
231-
zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, called_scope, object);
226+
zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, object_or_called_scope);
232227
return call;
233228
} else {
234229
EG(vm_stack_top) = (zval*)((char*)call + used_stack);
235-
zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
230+
zend_vm_init_call_frame(call, call_info, func, num_args, object_or_called_scope);
236231
return call;
237232
}
238233
}
@@ -247,12 +242,12 @@ static zend_always_inline uint32_t zend_vm_calc_used_stack(uint32_t num_args, ze
247242
return used_stack * sizeof(zval);
248243
}
249244

250-
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
245+
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope)
251246
{
252247
uint32_t used_stack = zend_vm_calc_used_stack(num_args, func);
253248

254249
return zend_vm_stack_push_call_frame_ex(used_stack, call_info,
255-
func, num_args, called_scope, object);
250+
func, num_args, object_or_called_scope);
256251
}
257252

258253
static zend_always_inline void zend_vm_stack_free_extra_args_ex(uint32_t call_info, zend_execute_data *call)

Zend/zend_execute_API.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,8 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
655655
zend_execute_data *call, dummy_execute_data;
656656
zend_fcall_info_cache fci_cache_local;
657657
zend_function *func;
658+
uint32_t call_info;
659+
void *object_or_called_scope;
658660

659661
ZVAL_UNDEF(fci->retval);
660662

@@ -727,11 +729,18 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
727729
}
728730

729731
func = fci_cache->function_handler;
730-
fci->object = (func->common.fn_flags & ZEND_ACC_STATIC) ?
731-
NULL : fci_cache->object;
732+
if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache->object) {
733+
fci->object = NULL;
734+
object_or_called_scope = fci_cache->called_scope;
735+
call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC;
736+
} else {
737+
fci->object = fci_cache->object;
738+
object_or_called_scope = fci->object;
739+
call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS;
740+
}
732741

733-
call = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC,
734-
func, fci->param_count, fci_cache->called_scope, fci->object);
742+
call = zend_vm_stack_push_call_frame(call_info,
743+
func, fci->param_count, object_or_called_scope);
735744

736745
if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_DEPRECATED)) {
737746
zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated",

Zend/zend_generators.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ ZEND_API void zend_generator_restore_call_stack(zend_generator *generator) /* {{
4040
(ZEND_CALL_INFO(call) & ~ZEND_CALL_ALLOCATED),
4141
call->func,
4242
ZEND_CALL_NUM_ARGS(call),
43-
(Z_TYPE(call->This) == IS_UNDEF) ?
44-
(zend_class_entry*)Z_OBJ(call->This) : NULL,
45-
(Z_TYPE(call->This) != IS_UNDEF) ?
46-
Z_OBJ(call->This) : NULL);
43+
Z_PTR(call->This));
4744
memcpy(((zval*)new_call) + ZEND_CALL_FRAME_SLOT, ((zval*)call) + ZEND_CALL_FRAME_SLOT, ZEND_CALL_NUM_ARGS(call) * sizeof(zval));
4845
new_call->prev_execute_data = prev_call;
4946
prev_call = new_call;

Zend/zend_types.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,6 @@ struct _zval_struct {
201201
zend_uchar type, /* active type */
202202
zend_uchar type_flags,
203203
union {
204-
uint16_t call_info; /* call info for EX(This) */
205204
uint16_t extra; /* not further specified */
206205
} u)
207206
} v;

0 commit comments

Comments
 (0)