From a434a7797f3fc2a72f08194381fd6846fb161d19 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 4 Aug 2025 02:37:16 +0100 Subject: [PATCH] ZPP: Reduce code bloat for Z_PARAM_STR This idea is based on @nielsdos previous PR to reduce codebloat: https://github.com/php/php-src/pull/18436 To do so we create a specialized function to handle parameters that only accept strings and not null such that we can assign the zend_string to the destination directly. --- Zend/zend_API.c | 30 ++++++++++++++-------------- Zend/zend_API.h | 36 +++++++++++++++++++++++++++------- Zend/zend_execute.c | 3 +-- Zend/zend_frameless_function.h | 2 +- Zend/zend_vm_def.h | 4 ++-- Zend/zend_vm_execute.h | 12 ++++++------ 6 files changed, 54 insertions(+), 33 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 11fa2e740cb7e..8dc4c110d9f06 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -755,58 +755,58 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval ** return true; } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("string", arg_num)) { - return 0; + return NULL; } convert_to_string(arg); - *dest = Z_STR_P(arg); + return Z_STR_P(arg); } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(arg); zval obj; if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) { OBJ_RELEASE(zobj); ZVAL_COPY_VALUE(arg, &obj); - *dest = Z_STR_P(arg); - return 1; + return Z_STR_P(arg); } - return 0; + return NULL; } else { - return 0; + return NULL; } - return 1; } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num) /* {{{ */ { if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { - return 0; + return NULL; } - return zend_parse_arg_str_weak(arg, dest, arg_num); + return zend_parse_arg_str_weak(arg, arg_num); } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) +ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num) { if (UNEXPECTED(ZEND_FLF_ARG_USES_STRICT_TYPES())) { - return 0; + return NULL; } - return zend_parse_arg_str_weak(arg, dest, arg_num); + return zend_parse_arg_str_weak(arg, arg_num); } ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num) /* {{{ */ { + zend_string *str; if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { return 0; } if (zend_parse_arg_long_weak(arg, dest_long, arg_num)) { *dest_str = NULL; return 1; - } else if (zend_parse_arg_str_weak(arg, dest_str, arg_num)) { + } else if ((str = zend_parse_arg_str_weak(arg, arg_num)) != NULL) { *dest_long = 0; + *dest_str = str; return 1; } else { return 0; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 78a30d630ae6e..e6d17cf29bb6e 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -2088,7 +2088,13 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string } #define Z_PARAM_STR(dest) \ - Z_PARAM_STR_EX(dest, 0, 0) + Z_PARAM_PROLOGUE(false, false); \ + dest = zend_parse_arg_str_no_null(_arg, _i); \ + if (UNEXPECTED(dest == NULL)) { \ + _expected_type = Z_EXPECTED_STRING; \ + _error_code = ZPP_ERROR_WRONG_ARG; \ + break; \ + } #define Z_PARAM_STR_OR_NULL(dest) \ Z_PARAM_STR_EX(dest, 1, 0) @@ -2183,14 +2189,14 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(const zval *arg, zend_long ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num); static zend_always_inline bool zend_parse_arg_bool_ex(const zval *arg, bool *dest, bool *is_null, bool check_null, uint32_t arg_num, bool frameless) @@ -2292,10 +2298,16 @@ static zend_always_inline bool zend_parse_arg_str_ex(zval *arg, zend_string **de } else if (check_null && Z_TYPE_P(arg) == IS_NULL) { *dest = NULL; } else { + zend_string *str; if (frameless) { - return zend_flf_parse_arg_str_slow(arg, dest, arg_num); + str = zend_flf_parse_arg_str_slow(arg, arg_num); } else { - return zend_parse_arg_str_slow(arg, dest, arg_num); + str = zend_parse_arg_str_slow(arg, arg_num); + } + if (str) { + *dest = str; + } else { + return 0; } } return 1; @@ -2306,6 +2318,15 @@ static zend_always_inline bool zend_parse_arg_str(zval *arg, zend_string **dest, return zend_parse_arg_str_ex(arg, dest, check_null, arg_num, /* frameless */ false); } +static zend_always_inline zend_string *zend_parse_arg_str_no_null(zval *arg, uint32_t arg_num) +{ + if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { + return Z_STR_P(arg); + } else { + return zend_parse_arg_str_slow(arg, arg_num); + } +} + static zend_always_inline bool zend_parse_arg_string(zval *arg, char **dest, size_t *dest_len, bool check_null, uint32_t arg_num) { zend_string *str; @@ -2529,7 +2550,8 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_str( *dest_str = NULL; } else { *dest_ht = NULL; - return zend_parse_arg_str_slow(arg, dest_str, arg_num); + *dest_str = zend_parse_arg_str_slow(arg, arg_num); + return *dest_str != NULL; } return 1; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 3908299a41c3f..5fca2821cf0f6 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -734,7 +734,6 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) { zend_long lval; double dval; - zend_string *str; bool bval; /* Type preference order: int -> float -> string -> bool */ @@ -766,7 +765,7 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) ZVAL_DOUBLE(arg, dval); return 1; } - if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, &str, 0)) { + if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, 0)) { /* on success "arg" is converted to IS_STRING */ return 1; } diff --git a/Zend/zend_frameless_function.h b/Zend/zend_frameless_function.h index d64ca7ee15e2f..6f2a987e88195 100644 --- a/Zend/zend_frameless_function.h +++ b/Zend/zend_frameless_function.h @@ -65,7 +65,7 @@ dest_ht = NULL; \ ZVAL_COPY(&str_tmp, arg ## arg_num); \ arg ## arg_num = &str_tmp; \ - if (!zend_flf_parse_arg_str_slow(arg ## arg_num, &dest_str, arg_num)) { \ + if (!(dest_str = zend_flf_parse_arg_str_slow(arg ## arg_num, arg_num))) { \ zend_wrong_parameter_type_error(arg_num, Z_EXPECTED_ARRAY_OR_STRING, arg ## arg_num); \ goto flf_clean; \ } \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d305b0a9516ba..ce7c0dec4a995 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8763,8 +8763,8 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY) strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -8777,7 +8777,7 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMPVAR|CV, ANY) } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 677166304f506..685b37f3e7414 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5964,8 +5964,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -5978,7 +5978,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -15707,8 +15707,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEN strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -15721,7 +15721,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_TMPVAR_HANDLER(ZEN } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -41709,8 +41709,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP strict = EX_USES_STRICT_TYPES(); do { if (EXPECTED(!strict)) { - zend_string *str; zval tmp; + zend_string *str; if (UNEXPECTED(Z_TYPE_P(value) == IS_NULL)) { zend_error(E_DEPRECATED, @@ -41723,7 +41723,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OP } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break;