Skip to content

Commit 89c2585

Browse files
committed
Restructure argument passing onto stack
Arguments now are prepended to the execute_data, inside the (last!) temporaries of the previous stack frame. Every stack frame has place for sent arguments plus five optional args (RECV_INIT...) and the execute_data. This allows EG(vm_stack_top) and EX(call) to be removed and saves us from expensive comparisons to push a call frame completely in some cases (every ICALL). The only downside is that arguments now are in reverse order causing variadics to be in reverse order on the stack; extensions might need some update ... Also, freeing of args and execute_data is now completely handled by liveness. Finally, the patch gives 0.5%-3% improvement on applications. There likely is still some room for optimization (e.g. eliminating ZEND_SEND_VAL with TMPs or speeding up the unpacking, removing INIT_FCALL for UCALLs, ...).
1 parent ef6514d commit 89c2585

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4584
-3593
lines changed

Zend/tests/arg_unpack/invalid_type.phpt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ test(1, 2, 3, ...new StdClass, ...3.14, ...[4, 5]);
1616

1717
?>
1818
--EXPECTF--
19-
Warning: Only arrays and Traversables can be unpacked in %s on line %d
19+
Warning: Argument unpacking at parameter 1 requires array or Traversable, null given in %s on line %d
2020
array(0) {
2121
}
2222

23-
Warning: Only arrays and Traversables can be unpacked in %s on line %d
23+
Warning: Argument unpacking at parameter 1 requires array or Traversable, integer given in %s on line %d
2424
array(0) {
2525
}
2626

27-
Warning: Only arrays and Traversables can be unpacked in %s on line %d
27+
Warning: Argument unpacking at parameter 1 requires array or Traversable, object of type stdClass given in %s on line %d
2828
array(0) {
2929
}
3030

31-
Warning: Only arrays and Traversables can be unpacked in %s on line %d
31+
Warning: Argument unpacking at parameter 4 requires array or Traversable, string given in %s on line %d
3232
array(5) {
3333
[0]=>
3434
int(1)
@@ -42,9 +42,9 @@ array(5) {
4242
int(5)
4343
}
4444

45-
Warning: Only arrays and Traversables can be unpacked in %s on line %d
45+
Warning: Argument unpacking at parameter 4 requires array or Traversable, object of type stdClass given in %s on line %d
4646

47-
Warning: Only arrays and Traversables can be unpacked in %s on line %d
47+
Warning: Argument unpacking at parameter 4 requires array or Traversable, float given in %s on line %d
4848
array(5) {
4949
[0]=>
5050
int(1)

Zend/zend_API.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ ZEND_API int zend_get_parameters(int ht, int param_count, ...) /* {{{ */
7070
ZVAL_COPY_VALUE(param_ptr, &new_tmp);
7171
}
7272
*param = param_ptr;
73-
param_ptr++;
73+
param_ptr--;
7474
}
7575
va_end(ptr);
7676

@@ -97,7 +97,7 @@ ZEND_API int zend_get_parameters_ex(int param_count, ...) /* {{{ */
9797
while (param_count-->0) {
9898
param = va_arg(ptr, zval **);
9999
*param = param_ptr;
100-
param_ptr++;
100+
param_ptr--;
101101
}
102102
va_end(ptr);
103103

@@ -120,7 +120,7 @@ ZEND_API int _zend_get_parameters_array_ex(int param_count, zval *argument_array
120120
while (param_count-->0) {
121121
ZVAL_COPY_VALUE(argument_array, param_ptr);
122122
argument_array++;
123-
param_ptr++;
123+
param_ptr--;
124124
}
125125

126126
return SUCCESS;
@@ -144,7 +144,7 @@ ZEND_API int zend_copy_parameters_array(int param_count, zval *argument_array) /
144144
Z_ADDREF_P(param_ptr);
145145
}
146146
zend_hash_next_index_insert_new(Z_ARRVAL_P(argument_array), param_ptr);
147-
param_ptr++;
147+
param_ptr--;
148148
}
149149

150150
return SUCCESS;
@@ -2158,6 +2158,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
21582158
internal_function->function_name = zend_new_interned_string(zend_string_init(ptr->fname, fname_len, 1));
21592159
internal_function->scope = scope;
21602160
internal_function->prototype = NULL;
2161+
internal_function->stack_size = ZEND_CALL_FRAME_SLOT * ZEND_MM_ALIGNED_SIZE(sizeof(zval));
21612162
if (ptr->flags) {
21622163
if (!(ptr->flags & ZEND_ACC_PPP_MASK)) {
21632164
if (ptr->flags != ZEND_ACC_DEPRECATED || scope) {

Zend/zend_API.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
767767
if (_optional) { \
768768
if (UNEXPECTED(_i >_num_args)) break; \
769769
} \
770-
_real_arg++; \
770+
_real_arg--; \
771771
_arg = _real_arg; \
772772
ZVAL_DEREF(_arg); \
773773
if (separate) { \
@@ -1004,7 +1004,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
10041004
ZEND_ASSERT(_i <= _min_num_args || _optional==1); \
10051005
ZEND_ASSERT(_i > _min_num_args || _optional==0); \
10061006
if (_optional && UNEXPECTED(_i >_num_args)) break; \
1007-
_real_arg++; \
1007+
_real_arg--; \
10081008
zend_parse_arg_zval(_real_arg, &dest, check_null); \
10091009
}
10101010

@@ -1023,10 +1023,10 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(int severity, in
10231023
#define Z_PARAM_VARIADIC_EX(spec, dest, dest_num, post_varargs) do { \
10241024
int _num_varargs = _num_args - _i - (post_varargs); \
10251025
if (EXPECTED(_num_varargs > 0)) { \
1026-
dest = _real_arg + 1; \
1026+
dest = _real_arg - 1; \
10271027
dest_num = _num_varargs; \
10281028
_i += _num_varargs; \
1029-
_real_arg += _num_varargs; \
1029+
_real_arg -= _num_varargs; \
10301030
} else { \
10311031
dest = NULL; \
10321032
dest_num = 0; \

Zend/zend_builtin_functions.c

Lines changed: 8 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,9 @@ ZEND_FUNCTION(func_num_args)
438438
Get the $arg_num'th argument that was passed to the function */
439439
ZEND_FUNCTION(func_get_arg)
440440
{
441-
uint32_t arg_count, first_extra_arg;
442441
zval *arg;
443442
zend_long requested_offset;
444-
zend_execute_data *ex;
443+
zend_execute_data *ex = EX(prev_execute_data);
445444

446445
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &requested_offset) == FAILURE) {
447446
return;
@@ -458,19 +457,12 @@ ZEND_FUNCTION(func_get_arg)
458457
RETURN_FALSE;
459458
}
460459

461-
arg_count = ZEND_CALL_NUM_ARGS(ex);
462-
463-
if (requested_offset >= arg_count) {
460+
if (requested_offset >= ZEND_CALL_NUM_ARGS(ex)) {
464461
zend_error(E_WARNING, "func_get_arg(): Argument " ZEND_LONG_FMT " not passed to function", requested_offset);
465462
RETURN_FALSE;
466463
}
467464

468-
first_extra_arg = ex->func->op_array.num_args;
469-
if (requested_offset >= first_extra_arg && (ZEND_CALL_NUM_ARGS(ex) > first_extra_arg)) {
470-
arg = ZEND_CALL_VAR_NUM(ex, ex->func->op_array.last_var + ex->func->op_array.T) + (requested_offset - first_extra_arg);
471-
} else {
472-
arg = ZEND_CALL_ARG(ex, requested_offset + 1);
473-
}
465+
arg = ZEND_CALL_ARG(ex, requested_offset + 1);
474466
if (EXPECTED(!Z_ISUNDEF_P(arg))) {
475467
ZVAL_DEREF(arg);
476468
ZVAL_COPY(return_value, arg);
@@ -483,8 +475,7 @@ ZEND_FUNCTION(func_get_arg)
483475
ZEND_FUNCTION(func_get_args)
484476
{
485477
zval *p, *q;
486-
uint32_t arg_count, first_extra_arg;
487-
uint32_t i, n;
478+
uint32_t arg_count, i, n;
488479
zend_execute_data *ex = EX(prev_execute_data);
489480

490481
if (ZEND_CALL_INFO(ex) & ZEND_CALL_CODE) {
@@ -496,29 +487,12 @@ ZEND_FUNCTION(func_get_args)
496487

497488
array_init_size(return_value, arg_count);
498489
if (arg_count) {
499-
first_extra_arg = ex->func->op_array.num_args;
500490
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
501491
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
502492
i = 0;
503493
n = 0;
504494
p = ZEND_CALL_ARG(ex, 1);
505-
if (arg_count > first_extra_arg) {
506-
while (i < first_extra_arg) {
507-
q = p;
508-
if (EXPECTED(Z_TYPE_INFO_P(q) != IS_UNDEF)) {
509-
ZVAL_DEREF(q);
510-
if (Z_OPT_REFCOUNTED_P(q)) {
511-
Z_ADDREF_P(q);
512-
}
513-
n++;
514-
}
515-
ZEND_HASH_FILL_ADD(q);
516-
p++;
517-
i++;
518-
}
519-
p = ZEND_CALL_VAR_NUM(ex, ex->func->op_array.last_var + ex->func->op_array.T);
520-
}
521-
while (i < arg_count) {
495+
do {
522496
q = p;
523497
if (EXPECTED(Z_TYPE_INFO_P(q) != IS_UNDEF)) {
524498
ZVAL_DEREF(q);
@@ -528,9 +502,9 @@ ZEND_FUNCTION(func_get_args)
528502
n++;
529503
}
530504
ZEND_HASH_FILL_ADD(q);
531-
p++;
505+
p--;
532506
i++;
533-
}
507+
} while (i < arg_count);
534508
} ZEND_HASH_FILL_END();
535509
Z_ARRVAL_P(return_value)->nNumOfElements = n;
536510
}
@@ -2243,25 +2217,6 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
22432217

22442218
zend_hash_real_init(Z_ARRVAL_P(arg_array), 1);
22452219
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(arg_array)) {
2246-
if (call->func->type == ZEND_USER_FUNCTION) {
2247-
uint32_t first_extra_arg = call->func->op_array.num_args;
2248-
2249-
if (ZEND_CALL_NUM_ARGS(call) > first_extra_arg) {
2250-
while (i < first_extra_arg) {
2251-
if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) {
2252-
if (Z_OPT_REFCOUNTED_P(p)) {
2253-
Z_ADDREF_P(p);
2254-
}
2255-
n++;
2256-
}
2257-
ZEND_HASH_FILL_ADD(p);
2258-
p++;
2259-
i++;
2260-
}
2261-
p = ZEND_CALL_VAR_NUM(call, call->func->op_array.last_var + call->func->op_array.T);
2262-
}
2263-
}
2264-
22652220
while (i < num_args) {
22662221
if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) {
22672222
if (Z_OPT_REFCOUNTED_P(p)) {
@@ -2270,7 +2225,7 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
22702225
n++;
22712226
}
22722227
ZEND_HASH_FILL_ADD(p);
2273-
p++;
2228+
p--;
22742229
i++;
22752230
}
22762231
} ZEND_HASH_FILL_END();

Zend/zend_closures.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ ZEND_METHOD(Closure, call)
147147

148148
fci.retval = &closure_result;
149149
fci.params = my_params;
150-
fci.param_count = my_param_count;
150+
fci.param_count = -my_param_count;
151151
fci.object = fci_cache.object = newobj;
152152
fci_cache.initialized = 1;
153153
fci_cache.called_scope = Z_OBJCE_P(newthis);
@@ -277,6 +277,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
277277
invoke->internal_function.module = 0;
278278
invoke->internal_function.scope = zend_ce_closure;
279279
invoke->internal_function.function_name = zend_string_init(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0);
280+
invoke->internal_function.stack_size = ZEND_CALL_FRAME_SLOT * sizeof(zval);
280281
return invoke;
281282
}
282283
/* }}} */

0 commit comments

Comments
 (0)