Skip to content

Commit a6e220e

Browse files
committed
zend_partial_create(): Remove the dependency on the declaring op_array
The declaring op_array is used for multiple purposes: * Generating a PFA name * Finding the filename * Finding whether op_array is cached, and therefore whether the PFA should be cached too However it's not available when evaluating a PFA AST. Remove the declaring_op_array argument, and pass the relevant information directly: * Generate the PFA name at compile time, pass it as argument to zend_partial_create() * Pass the filename and a cacheable flag as argument to zend_partial_create()
1 parent 8747a24 commit a6e220e

20 files changed

Lines changed: 358 additions & 274 deletions

Zend/Optimizer/compact_literals.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "zend_execute.h"
2727
#include "zend_vm.h"
2828
#include "zend_extensions.h"
29+
#include "zend_partial.h"
2930

3031
#define DEBUG_COMPACT_LITERALS 0
3132

@@ -747,7 +748,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
747748
}
748749
break;
749750
case ZEND_CALLABLE_CONVERT_PARTIAL:
750-
opline->op1.num = cache_size;
751+
opline->extended_value = cache_size | (opline->extended_value & ZEND_PARTIAL_FLAGS);
751752
cache_size += 2 * sizeof(void *);
752753
break;
753754
}

Zend/tests/partial_application/pipe_optimization_004.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ $_main:
3737
0005 INIT_FCALL_BY_NAME 2 string("foo")
3838
0006 SEND_PLACEHOLDER 1
3939
0007 SEND_PLACEHOLDER 2
40-
0008 T1 = CALLABLE_CONVERT_PARTIAL %d
40+
0008 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
4141
0009 INIT_DYNAMIC_CALL 1 T1
4242
0010 SEND_VAL_EX int(2) 1
4343
0011 DO_FCALL

Zend/tests/partial_application/pipe_optimization_007.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ $_main:
3737
0005 INIT_FCALL_BY_NAME 0 string("foo")
3838
0006 SEND_PLACEHOLDER string("a")
3939
0007 SEND_PLACEHOLDER string("b")
40-
0008 T1 = CALLABLE_CONVERT_PARTIAL %d array(...)
40+
0008 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") array(...)
4141
0009 INIT_DYNAMIC_CALL 1 T1
4242
0010 SEND_VAL_EX int(2) 1
4343
0011 DO_FCALL

Zend/tests/partial_application/pipe_optimization_008.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ $_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 T1 = CALLABLE_CONVERT_PARTIAL %d
39+
0007 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
4040
0008 INIT_DYNAMIC_CALL 1 T1
4141
0009 SEND_VAL_EX int(2) 1
4242
0010 DO_FCALL

Zend/tests/partial_application/pipe_optimization_013.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ $_main:
3232
0004 DECLARE_FUNCTION string("foo") 0
3333
0005 INIT_FCALL_BY_NAME 0 string("foo")
3434
0006 SEND_VAL_EX int(2) string("b")
35-
0007 T0 = CALLABLE_CONVERT_PARTIAL 3
35+
0007 T0 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
3636
0008 INIT_DYNAMIC_CALL 1 T0
3737
0009 SEND_VAL_EX int(1) 1
3838
0010 DO_FCALL

Zend/zend_ast.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args) {
6060
ast->kind = ZEND_AST_CALLABLE_CONVERT;
6161
ast->attr = 0;
6262
ast->lineno = CG(zend_lineno);
63+
ast->filename = NULL;
64+
ast->name = NULL;
6365
ast->args = args;
6466
ZEND_MAP_PTR_INIT(ast->fptr, NULL);
6567

@@ -1425,6 +1427,16 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
14251427
new->kind = old->kind;
14261428
new->attr = old->attr;
14271429
new->lineno = old->lineno;
1430+
if (old->filename) {
1431+
new->filename = zend_string_copy(old->filename);
1432+
} else {
1433+
new->filename = NULL;
1434+
}
1435+
if (old->name) {
1436+
new->name = zend_string_copy(old->name);
1437+
} else {
1438+
new->name = NULL;
1439+
}
14281440
ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr));
14291441
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
14301442
new->args = buf;
@@ -1525,6 +1537,12 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
15251537
} else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) {
15261538
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
15271539

1540+
if (fcc_ast->filename) {
1541+
zend_string_release_ex(fcc_ast->filename, 0);
1542+
}
1543+
if (fcc_ast->name) {
1544+
zend_string_release_ex(fcc_ast->name, 0);
1545+
}
15281546
ast = fcc_ast->args;
15291547
goto tail_call;
15301548
}

Zend/zend_ast.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ typedef struct _zend_ast_fcc {
233233
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
234234
zend_ast_attr attr; /* Additional attribute, use depending on node type */
235235
uint32_t lineno; /* Line number */
236+
zend_string *filename;
237+
zend_string *name;
236238
zend_ast *args;
237239
ZEND_MAP_PTR_DEF(zend_function *, fptr);
238240
} zend_ast_fcc;

Zend/zend_compile.c

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "zend_call_stack.h"
4141
#include "zend_frameless_function.h"
4242
#include "zend_property_hooks.h"
43+
#include "zend_partial.h"
4344

4445
#define SET_NODE(target, src) do { \
4546
target ## _type = (src)->op_type; \
@@ -4088,7 +4089,50 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f
40884089
}
40894090
/* }}} */
40904091

4091-
static void zend_compile_call_partial(znode *result, uint32_t arg_count,
4092+
static zend_string *zend_compile_partial_name(const zend_op_array *declaring_op_array,
4093+
const uint32_t declaring_lineno)
4094+
{
4095+
/* We attempt to generate a name that hints at where the PFA was created,
4096+
* similarly to Closures (GH-13550).
4097+
* We do not attempt to make the name unique. */
4098+
4099+
zend_string *filename = declaring_op_array->filename;
4100+
uint32_t start_lineno = declaring_lineno;
4101+
4102+
zend_string *class = zend_empty_string;
4103+
zend_string *separator = zend_empty_string;
4104+
zend_string *function = filename;
4105+
const char *parens = "";
4106+
4107+
if (declaring_op_array->function_name) {
4108+
function = declaring_op_array->function_name;
4109+
if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) {
4110+
/* If the parent function is a closure, don't redundantly
4111+
* add the classname and parentheses. */
4112+
} else {
4113+
parens = "()";
4114+
4115+
if (declaring_op_array->scope && declaring_op_array->scope->name) {
4116+
class = declaring_op_array->scope->name;
4117+
separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM);
4118+
}
4119+
}
4120+
}
4121+
4122+
zend_string *name = zend_strpprintf_unchecked(
4123+
0,
4124+
"{closure:pfa:%S%S%S%s:%" PRIu32 "}",
4125+
class,
4126+
separator,
4127+
function,
4128+
parens,
4129+
start_lineno
4130+
);
4131+
4132+
return name;
4133+
}
4134+
4135+
static void zend_compile_call_partial(znode *result, zend_ast_fcc *fcc_ast, uint32_t arg_count,
40924136
bool may_have_extra_named_args, bool uses_variadic_placeholder,
40934137
zval *named_positions, uint32_t opnum_init, const zend_function *fbc) {
40944138

@@ -4105,15 +4149,16 @@ static void zend_compile_call_partial(znode *result, uint32_t arg_count,
41054149
zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL,
41064150
NULL, NULL);
41074151

4108-
opline->op1.num = zend_alloc_cache_slots(2);
4152+
opline->extended_value = zend_alloc_cache_slots(2);
41094153

4110-
if (may_have_extra_named_args) {
4111-
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
4112-
}
41134154
if (uses_variadic_placeholder) {
4114-
opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER;
4155+
opline->extended_value |= ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER;
41154156
}
41164157

4158+
zend_string *name = zend_compile_partial_name(CG(active_op_array), opline->lineno);
4159+
opline->op1.constant = zend_add_literal_string(&name);
4160+
opline->op1_type = IS_CONST;
4161+
41174162
if (!Z_ISUNDEF_P(named_positions)) {
41184163
opline->op2.constant = zend_add_literal(named_positions);
41194164
opline->op2_type = IS_CONST;
@@ -4156,7 +4201,8 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze
41564201
return true;
41574202
}
41584203

4159-
args_ast = ((zend_ast_fcc*)args_ast)->args;
4204+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)args_ast;
4205+
args_ast = fcc_ast->args;
41604206

41614207
bool may_have_extra_named_args;
41624208
bool uses_variadic_placeholder;
@@ -4168,7 +4214,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze
41684214
&may_have_extra_named_args, true, &uses_variadic_placeholder,
41694215
&named_positions);
41704216

4171-
zend_compile_call_partial(result, arg_count,
4217+
zend_compile_call_partial(result, fcc_ast, arg_count,
41724218
may_have_extra_named_args, uses_variadic_placeholder,
41734219
&named_positions, opnum_init, fbc);
41744220

@@ -12031,10 +12077,14 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
1203112077
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
1203212078
}
1203312079

12034-
zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args);
12035-
if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) {
12036-
// TODO: PFAs
12037-
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
12080+
zend_ast_fcc *fcc = (zend_ast_fcc*)args_ast;
12081+
12082+
zend_ast_list *args = zend_ast_get_list(fcc->args);
12083+
bool is_fcc = args->children == 1 && args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC;
12084+
12085+
if (!is_fcc) {
12086+
fcc->filename = zend_string_copy(CG(active_op_array)->filename);
12087+
fcc->name = zend_compile_partial_name(CG(active_op_array), fcc->lineno);
1203812088
}
1203912089

1204012090
switch ((*ast_ptr)->kind) {

Zend/zend_compile.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,6 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
11211121
#define ZEND_THROW_IS_EXPR 1u
11221122

11231123
#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0)
1124-
#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1)
11251124

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

Zend/zend_partial.c

Lines changed: 19 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "zend_closures.h"
5252
#include "zend_attributes.h"
5353
#include "zend_exceptions.h"
54+
#include "zend_partial.h"
5455
#include "ext/opcache/ZendAccelerator.h"
5556

5657
static zend_always_inline bool Z_IS_PLACEHOLDER_P(const zval *p) {
@@ -502,49 +503,6 @@ static zend_ast *zp_param_attributes_to_ast(zend_function *function,
502503
return attributes_ast;
503504
}
504505

505-
static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array,
506-
const uint32_t *declaring_lineno_ptr)
507-
{
508-
/* We attempt to generate a name that hints at where the PFA was created,
509-
* similarly to Closures (GH-13550).
510-
* We do not attempt to make the name unique. */
511-
512-
zend_string *filename = declaring_op_array->filename;
513-
uint32_t start_lineno = *declaring_lineno_ptr;
514-
515-
zend_string *class = zend_empty_string;
516-
zend_string *separator = zend_empty_string;
517-
zend_string *function = filename;
518-
const char *parens = "";
519-
520-
if (declaring_op_array->function_name) {
521-
function = declaring_op_array->function_name;
522-
if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) {
523-
/* If the parent function is a closure, don't redundantly
524-
* add the classname and parentheses. */
525-
} else {
526-
parens = "()";
527-
528-
if (declaring_op_array->scope && declaring_op_array->scope->name) {
529-
class = declaring_op_array->scope->name;
530-
separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM);
531-
}
532-
}
533-
}
534-
535-
zend_string *name = zend_strpprintf_unchecked(
536-
0,
537-
"{closure:pfa:%S%S%S%s:%" PRIu32 "}",
538-
class,
539-
separator,
540-
function,
541-
parens,
542-
start_lineno
543-
);
544-
545-
return name;
546-
}
547-
548506
/* Generate the AST for calling the actual function */
549507
static zend_ast *zp_compile_forwarding_call(
550508
zval *this_ptr, zend_function *function,
@@ -701,9 +659,9 @@ static uint32_t zp_compute_num_required(const zend_function *function,
701659
static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
702660
uint32_t argc, zval *argv, zend_array *extra_named_params,
703661
const zend_array *named_positions,
704-
const zend_op_array *declaring_op_array,
662+
zend_string *declaring_filename,
705663
const uint32_t *declaring_lineno_ptr, void **cache_slot,
706-
bool uses_variadic_placeholder) {
664+
zend_string *pfa_name, uint32_t flags) {
707665

708666
zend_op_array *op_array = NULL;
709667

@@ -713,6 +671,8 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
713671
return NULL;
714672
}
715673

674+
bool uses_variadic_placeholder = flags & ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER;
675+
716676
if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) {
717677
ZEND_ASSERT(EG(exception));
718678
return NULL;
@@ -1012,10 +972,8 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
1012972
}
1013973
#endif
1014974

1015-
zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_lineno_ptr);
1016-
1017-
op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array,
1018-
declaring_lineno_ptr, function, pfa_name);
975+
op_array = zend_accel_compile_pfa(closure_ast, declaring_filename,
976+
declaring_lineno_ptr, function, pfa_name, flags & ZEND_PARTIAL_CACHEABLE_IN_SHM);
1019977

1020978
zend_ast_destroy(closure_ast);
1021979

@@ -1040,9 +998,9 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
1040998
static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function,
1041999
uint32_t argc, zval *argv, zend_array *extra_named_params,
10421000
const zend_array *named_positions,
1043-
const zend_op_array *declaring_op_array,
1001+
zend_string *declaring_filename,
10441002
const uint32_t *declaring_lineno_ptr, void **cache_slot,
1045-
bool uses_variadic_placeholder) {
1003+
zend_string *pfa_name, uint32_t flags) {
10461004

10471005
if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION
10481006
? cache_slot[0] == function
@@ -1051,13 +1009,13 @@ static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *funct
10511009
return cache_slot[1];
10521010
}
10531011

1054-
const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array,
1055-
declaring_lineno_ptr, function);
1012+
const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_lineno_ptr, function,
1013+
flags & ZEND_PARTIAL_CACHEABLE_IN_SHM);
10561014

10571015
if (UNEXPECTED(!op_array)) {
10581016
op_array = zp_compile(this_ptr, function, argc, argv,
1059-
extra_named_params, named_positions, declaring_op_array, declaring_lineno_ptr,
1060-
cache_slot, uses_variadic_placeholder);
1017+
extra_named_params, named_positions, declaring_filename, declaring_lineno_ptr,
1018+
cache_slot, pfa_name, flags);
10611019
}
10621020

10631021
if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) {
@@ -1135,14 +1093,16 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *
11351093
void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
11361094
uint32_t argc, zval *argv, zend_array *extra_named_params,
11371095
const zend_array *named_positions,
1138-
const zend_op_array *declaring_op_array,
1096+
zend_string *declaring_filename,
11391097
const uint32_t *declaring_lineno_ptr, void **cache_slot,
1140-
bool uses_variadic_placeholder) {
1098+
zend_string *pfa_name, uint32_t flags) {
1099+
1100+
ZEND_ASSERT(pfa_name);
11411101

11421102
const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv,
11431103
extra_named_params, named_positions,
1144-
declaring_op_array, declaring_lineno_ptr,
1145-
cache_slot, uses_variadic_placeholder);
1104+
declaring_filename, declaring_lineno_ptr,
1105+
cache_slot, pfa_name, flags);
11461106

11471107
if (UNEXPECTED(!op_array)) {
11481108
ZEND_ASSERT(EG(exception));

0 commit comments

Comments
 (0)