Skip to content

Commit 178c3e3

Browse files
committed
Improve how the variadic placeholder is passed
* We can't handle the variadic placeholder like non-variadic ones, as the slot may be occupied by a named arg. * Using Z_EXTRA(arg0) works, but it's uninitialized unless we set it in all SEND_PLACEHOLDER ops.
1 parent 0332e6f commit 178c3e3

File tree

7 files changed

+70
-56
lines changed

7 files changed

+70
-56
lines changed

Zend/tests/partial_application/pipe_optimization_008.phpt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ try {
2626
?>
2727
--EXPECTF--
2828
$_main:
29-
; (lines=19, args=0, vars=1, tmps=2)
29+
; (lines=18, args=0, vars=1, tmps=2)
3030
; (after optimizer)
3131
; %spipe_optimization_008.php:1-16
3232
0000 INIT_FCALL 0 %d string("time")
@@ -36,20 +36,19 @@ $_main:
3636
0004 DECLARE_FUNCTION string("foo") 0
3737
0005 INIT_FCALL_BY_NAME 0 string("foo")
3838
0006 SEND_VAL_EX int(1) string("a")
39-
0007 SEND_PLACEHOLDER
40-
0008 T1 = CALLABLE_CONVERT_PARTIAL %d
41-
0009 INIT_DYNAMIC_CALL 1 T1
42-
0010 SEND_VAL_EX int(2) 1
43-
0011 DO_FCALL
44-
0012 RETURN int(1)
45-
0013 CV0($e) = CATCH string("Throwable")
46-
0014 INIT_METHOD_CALL 0 CV0($e) string("getMessage")
47-
0015 V1 = DO_FCALL
48-
0016 ECHO V1
49-
0017 ECHO string("\n")
50-
0018 RETURN int(1)
39+
0007 T1 = CALLABLE_CONVERT_PARTIAL %d
40+
0008 INIT_DYNAMIC_CALL 1 T1
41+
0009 SEND_VAL_EX int(2) 1
42+
0010 DO_FCALL
43+
0011 RETURN int(1)
44+
0012 CV0($e) = CATCH string("Throwable")
45+
0013 INIT_METHOD_CALL 0 CV0($e) string("getMessage")
46+
0014 V1 = DO_FCALL
47+
0015 ECHO V1
48+
0016 ECHO string("\n")
49+
0017 RETURN int(1)
5150
EXCEPTION TABLE:
52-
0005, 0013, -, -
51+
0005, 0012, -, -
5352

5453
foo:
5554
; (lines=7, args=2, vars=2, tmps=0)

Zend/zend_compile.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3714,12 +3714,13 @@ static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg
37143714

37153715
static uint32_t zend_compile_args(
37163716
zend_ast *ast, const zend_function *fbc,
3717-
bool is_call_partial, bool *may_have_extra_named_args) /* {{{ */
3717+
bool is_call_partial, bool *may_have_extra_named_args,
3718+
bool *uses_variadic_placeholder_p) /* {{{ */
37183719
{
37193720
const zend_ast_list *args = zend_ast_get_list(ast);
37203721
uint32_t i;
37213722
bool uses_arg_unpack = false;
3722-
bool uses_variadic_placeholder = 0;
3723+
bool uses_variadic_placeholder = false;
37233724
uint32_t arg_count = 0; /* number of arguments not including unpacks */
37243725

37253726
/* Whether named arguments are used syntactically, to enforce language level limitations.
@@ -3831,12 +3832,17 @@ static uint32_t zend_compile_args(
38313832
"Cannot combine partial application and unpacking");
38323833
}
38333834

3835+
// TODO: why?
3836+
fbc = NULL;
3837+
38343838
if (arg->attr == _IS_PLACEHOLDER_VARIADIC) {
38353839
uses_variadic_placeholder = true;
3840+
/* Do not emit ZEND_SEND_PLACEHOLDER: We represent the variadic
3841+
* placeholder with a flag on the ZEND_CONVERT_CALLABLE_PARTIAL
3842+
* op instead. */
3843+
continue;
38363844
}
38373845

3838-
fbc = NULL;
3839-
38403846
opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL);
38413847
opline->op1.num = arg->attr;
38423848
if (arg_name) {
@@ -3966,6 +3972,8 @@ static uint32_t zend_compile_args(
39663972
if (may_have_undef) {
39673973
zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL);
39683974
}
3975+
} else {
3976+
*uses_variadic_placeholder_p = uses_variadic_placeholder;
39693977
}
39703978

39713979
return arg_count;
@@ -4006,7 +4014,8 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f
40064014
/* }}} */
40074015

40084016
void zend_compile_call_partial(znode *result, const zend_ast *call_ast,
4009-
uint32_t arg_count, bool may_have_extra_named_args, uint32_t opnum_init,
4017+
uint32_t arg_count, bool may_have_extra_named_args,
4018+
bool uses_variadic_placeholder, uint32_t opnum_init,
40104019
const zend_function *fbc) { /* {{{ */
40114020

40124021
zend_op *init_opline = &CG(active_op_array)->opcodes[opnum_init];
@@ -4027,6 +4036,9 @@ void zend_compile_call_partial(znode *result, const zend_ast *call_ast,
40274036
if (may_have_extra_named_args) {
40284037
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
40294038
}
4039+
if (uses_variadic_placeholder) {
4040+
opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER;
4041+
}
40304042

40314043
/* Collect placeholder order */
40324044
int level = 0;
@@ -4083,6 +4095,8 @@ static bool zend_compile_call_common(znode *result, const zend_ast *call_ast, ze
40834095
bool may_have_extra_named_args;
40844096

40854097
if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) {
4098+
bool uses_variadic_placeholder;
4099+
40864100
opline = &CG(active_op_array)->opcodes[opnum_init];
40874101
opline->extended_value = 0;
40884102
/* opcode array may be reallocated, so don't access opcode field after zend_emit_op_tmp(). */
@@ -4115,16 +4129,17 @@ static bool zend_compile_call_common(znode *result, const zend_ast *call_ast, ze
41154129

41164130
args_ast = ((zend_ast_fcc*)args_ast)->args;
41174131
uint32_t arg_count = zend_compile_args(args_ast, fbc, true,
4118-
&may_have_extra_named_args);
4132+
&may_have_extra_named_args, &uses_variadic_placeholder);
41194133

41204134
zend_compile_call_partial(result, call_ast, arg_count,
4121-
may_have_extra_named_args, opnum_init, fbc);
4135+
may_have_extra_named_args, uses_variadic_placeholder,
4136+
opnum_init, fbc);
41224137

41234138
return true;
41244139
}
41254140

41264141
uint32_t arg_count = zend_compile_args(args_ast, fbc,
4127-
false, &may_have_extra_named_args);
4142+
false, &may_have_extra_named_args, NULL);
41284143

41294144
zend_do_extended_fcall_begin();
41304145

Zend/zend_compile.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
11251125

11261126
#define ZEND_THROW_IS_EXPR 1u
11271127

1128-
#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1
1128+
#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0)
1129+
#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1)
11291130

11301131
/* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */
11311132
#define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT

Zend/zend_partial.c

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ ZEND_COLD static zend_never_inline void zend_partial_args_overflow(
7878

7979
static zend_result zp_args_check(const zend_function *function,
8080
uint32_t argc, const zval *argv,
81-
const zend_array *extra_named_args) {
81+
const zend_array *extra_named_args,
82+
bool uses_variadic_placeholder) {
8283

8384
if (extra_named_args) {
8485
zval *arg;
@@ -93,13 +94,11 @@ static zend_result zp_args_check(const zend_function *function,
9394
} ZEND_HASH_FOREACH_END();
9495
}
9596

96-
/* Z_EXTRA(ZEND_CALL_ARG(call, 1)) is set by ZEND_SEND_PLACEHOLDER */
97-
bool variadic = Z_EXTRA(argv[0]) == _IS_PLACEHOLDER_VARIADIC;
98-
99-
uint32_t num = argc + (variadic ? -1 : 0);
97+
// TODO: should be just 'argc', since the placeholder param doesn't increase argc?
98+
uint32_t num = argc + (uses_variadic_placeholder ? -1 : 0);
10099

101100
if (num < function->common.required_num_args) {
102-
if (variadic) {
101+
if (uses_variadic_placeholder) {
103102
return SUCCESS;
104103
}
105104

@@ -674,7 +673,8 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
674673
uint32_t argc, zval *argv, zend_array *extra_named_params,
675674
const zend_array *named_positions,
676675
const zend_op_array *declaring_op_array,
677-
const zend_op *declaring_opline, void **cache_slot) {
676+
const zend_op *declaring_opline, void **cache_slot,
677+
bool uses_variadic_placeholder) {
678678

679679
zend_op_array *op_array = NULL;
680680

@@ -684,7 +684,7 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
684684
return NULL;
685685
}
686686

687-
if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params) != SUCCESS)) {
687+
if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) {
688688
ZEND_ASSERT(EG(exception));
689689
return NULL;
690690
}
@@ -702,16 +702,14 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
702702
int orig_lineno = CG(zend_lineno);
703703
CG(zend_lineno) = zend_get_executed_lineno();
704704

705-
bool variadic_partial = false;
706705
int new_argc = argc;
707706

708-
/* Z_EXTRA(ZEND_CALL_ARG(call, 1)) is set in ZEND_SEND_PLACEHOLDER */
709-
if (Z_EXTRA(argv[0]) == _IS_PLACEHOLDER_VARIADIC) {
710-
variadic_partial = true;
707+
if (uses_variadic_placeholder) {
711708
new_argc = MAX(new_argc, function->common.num_args);
712709
}
713710

714711
zval *tmp = zend_arena_alloc(&CG(arena), new_argc * sizeof(zval));
712+
// TODO: should be argc * sizeof(zval) ?
715713
memcpy(tmp, argv, new_argc * sizeof(zval));
716714
argv = tmp;
717715

@@ -754,7 +752,7 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
754752
offset, param_offset, num_required);
755753
}
756754

757-
if (variadic_partial) {
755+
if (uses_variadic_placeholder) {
758756
/* Handle implicit placeholders added by '...' */
759757
for (uint32_t offset = 0; offset < new_argc; offset++) {
760758
if (offset < argc && !Z_ISUNDEF(argv[offset])) {
@@ -780,13 +778,13 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
780778

781779
/* Assign variable names */
782780

783-
uint32_t num_names = argc + variadic_partial + (extra_named_params != NULL)
781+
uint32_t num_names = argc + uses_variadic_placeholder + (extra_named_params != NULL)
784782
+ ((function->common.fn_flags & ZEND_ACC_CLOSURE) != 0);
785783
zend_string **param_names = zend_arena_calloc(&CG(arena),
786784
num_names, sizeof(zend_string*));
787785
memset(param_names, 0, sizeof(zend_string*) * num_names);
788786
zp_assign_names(param_names, num_names, argc, argv, function,
789-
variadic_partial, extra_named_params);
787+
uses_variadic_placeholder, extra_named_params);
790788

791789
/* Generate AST */
792790

@@ -801,7 +799,7 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
801799
/* Must be first, as this is assumed in do_closure_bind() */
802800
if (function->common.fn_flags & ZEND_ACC_CLOSURE) {
803801
zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
804-
zend_string_copy(param_names[argc + variadic_partial + (extra_named_params != NULL)]));
802+
zend_string_copy(param_names[argc + uses_variadic_placeholder + (extra_named_params != NULL)]));
805803
lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast);
806804
}
807805

@@ -858,13 +856,13 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
858856

859857
if (extra_named_params) {
860858
zend_ast *lexical_var_ast = zend_ast_create_zval_from_str(
861-
zend_string_copy(param_names[argc + variadic_partial]));
859+
zend_string_copy(param_names[argc + uses_variadic_placeholder]));
862860
lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast);
863861
}
864862

865863
/* If we have a variadic placeholder and the underlying function is
866864
* variadic, add a variadic param. */
867-
if (variadic_partial
865+
if (uses_variadic_placeholder
868866
&& (function->common.fn_flags & ZEND_ACC_VARIADIC)) {
869867
zend_arg_info *arg_info = &function->common.arg_info[function->common.num_args];
870868
int param_flags = ZEND_PARAM_VARIADIC;
@@ -901,13 +899,13 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
901899
* The func_num_args() call should be compiled to a single FUNC_NUM_ARGS op.
902900
*/
903901

904-
if (variadic_partial && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) {
902+
if (uses_variadic_placeholder && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) {
905903
zend_ast *no_forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST);
906904
zend_ast *forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST);
907905

908906
no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function,
909907
argc, argv, extra_named_params,
910-
param_names, variadic_partial, num_params,
908+
param_names, uses_variadic_placeholder, num_params,
911909
called_scope, return_type, false, no_forwarding_ast);
912910

913911
if (!no_forwarding_ast) {
@@ -917,7 +915,7 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
917915

918916
forwarding_ast = zp_compile_forwarding_call(this_ptr, function,
919917
argc, argv, extra_named_params,
920-
param_names, variadic_partial, num_params,
918+
param_names, uses_variadic_placeholder, num_params,
921919
called_scope, return_type, true, forwarding_ast);
922920

923921
if (!forwarding_ast) {
@@ -944,7 +942,7 @@ zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
944942
} else {
945943
stmts_ast = zp_compile_forwarding_call(this_ptr, function,
946944
argc, argv, extra_named_params,
947-
param_names, variadic_partial, num_params,
945+
param_names, uses_variadic_placeholder, num_params,
948946
called_scope, return_type, false, stmts_ast);
949947

950948
if (!stmts_ast) {
@@ -1024,7 +1022,8 @@ zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function,
10241022
uint32_t argc, zval *argv, zend_array *extra_named_params,
10251023
const zend_array *named_positions,
10261024
const zend_op_array *declaring_op_array,
1027-
const zend_op *declaring_opline, void **cache_slot) {
1025+
const zend_op *declaring_opline, void **cache_slot,
1026+
bool uses_variadic_placeholder) {
10281027

10291028
if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION
10301029
? cache_slot[0] == function
@@ -1038,7 +1037,7 @@ zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function,
10381037
if (UNEXPECTED(!op_array)) {
10391038
op_array = zp_compile(this_ptr, function, argc, argv,
10401039
extra_named_params, named_positions, declaring_op_array, declaring_opline,
1041-
cache_slot);
1040+
cache_slot, uses_variadic_placeholder);
10421041
}
10431042

10441043
if (EXPECTED(op_array)) {
@@ -1103,12 +1102,13 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
11031102
uint32_t argc, zval *argv, zend_array *extra_named_params,
11041103
const zend_array *named_positions,
11051104
const zend_op_array *declaring_op_array,
1106-
const zend_op *declaring_opline, void **cache_slot) {
1105+
const zend_op *declaring_opline, void **cache_slot,
1106+
bool uses_variadic_placeholder) {
11071107

11081108
zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv,
11091109
extra_named_params, named_positions,
11101110
declaring_op_array, declaring_opline,
1111-
cache_slot);
1111+
cache_slot, uses_variadic_placeholder);
11121112

11131113
if (UNEXPECTED(!op_array)) {
11141114
ZEND_ASSERT(EG(exception));

Zend/zend_partial.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
2727
uint32_t argc, zval *argv, zend_array *extra_named_params,
2828
const zend_array *named_positions,
2929
const zend_op_array *declaring_op_array,
30-
const zend_op *declaring_opline, void **cache_slot);
30+
const zend_op *declaring_opline, void **cache_slot,
31+
bool uses_variadic_placeholder);
3132

3233
void zend_partial_op_array_dtor(zval *pDest);
3334

Zend/zend_vm_def.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9258,10 +9258,6 @@ ZEND_VM_HANDLER(211, ZEND_SEND_PLACEHOLDER, UNUSED, CONST|UNUSED)
92589258
ZEND_ASSERT(opline->op1.num == _IS_PLACEHOLDER_ARG);
92599259
Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER_ARG;
92609260
Z_EXTRA_P(ZEND_CALL_ARG(call, 1)) = 0;
9261-
} else if (opline->op1.num == _IS_PLACEHOLDER_VARIADIC) {
9262-
/* TODO: maybe find another way to pass the variadic flag, as this
9263-
* requires to initialize this slot for each placeholder. */
9264-
Z_EXTRA_P(ZEND_CALL_ARG(call, 1)) = _IS_PLACEHOLDER_VARIADIC;
92659261
} else {
92669262
ZEND_ASSERT(opline->op1.num == _IS_PLACEHOLDER_ARG);
92679263
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
@@ -9794,6 +9790,7 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT)
97949790
ZEND_VM_NEXT_OPCODE();
97959791
}
97969792

9793+
// TODO: verify handler flags. We have a cache slot on op1, some flags on extended_value
97979794
ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, NUM, CONST|UNUSED, CACHE_SLOT)
97989795
{
97999796
USE_OPLINE
@@ -9809,7 +9806,8 @@ ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, NUM, CONST|UNUSED, CACHE_SLO
98099806
ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS ?
98109807
call->extra_named_params : NULL,
98119808
OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
9812-
&EX(func)->op_array, opline, cache_slot);
9809+
&EX(func)->op_array, opline, cache_slot,
9810+
opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER);
98139811

98149812
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
98159813
zend_array_release(call->extra_named_params);

ext/opcache/jit/zend_jit_ir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10078,7 +10078,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
1007810078
}
1007910079

1008010080
bool may_have_extra_named_params =
10081-
opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
10081+
(opline->extended_value & ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS) &&
1008210082
(!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
1008310083

1008410084
if (!jit->reuse_ip) {

0 commit comments

Comments
 (0)