Skip to content

Commit 3a18597

Browse files
committed
Optimize argument passing handlers:
- predcalculate offsets of passed argument and store it in opline->result.var for ZEND_SEND_*; - don't update "number of passed arguments" op each ZEND_SEND_* opcode, store it on call frame creation - Change ZEND_HANDLE_EXCEPTION to determine "number of passed arguments" in incomplete calls fefore freeing
1 parent ea7f768 commit 3a18597

File tree

5 files changed

+194
-64
lines changed

5 files changed

+194
-64
lines changed

Zend/zend_compile.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2407,6 +2407,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc TSRMLS_DC) /* {{{ *
24072407
zend_compile_expr(&arg_node, arg->child[0] TSRMLS_CC);
24082408
opline = zend_emit_op(NULL, ZEND_SEND_UNPACK, &arg_node, NULL TSRMLS_CC);
24092409
opline->op2.num = arg_count;
2410+
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_count);
24102411
continue;
24112412
}
24122413

@@ -2469,6 +2470,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc TSRMLS_DC) /* {{{ *
24692470
SET_NODE(opline->op1, &arg_node);
24702471
SET_UNUSED(opline->op2);
24712472
opline->op2.opline_num = arg_num;
2473+
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_num);
24722474

24732475
if (opcode == ZEND_SEND_VAR_NO_REF) {
24742476
if (fbc) {
@@ -2696,7 +2698,7 @@ int zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcna
26962698
return FAILURE;
26972699
}
26982700

2699-
zend_compile_init_user_func(args->child[0], 1, lcname TSRMLS_CC);
2701+
zend_compile_init_user_func(args->child[0], 0, lcname TSRMLS_CC);
27002702
zend_compile_expr(&arg_node, args->child[1] TSRMLS_CC);
27012703
zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL TSRMLS_CC);
27022704
zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL TSRMLS_CC);
@@ -2737,7 +2739,8 @@ int zend_compile_func_cuf(znode *result, zend_ast_list *args, zend_string *lcnam
27372739
opline = zend_emit_op(NULL, ZEND_SEND_VAL, &arg_node, NULL TSRMLS_CC);
27382740
}
27392741

2740-
opline->op2.opline_num = i;
2742+
opline->op2.num = i;
2743+
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, i);
27412744
}
27422745
zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL TSRMLS_CC);
27432746

Zend/zend_execute.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,14 @@ static zend_always_inline zval* zend_vm_stack_alloc(size_t size TSRMLS_DC)
143143
return (zval*)top;
144144
}
145145

146-
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t call_info, zend_function *func, uint32_t used_stack, zend_class_entry *called_scope, zend_object *object, zend_execute_data *prev TSRMLS_DC)
146+
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, zend_execute_data *prev TSRMLS_DC)
147147
{
148148
zend_execute_data *call = (zend_execute_data*)zend_vm_stack_alloc(used_stack TSRMLS_CC);
149149

150150
call->func = func;
151151
Z_OBJ(call->This) = object;
152152
ZEND_SET_CALL_INFO(call, call_info);
153-
ZEND_CALL_NUM_ARGS(call) = 0;
153+
ZEND_CALL_NUM_ARGS(call) = num_args;
154154
call->called_scope = called_scope;
155155
call->prev_execute_data = prev;
156156
return call;
@@ -170,8 +170,8 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint3
170170
{
171171
uint32_t used_stack = zend_vm_calc_used_stack(num_args, func);
172172

173-
return zend_vm_stack_push_call_frame_ex(call_info,
174-
func, used_stack, called_scope, object, prev TSRMLS_CC);
173+
return zend_vm_stack_push_call_frame_ex(used_stack, call_info,
174+
func, num_args, called_scope, object, prev TSRMLS_CC);
175175
}
176176

177177
static zend_always_inline void zend_vm_stack_free_extra_args(zend_execute_data *call TSRMLS_DC)

Zend/zend_execute_API.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,6 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
818818
ZVAL_COPY(param, &fci->params[i]);
819819
}
820820
}
821-
ZEND_CALL_NUM_ARGS(call) = fci->param_count;
822821

823822
EG(scope) = calling_scope;
824823
if (func->common.fn_flags & ZEND_ACC_STATIC) {

Zend/zend_vm_def.h

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,8 +2736,9 @@ ZEND_VM_HANDLER(61, ZEND_INIT_FCALL, ANY, CONST)
27362736
CACHE_PTR(Z_CACHE_SLOT_P(fname), fbc);
27372737
}
27382738

2739-
EX(call) = zend_vm_stack_push_call_frame_ex(ZEND_CALL_NESTED_FUNCTION,
2740-
fbc, opline->op1.num, NULL, NULL, EX(call) TSRMLS_CC);
2739+
EX(call) = zend_vm_stack_push_call_frame_ex(
2740+
opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
2741+
fbc, opline->extended_value, NULL, NULL, EX(call) TSRMLS_CC);
27412742

27422743
FREE_OP2();
27432744

@@ -3126,8 +3127,7 @@ ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMP, ANY)
31263127

31273128
SAVE_OPLINE();
31283129
value = GET_OP1_ZVAL_PTR(BP_VAR_R);
3129-
arg = ZEND_CALL_ARG(EX(call), opline->op2.num);
3130-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3130+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
31313131
ZVAL_COPY_VALUE(arg, value);
31323132
if (OP1_TYPE == IS_CONST) {
31333133
if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
@@ -3148,8 +3148,7 @@ ZEND_VM_HANDLER(116, ZEND_SEND_VAL_EX, CONST|TMP, ANY)
31483148
zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", opline->op2.num);
31493149
}
31503150
value = GET_OP1_ZVAL_PTR(BP_VAR_R);
3151-
arg = ZEND_CALL_ARG(EX(call), opline->op2.num);
3152-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3151+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
31533152
ZVAL_COPY_VALUE(arg, value);
31543153
if (OP1_TYPE == IS_CONST) {
31553154
if (UNEXPECTED(Z_OPT_COPYABLE_P(arg))) {
@@ -3166,8 +3165,7 @@ ZEND_VM_HANDLER(117, ZEND_SEND_VAR, VAR|CV, ANY)
31663165
zend_free_op free_op1;
31673166

31683167
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
3169-
arg = ZEND_CALL_ARG(EX(call), opline->op2.num);
3170-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3168+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
31713169
if (Z_ISREF_P(varptr)) {
31723170
ZVAL_COPY(arg, Z_REFVAL_P(varptr));
31733171
FREE_OP1();
@@ -3211,8 +3209,7 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY)
32113209
}
32123210
}
32133211

3214-
arg = ZEND_CALL_ARG(EX(call), opline->op2.num);
3215-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3212+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
32163213
ZVAL_COPY_VALUE(arg, varptr);
32173214

32183215
CHECK_EXCEPTION();
@@ -3232,8 +3229,7 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY)
32323229
zend_error_noreturn(E_ERROR, "Only variables can be passed by reference");
32333230
}
32343231

3235-
arg = ZEND_CALL_ARG(EX(call), opline->op2.num);
3236-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3232+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
32373233
if (OP1_TYPE == IS_VAR && UNEXPECTED(varptr == &EG(error_zval))) {
32383234
ZVAL_NEW_REF(arg, &EG(uninitialized_zval));
32393235
ZEND_VM_NEXT_OPCODE();
@@ -3265,8 +3261,7 @@ ZEND_VM_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, ANY)
32653261
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
32663262
}
32673263
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
3268-
arg = ZEND_CALL_ARG(EX(call), opline->op2.num);
3269-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3264+
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
32703265
if (Z_ISREF_P(varptr)) {
32713266
ZVAL_COPY(arg, Z_REFVAL_P(varptr));
32723267
FREE_OP1();
@@ -3477,7 +3472,6 @@ ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY)
34773472
zval *arg, *param, tmp;
34783473

34793474
ZEND_VM_C_LABEL(send_array):
3480-
arg_num = 1;
34813475
ht = Z_ARRVAL_P(args);
34823476
zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht) TSRMLS_CC);
34833477

@@ -3487,7 +3481,7 @@ ZEND_VM_C_LABEL(send_array):
34873481

34883482
/* check if any of arguments are going to be passed by reference */
34893483
for (i = 0; i < zend_hash_num_elements(ht); i++) {
3490-
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
3484+
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, i)) {
34913485
separate = 1;
34923486
break;
34933487
}
@@ -3498,7 +3492,8 @@ ZEND_VM_C_LABEL(send_array):
34983492
}
34993493
}
35003494

3501-
param = ZEND_CALL_ARG(EX(call), arg_num);
3495+
arg_num = 1;
3496+
param = ZEND_CALL_ARG(EX(call), 1);
35023497
ZEND_HASH_FOREACH_VAL(ht, arg) {
35033498
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
35043499
// TODO: Scalar values don't have reference counters anymore.
@@ -3577,7 +3572,7 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
35773572
zend_free_op free_op1;
35783573

35793574
arg = GET_OP1_ZVAL_PTR(BP_VAR_R);
3580-
param = ZEND_CALL_ARG(EX(call), opline->op2.num);
3575+
param = ZEND_CALL_VAR(EX(call), opline->result.var);
35813576

35823577
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, opline->op2.num)) {
35833578
// TODO: Scalar values don't have reference counters anymore.
@@ -3642,8 +3637,6 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, VAR|CV, ANY)
36423637
ZVAL_COPY(param, arg);
36433638
}
36443639

3645-
ZEND_CALL_NUM_ARGS(EX(call)) = opline->op2.num;
3646-
36473640
FREE_OP1();
36483641
CHECK_EXCEPTION();
36493642
ZEND_VM_NEXT_OPCODE();
@@ -5670,11 +5663,86 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
56705663
}
56715664
}
56725665

5673-
if (EX(call)) {
5666+
if (UNEXPECTED(EX(call))) {
56745667
zend_execute_data *call = EX(call);
5668+
zend_op *opline = EX(func)->op_array.opcodes + op_num;
5669+
int level;
5670+
int do_exit;
5671+
56755672
do {
56765673
/* If the exception was thrown during a function call there might be
56775674
* arguments pushed to the stack that have to be dtor'ed. */
5675+
5676+
/* find the number of actually passed arguments */
5677+
level = 0;
5678+
do_exit = 0;
5679+
do {
5680+
switch (opline->opcode) {
5681+
case ZEND_DO_FCALL:
5682+
level++;
5683+
break;
5684+
case ZEND_INIT_FCALL:
5685+
case ZEND_INIT_FCALL_BY_NAME:
5686+
case ZEND_INIT_NS_FCALL_BY_NAME:
5687+
case ZEND_INIT_USER_CALL:
5688+
case ZEND_INIT_METHOD_CALL:
5689+
case ZEND_INIT_STATIC_METHOD_CALL:
5690+
case ZEND_NEW:
5691+
if (level == 0) {
5692+
ZEND_CALL_NUM_ARGS(call) = 0;
5693+
do_exit = 1;
5694+
}
5695+
level--;
5696+
break;
5697+
case ZEND_SEND_VAL:
5698+
case ZEND_SEND_VAL_EX:
5699+
case ZEND_SEND_VAR:
5700+
case ZEND_SEND_VAR_EX:
5701+
case ZEND_SEND_REF:
5702+
case ZEND_SEND_VAR_NO_REF:
5703+
case ZEND_SEND_USER:
5704+
if (level == 0) {
5705+
ZEND_CALL_NUM_ARGS(call) = opline->op2.num;
5706+
do_exit = 1;
5707+
}
5708+
break;
5709+
case ZEND_SEND_ARRAY:
5710+
case ZEND_SEND_UNPACK:
5711+
if (level == 0) {
5712+
do_exit = 1;
5713+
}
5714+
break;
5715+
}
5716+
if (!do_exit) {
5717+
opline--;
5718+
}
5719+
} while (!do_exit);
5720+
if (call->prev_execute_data) {
5721+
/* skip current call region */
5722+
level = 0;
5723+
do_exit = 0;
5724+
do {
5725+
switch (opline->opcode) {
5726+
case ZEND_DO_FCALL:
5727+
level++;
5728+
break;
5729+
case ZEND_INIT_FCALL:
5730+
case ZEND_INIT_FCALL_BY_NAME:
5731+
case ZEND_INIT_NS_FCALL_BY_NAME:
5732+
case ZEND_INIT_USER_CALL:
5733+
case ZEND_INIT_METHOD_CALL:
5734+
case ZEND_INIT_STATIC_METHOD_CALL:
5735+
case ZEND_NEW:
5736+
if (level == 0) {
5737+
do_exit = 1;
5738+
}
5739+
level--;
5740+
break;
5741+
}
5742+
opline--;
5743+
} while (!do_exit);
5744+
}
5745+
56785746
zend_vm_stack_free_args(EX(call) TSRMLS_CC);
56795747

56805748
if (Z_OBJ(call->This)) {

0 commit comments

Comments
 (0)