Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Use the same zend_arg_info struct for internal and user functions #19022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
arnaud-lb wants to merge 11 commits into php:master
base: master
Choose a base branch
Loading
from arnaud-lb:zstr-arg-info

Conversation

Copy link
Member

@arnaud-lb arnaud-lb commented Jul 3, 2025
edited
Loading

Internal functions use char* strings to represent arg names and default values. This differs from user functions, which use zend strings.

Here I unify this by using the same struct, zend_arg_info in both function types.

This simplifies accesses to arg infos. Also, in the PFAs RFC this avoids converting internal arg infos at runtime when applying an internal function.

Copy link
Member

@Girgias Girgias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for having a look at this, I'm overall in favour of unifying those, as it makes it easier to reason about.

See some of my comments, which are mostly nits. :)

arnaud-lb reacted with thumbs up emoji
@@ -790,17 +785,15 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
if (ZEND_ARG_IS_VARIADIC(arg_info)) {
smart_str_appends(str, "...");
}
smart_str_append_printf(str, "$%s", has_internal_arg_info(fptr)
? ((zend_internal_arg_info*)arg_info)->name : ZSTR_VAL(arg_info->name));
smart_str_append_printf(str, "$%s", ZSTR_VAL(arg_info->name));
Copy link
Member

@Girgias Girgias Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do this with a printf? Can't we just append a char and then the zend_string?

Copy link
Member Author

@arnaud-lb arnaud-lb Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, but I want to avoid changes that are not directly related

Copy link
Member

@Girgias Girgias Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK, let's keep this for a follow-up then :)

Comment on lines 36 to 39
if (arginfo) {
if (func->type == ZEND_INTERNAL_FUNCTION) {
arg_name = (char *) ((zend_internal_arg_info *) &arginfo[i])->name;
} else {
arg_name = ZSTR_VAL(arginfo[i].name);
}
arg_name = ZSTR_VAL(arginfo[i].name);
}
smart_str_appends(s, arg_name ? arg_name : "?");
Copy link
Member

@Girgias Girgias Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be improved to prevent a strlen computation within the smart_str_appends.

Copy link
Member

nielsdos commented Jul 3, 2025

You can probably get rid of ZEND_ACC_USER_ARG_INFO

Copy link
Member Author

Right :) I wanted to do it, but it's not trivial because the flag is also used to signal how arg default values should be fetched. I may try to remove it later.

@arnaud-lb arnaud-lb marked this pull request as ready for review July 3, 2025 16:16
#endif
return get_default_via_ast(default_value_zval, default_value);
return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value));
Copy link
Member

@Girgias Girgias Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a follow-up, the get_default_via_ast() function should be changed to accept a zend_string to get rid of an unnecessary strlen() computation :)

arnaud-lb reacted with thumbs up emoji
Copy link
Member

nielsdos commented Jul 3, 2025

This does seem to have a negative impact on the Valgrind instruction counts though. Probably because of the slightly higher memory requirement if you have the zend_string header too.

@@ -790,17 +785,15 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
if (ZEND_ARG_IS_VARIADIC(arg_info)) {
smart_str_appends(str, "...");
}
smart_str_append_printf(str, "$%s", has_internal_arg_info(fptr)
? ((zend_internal_arg_info*)arg_info)->name : ZSTR_VAL(arg_info->name));
smart_str_append_printf(str, "$%s", ZSTR_VAL(arg_info->name));

if (!required && !ZEND_ARG_IS_VARIADIC(arg_info)) {
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
smart_str_appends(str, " = ");
/* TODO: We don't have a way to fetch the default value for an internal function
Copy link
Member

@DanielEScherzer DanielEScherzer Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment still correct?

Copy link
Member Author

@arnaud-lb arnaud-lb Jul 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is still correct. This is also related to why we can not remove ZEND_ACC_USER_ARG_INFO immediately.

Copy link
Member Author

@nielsdos I believe this is purely startup/shutdown overhead. When ignoring startup/shutdown, I see absolutely zero degradation under valgrind.

nielsdos reacted with thumbs up emoji

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't object.
This wasn't made before to avoid the waste of memory.
Now names are going to be duplicated from .text to heap and shm.
This is not a big problem, but it's better to measure this in some way.

arnaud-lb reacted with thumbs up emoji
Copy link
Member Author

I've measured zero regression on wall time or valgrind icount on the symfony benchmark. I will measure the memory impact once I get back from vacations.

NB: I've also tried declaring zend strings in static storage, but there are a number of complications that make the current solution more practicable. The most annoying one is that we can't initialize a zend string directly due to the variable size member.

Copy link
Member

dstogov commented Jul 7, 2025

NB: I've also tried declaring zend strings in static storage, but there are a number of complications that make the current solution more practicable. The most annoying one is that we can't initialize a zend string directly due to the variable size member.

I know. I tried this years ago and gave up :)

arnaud-lb reacted with laugh emoji

Copy link
Member Author

I don't object. This wasn't made before to avoid the waste of memory. Now names are going to be duplicated from .text to heap and shm. This is not a big problem, but it's better to measure this in some way.

With a realistic set of extensions: Core,ctype,date,dom,FFI,fileinfo,filter,gmp,hash,iconv,intl,json,lexbor,libxml,mbstring,mysqli,mysqlnd,openssl,pcre,PDO,pdo_mysql,pdo_sqlite,Phar,posix,random,Reflection,session,SimpleXML,sockets,SPL,sqlite3,standard,tokenizer,uri,xml,xmlreader,xmlwriter,Zend OPcache,zend_test,zlib

I measured the memory usage and interned strings usage when running the following script:

readfile("/proc/self/status");
var_dump(opcache_get_status()["interned_strings_usage"]);
Metric Before After Change
VmPeak 514684 kB 515032 kB +348 kB (+0.07%)
VmSize 514684 kB 515032 kB +348 kB (+0.07%)
VmHWM 26076 kB 26284 kB +208 kB (+0.80%)
VmRSS 26076 kB 26284 kB +208 kB (+0.80%)
VmData 7584 kB 7936 kB +352 kB (+4.64%)
VmStk 132 kB 132 kB +0 kB (+0.00%)
VmExe 5772 kB 5768 kB -4 kB (-0.07%)
VmLib 15720 kB 15720 kB +0 kB (+0.00%)
VmPTE 168 kB 172 kB +4 kB (+2.38%)

Opcache interned strings:

Metric Before After Change
buffer_size 8192 kB 8192 kB +0 kB (+0.00%)
used_memory 2403.03 kB 2428.09 kB +25 kB (+1.04%)
free_memory 5788.97 kB 5763.91 kB -25 kB (-0.43%)
number_of_strings 7693 8286 +593 (+7.71%)

The relative increase of VmData is not negligible, but the absolute difference (352 KiB) is not really high when considering this is likely to be shared between processes. And this is a "fixed" overhead: This does not grow proportionally to the program size or its own memory usage.

Given there is zero time or instruction overhead in the symfony benchmark, this seems acceptable to me.

@@ -929,8 +929,11 @@ ZEND_API bool zend_is_iterable(const zval *iterable);

ZEND_API bool zend_is_countable(const zval *countable);

ZEND_API zend_result zend_get_default_from_internal_arg_info(
zval *default_value_zval, zend_internal_arg_info *arg_info);
void zend_convert_internal_arg_info(zend_arg_info *new_arg_info,
Copy link
Member

@iluuu1994 iluuu1994 Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be ZEND_API if used from extensions (e.g. ext/pdo/pdo_dbh.c).

type->type_mask |= _ZEND_TYPE_NAME_BIT;
} else {
/* Union type */
zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
Copy link
Member

@iluuu1994 iluuu1994 Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Use pemalloc for system OOM safety.

if (ZEND_TYPE_HAS_LITERAL_NAME(*type)) {
// gen_stubs.php does not support codegen for DNF types in arg infos.
// As a temporary workaround, we split the type name on `|` characters,
// converting it to an union type if necessary.
Copy link
Member

@iluuu1994 iluuu1994 Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this comment. It's referring to DNF but is handling only union types.

Edit: I see this comes from a moved code block. Would still be nice to clarify. /cc @Girgias

@@ -5371,49 +5390,44 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) {
return zend_string_init(str, len, 0);
}

ZEND_API zend_result zend_get_default_from_internal_arg_info(
zval *default_value_zval, zend_internal_arg_info *arg_info)
ZEND_API zend_result zend_get_default_from_arg_info(
Copy link
Member

@iluuu1994 iluuu1994 Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I realize this was renamed to match the argument, but this is still used only for internal function arg infos. So maybe the existing name still fits?

Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the unification, and it looks reasonable to me. Should probably be delayed for 8.6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Reviewers

@TimWolla TimWolla TimWolla left review comments

@dstogov dstogov dstogov left review comments

@Girgias Girgias Girgias left review comments

@DanielEScherzer DanielEScherzer DanielEScherzer left review comments

@iluuu1994 iluuu1994 iluuu1994 approved these changes

@SakiTakamachi SakiTakamachi Awaiting requested review from SakiTakamachi SakiTakamachi is a code owner

Assignees
No one assigned
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /