Skip to content

Commit ab4ccff

Browse files
committed
Avoid unnecessary reference counter incrementation on $this when call methods
1 parent 62b1293 commit ab4ccff

File tree

5 files changed

+138
-99
lines changed

5 files changed

+138
-99
lines changed

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ struct _zend_execute_data {
454454
#define ZEND_CALL_CTOR (1 << 3)
455455
#define ZEND_CALL_CTOR_RESULT_UNUSED (1 << 4)
456456
#define ZEND_CALL_CLOSURE (1 << 5)
457+
#define ZEND_CALL_RELEASE_THIS (1 << 6)
457458

458459
#define ZEND_CALL_INFO(call) \
459460
(Z_TYPE_INFO((call)->This) >> 24)

Zend/zend_execute.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2067,6 +2067,7 @@ ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data
20672067
zend_execute_data *execute_data;
20682068
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
20692069
size_t stack_size = (ZEND_CALL_FRAME_SLOT + MAX(op_array->last_var + op_array->T, num_args)) * sizeof(zval);
2070+
uint32_t call_info;
20702071

20712072
EG(vm_stack) = zend_vm_stack_new_page(
20722073
EXPECTED(stack_size < ZEND_VM_STACK_FREE_PAGE_SIZE(1)) ?
@@ -2076,8 +2077,12 @@ ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data
20762077
EG(vm_stack_top) = EG(vm_stack)->top;
20772078
EG(vm_stack_end) = EG(vm_stack)->end;
20782079

2080+
call_info = ZEND_CALL_TOP_FUNCTION | (ZEND_CALL_INFO(call) & (ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS));
2081+
if (Z_OBJ(call->This)) {
2082+
call_info |= ZEND_CALL_RELEASE_THIS;
2083+
}
20792084
execute_data = zend_vm_stack_push_call_frame(
2080-
ZEND_CALL_TOP_FUNCTION | (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE),
2085+
call_info,
20812086
(zend_function*)op_array,
20822087
num_args,
20832088
call->called_scope,

Zend/zend_execute_API.c

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -825,12 +825,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
825825
if (func->common.fn_flags & ZEND_ACC_STATIC) {
826826
fci->object = NULL;
827827
}
828-
if (!fci->object) {
829-
Z_OBJ(call->This) = NULL;
830-
} else {
831-
Z_OBJ(call->This) = fci->object;
832-
GC_REFCOUNT(fci->object)++;
833-
}
828+
Z_OBJ(call->This) = fci->object;
834829

835830
if (func->type == ZEND_USER_FUNCTION) {
836831
int call_via_handler = (func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0;
@@ -868,7 +863,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
868863
}
869864
EG(current_execute_data) = call->prev_execute_data;
870865
zend_vm_stack_free_args(call);
871-
zend_vm_stack_free_call_frame(call);
872866

873867
/* We shouldn't fix bad extensions here,
874868
because it can break proper ones (Bug #34045)
@@ -899,7 +893,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
899893
}
900894

901895
zend_vm_stack_free_args(call);
902-
zend_vm_stack_free_call_frame(call);
903896

904897
if (func->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
905898
zend_string_release(func->common.function_name);
@@ -912,11 +905,9 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
912905
}
913906
}
914907

915-
if (fci->object) {
916-
OBJ_RELEASE(fci->object);
917-
}
918-
919908
EG(scope) = orig_scope;
909+
zend_vm_stack_free_call_frame(call);
910+
920911
if (EG(current_execute_data) == &dummy_execute_data) {
921912
EG(current_execute_data) = dummy_execute_data.prev_execute_data;
922913
}

Zend/zend_vm_def.h

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,12 +2401,15 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
24012401
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
24022402
OBJ_RELEASE((zend_object*)old_execute_data->func->op_array.prototype);
24032403
}
2404-
object = Z_OBJ(old_execute_data->This);
2405-
zend_vm_stack_free_call_frame(old_execute_data);
2406-
2407-
if (object) {
2404+
if (call_info & ZEND_CALL_RELEASE_THIS) {
2405+
object = Z_OBJ(old_execute_data->This);
2406+
#if 0
24082407
if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
24092408
if (!(EX(opline)->op1.num & ZEND_CALL_CTOR_RESULT_UNUSED)) {
2409+
#else
2410+
if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
2411+
if (!(call_info & ZEND_CALL_CTOR_RESULT_UNUSED)) {
2412+
#endif
24102413
GC_REFCOUNT(object)--;
24112414
}
24122415
if (GC_REFCOUNT(object) == 1) {
@@ -2417,6 +2420,8 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
24172420
}
24182421
EG(scope) = EX(func)->op_array.scope;
24192422

2423+
zend_vm_stack_free_call_frame(old_execute_data);
2424+
24202425
if (UNEXPECTED(EG(exception) != NULL)) {
24212426
const zend_op *old_opline = EX(opline);
24222427
zend_throw_exception_internal(NULL);
@@ -2882,6 +2887,7 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR
28822887
zend_class_entry *called_scope;
28832888
zend_object *obj;
28842889
zend_execute_data *call;
2890+
uint32_t call_info;
28852891

28862892
SAVE_OPLINE();
28872893

@@ -2959,13 +2965,15 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|CV, CONST|TMPVAR
29592965
}
29602966
}
29612967

2968+
call_info = ZEND_CALL_NESTED_FUNCTION;
29622969
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) {
29632970
obj = NULL;
2964-
} else {
2971+
} else if (OP1_TYPE & (IS_VAR|IS_TMP_VAR)) {
2972+
call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS;
29652973
GC_REFCOUNT(obj)++; /* For $this pointer */
29662974
}
29672975

2968-
call = zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
2976+
call = zend_vm_stack_push_call_frame(call_info,
29692977
fbc, opline->extended_value, called_scope, obj);
29702978
call->prev_execute_data = EX(call);
29712979
EX(call) = call;
@@ -3070,7 +3078,6 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMPVAR|UNUSE
30703078
if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) {
30713079
if (Z_OBJ(EX(This)) && instanceof_function(Z_OBJCE(EX(This)), ce)) {
30723080
object = Z_OBJ(EX(This));
3073-
GC_REFCOUNT(object)++;
30743081
}
30753082
if (!object) {
30763083
if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
@@ -3175,9 +3182,6 @@ ZEND_VM_C_LABEL(try_function_name):
31753182
EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT) &&
31763183
Z_OBJ_HANDLER_P(function_name, get_closure) &&
31773184
Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &called_scope, &fbc, &object) == SUCCESS) {
3178-
if (object) {
3179-
GC_REFCOUNT(object)++;
3180-
}
31813185
if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
31823186
/* Delay closure destruction until its invocation */
31833187
ZEND_ASSERT(GC_TYPE(fbc->common.prototype) == IS_OBJECT);
@@ -3262,6 +3266,7 @@ ZEND_VM_C_LABEL(try_function_name):
32623266
if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
32633267
object = NULL;
32643268
} else {
3269+
call_info |= ZEND_CALL_RELEASE_THIS;
32653270
GC_REFCOUNT(object)++; /* For $this pointer */
32663271
}
32673272
}
@@ -3318,6 +3323,7 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV)
33183323
called_scope = fcc.called_scope;
33193324
object = fcc.object;
33203325
if (object) {
3326+
call_info |= ZEND_CALL_RELEASE_THIS;
33213327
GC_REFCOUNT(object)++; /* For $this pointer */
33223328
} else if (func->common.scope &&
33233329
!(func->common.fn_flags & ZEND_ACC_STATIC)) {
@@ -3595,7 +3601,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
35953601
USE_OPLINE
35963602
zend_execute_data *call = EX(call);
35973603
zend_function *fbc = call->func;
3598-
zend_object *object = Z_OBJ(call->This);
3604+
zend_object *object;
35993605
zval *ret;
36003606

36013607
SAVE_OPLINE();
@@ -3628,8 +3634,6 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
36283634
} else {
36293635
zend_vm_stack_free_args(call);
36303636
}
3631-
3632-
zend_vm_stack_free_call_frame(call);
36333637
} else {
36343638
ret = NULL;
36353639
call->symbol_table = NULL;
@@ -3670,7 +3674,6 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
36703674
if (UNEXPECTED(EG(exception) != NULL)) {
36713675
EG(current_execute_data) = call->prev_execute_data;
36723676
zend_vm_stack_free_args(call);
3673-
zend_vm_stack_free_call_frame(call);
36743677
if (RETURN_VALUE_USED(opline)) {
36753678
ZVAL_UNDEF(EX_VAR(opline->result.var));
36763679
}
@@ -3704,7 +3707,6 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
37043707

37053708
EG(current_execute_data) = call->prev_execute_data;
37063709
zend_vm_stack_free_args(call);
3707-
zend_vm_stack_free_call_frame(call);
37083710

37093711
if (!RETURN_VALUE_USED(opline)) {
37103712
zval_ptr_dtor(EX_VAR(opline->result.var));
@@ -3721,6 +3723,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
37213723
ZVAL_NULL(EX_VAR(opline->result.var));
37223724

37233725
/* Not sure what should be done here if it's a static method */
3726+
object = Z_OBJ(call->This);
37243727
if (EXPECTED(object != NULL)) {
37253728
call->prev_execute_data = execute_data;
37263729
EG(current_execute_data) = call;
@@ -3744,8 +3747,6 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
37443747

37453748
zend_vm_stack_free_args(call);
37463749

3747-
zend_vm_stack_free_call_frame(call);
3748-
37493750
if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
37503751
zend_string_release(fbc->common.function_name);
37513752
}
@@ -3759,9 +3760,15 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
37593760
}
37603761

37613762
ZEND_VM_C_LABEL(fcall_end_change_scope):
3762-
if (object) {
3763+
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
3764+
object = Z_OBJ(call->This);
3765+
#if 0
37633766
if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) {
37643767
if (!(opline->op1.num & ZEND_CALL_CTOR_RESULT_UNUSED)) {
3768+
#else
3769+
if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) {
3770+
if (!(ZEND_CALL_INFO(call) & ZEND_CALL_CTOR_RESULT_UNUSED)) {
3771+
#endif
37653772
GC_REFCOUNT(object)--;
37663773
}
37673774
if (GC_REFCOUNT(object) == 1) {
@@ -3773,6 +3780,7 @@ ZEND_VM_C_LABEL(fcall_end_change_scope):
37733780
EG(scope) = EX(func)->op_array.scope;
37743781

37753782
ZEND_VM_C_LABEL(fcall_end):
3783+
zend_vm_stack_free_call_frame(call);
37763784
if (UNEXPECTED(EG(exception) != NULL)) {
37773785
zend_throw_exception_internal(NULL);
37783786
if (RETURN_VALUE_USED(opline)) {
@@ -4891,7 +4899,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, CONST|VAR, ANY)
48914899
} else {
48924900
/* We are not handling overloaded classes right now */
48934901
zend_execute_data *call = zend_vm_stack_push_call_frame(
4894-
ZEND_CALL_FUNCTION | ZEND_CALL_CTOR |
4902+
ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR |
48954903
(EXPECTED(RETURN_VALUE_USED(opline)) ? 0 : ZEND_CALL_CTOR_RESULT_UNUSED),
48964904
constructor,
48974905
opline->extended_value,
@@ -7121,7 +7129,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
71217129

71227130
zend_vm_stack_free_args(EX(call));
71237131

7124-
if (Z_OBJ(call->This)) {
7132+
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
71257133
if (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR) {
71267134
if (!(ZEND_CALL_INFO(call) & ZEND_CALL_CTOR_RESULT_UNUSED)) {
71277135
GC_REFCOUNT(Z_OBJ(call->This))--;
@@ -7882,7 +7890,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
78827890
zend_function *fbc = EX(func);
78837891
zend_object *object = Z_OBJ(EX(This));
78847892
zval *ret = EX(return_value);
7885-
zend_call_kind call_kind = EX_CALL_KIND();
7893+
uint32_t call_info = EX_CALL_INFO() & (ZEND_CALL_NESTED | ZEND_CALL_TOP | ZEND_CALL_RELEASE_THIS);
78867894
zend_class_entry *scope = EX(called_scope);
78877895
uint32_t num_args = EX_NUM_ARGS();
78887896
zend_execute_data *call;
@@ -7907,7 +7915,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
79077915
call = execute_data;
79087916
execute_data = EG(current_execute_data) = EX(prev_execute_data);
79097917
zend_vm_stack_free_call_frame(call);
7910-
call = zend_vm_stack_push_call_frame(call_kind, fbc->common.prototype, 2, scope, object);
7918+
call = zend_vm_stack_push_call_frame(call_info, fbc->common.prototype, 2, scope, object);
79117919
call->prev_execute_data = execute_data;
79127920

79137921
ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name);
@@ -7981,7 +7989,6 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
79817989
EG(current_execute_data) = call->prev_execute_data;
79827990

79837991
zend_vm_stack_free_args(call);
7984-
zend_vm_stack_free_call_frame(call);
79857992

79867993
if (ret == &retval) {
79877994
zval_ptr_dtor(ret);
@@ -7991,16 +7998,18 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
79917998
ZEND_VM_C_LABEL(call_trampoline_end):
79927999
execute_data = EG(current_execute_data);
79938000

7994-
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_kind & ZEND_CALL_TOP)) {
8001+
if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) {
79958002
ZEND_VM_RETURN();
79968003
}
79978004

79988005
opline = EX(opline);
79998006

8000-
if (object) {
8007+
if (call_info & ZEND_CALL_RELEASE_THIS) {
8008+
object = Z_OBJ(call->This);
80018009
OBJ_RELEASE(object);
80028010
}
80038011
EG(scope) = EX(func)->op_array.scope;
8012+
zend_vm_stack_free_call_frame(call);
80048013

80058014
if (UNEXPECTED(EG(exception) != NULL)) {
80068015
zend_throw_exception_internal(NULL);

0 commit comments

Comments
 (0)