From 9aa8e8825f4c7fc05dd366cfa521d47af813bfd7 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:52:09 +0200 Subject: [PATCH 01/25] Remove zend_jit_vm_kind (#19299) JIT used to not have a compile-time dependency on VM kind, such that a single build of opcache could work with different VM kinds at runtime. This has been broken over time and would be difficult to restore. Additionally, as opcache is now built-in, this would not be useful anymore. Remove the zend_jit_vm_kind variable. --- ext/opcache/jit/zend_jit.c | 14 ++++-------- ext/opcache/jit/zend_jit_ir.c | 42 +++++++++++++++++------------------ 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index eadd72ff4c800..e743532a69337 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -77,7 +77,6 @@ int zend_jit_profile_counter_rid = -1; int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT]; const zend_op *zend_jit_halt_op = NULL; -static int zend_jit_vm_kind = 0; #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP static int zend_write_protect = 1; #endif @@ -3707,19 +3706,14 @@ void zend_jit_init(void) #endif } +#if ZEND_VM_KIND != ZEND_VM_KIND_CALL && ZEND_VM_KIND != ZEND_VM_KIND_HYBRID +# error JIT is compatible only with CALL and HYBRID VM +#endif + int zend_jit_check_support(void) { int i; - zend_jit_vm_kind = zend_vm_kind(); - if (zend_jit_vm_kind != ZEND_VM_KIND_CALL && - zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { - zend_error(E_WARNING, "JIT is compatible only with CALL and HYBRID VM. JIT disabled."); - JIT_G(enabled) = 0; - JIT_G(on) = 0; - return FAILURE; - } - if (zend_execute_ex != execute_ex) { if (zend_dtrace_enabled) { zend_error(E_WARNING, "JIT is incompatible with DTrace. JIT disabled."); diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 2a2c6c79f17f9..16f3de648a2f1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1926,7 +1926,7 @@ static void zend_jit_vm_leave(zend_jit_ctx *jit, ir_ref to_opline) static int zend_jit_exception_handler_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { zend_vm_opcode_handler_func_t handler = (zend_vm_opcode_handler_func_t)zend_get_opcode_handler_func(EG(exception_op)); ir_CALL(IR_VOID, ir_CONST_FUNC(handler)); @@ -2051,7 +2051,7 @@ static int zend_jit_leave_function_handler_stub(zend_jit_ctx *jit) ir_IF_FALSE(if_top); - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_leave_nested_func_helper), call_info); jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); @@ -2064,7 +2064,7 @@ static int zend_jit_leave_function_handler_stub(zend_jit_ctx *jit) ir_IF_TRUE(if_top); - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_leave_top_func_helper), call_info); ir_TAILCALL(IR_VOID, ir_LOAD_A(jit_IP(jit))); } else if (GCC_GLOBAL_REGS) { @@ -2248,7 +2248,7 @@ static int zend_jit_leave_throw_stub(zend_jit_ctx *jit) static int zend_jit_hybrid_runtime_jit_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID) { return 0; } @@ -2261,7 +2261,7 @@ static int zend_jit_hybrid_profile_jit_stub(zend_jit_ctx *jit) { ir_ref addr, func, run_time_cache, jit_extension; - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID) { return 0; } @@ -2317,7 +2317,7 @@ static int _zend_jit_hybrid_hot_counter_stub(zend_jit_ctx *jit, uint32_t cost) static int zend_jit_hybrid_func_hot_counter_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) { return 0; } @@ -2327,7 +2327,7 @@ static int zend_jit_hybrid_func_hot_counter_stub(zend_jit_ctx *jit) static int zend_jit_hybrid_loop_hot_counter_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) { return 0; } @@ -2389,7 +2389,7 @@ static int _zend_jit_hybrid_trace_counter_stub(zend_jit_ctx *jit, uint32_t cost) static int zend_jit_hybrid_func_trace_counter_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !JIT_G(hot_func)) { return 0; } @@ -2399,7 +2399,7 @@ static int zend_jit_hybrid_func_trace_counter_stub(zend_jit_ctx *jit) static int zend_jit_hybrid_ret_trace_counter_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !JIT_G(hot_return)) { return 0; } @@ -2409,7 +2409,7 @@ static int zend_jit_hybrid_ret_trace_counter_stub(zend_jit_ctx *jit) static int zend_jit_hybrid_loop_trace_counter_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) { + if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID || !JIT_G(hot_loop)) { return 0; } @@ -2419,7 +2419,7 @@ static int zend_jit_hybrid_loop_trace_counter_stub(zend_jit_ctx *jit) static int zend_jit_trace_halt_stub(zend_jit_ctx *jit) { - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { ir_TAILCALL(IR_VOID, ir_CONST_FC_FUNC(zend_jit_halt_op->handler)); } else if (GCC_GLOBAL_REGS) { jit_STORE_IP(jit, IR_NULL); @@ -2676,7 +2676,7 @@ static void zend_jit_init_ctx(zend_jit_ctx *jit, uint32_t flags) jit->ctx.fixed_regset = (1<ctx.flags |= IR_NO_STACK_COMBINE; - if (zend_jit_vm_kind == ZEND_VM_KIND_CALL) { + if (ZEND_VM_KIND == ZEND_VM_KIND_CALL) { jit->ctx.flags |= IR_FUNCTION; /* Stack must be 16 byte aligned */ /* TODO: select stack size ??? */ @@ -3146,7 +3146,7 @@ static void zend_jit_calc_trace_prologue_size(void) void *entry; size_t size; - zend_jit_init_ctx(jit, (zend_jit_vm_kind == ZEND_VM_KIND_CALL) ? 0 : IR_START_BR_TARGET); + zend_jit_init_ctx(jit, (ZEND_VM_KIND == ZEND_VM_KIND_CALL) ? 0 : IR_START_BR_TARGET); if (!GCC_GLOBAL_REGS) { ir_ref execute_data_ref = ir_PARAM(IR_ADDR, "execute_data", 1); @@ -3333,7 +3333,7 @@ static void zend_jit_setup(bool reattached) #endif #if !defined(ZEND_WIN32) && !defined(IR_TARGET_AARCH64) - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { zend_jit_set_sp_adj_vm(); // set zend_jit_hybrid_vm_sp_adj } #endif @@ -4073,7 +4073,7 @@ static int zend_jit_tail_handler(zend_jit_ctx *jit, const zend_op *opline) zend_basic_block *bb; zend_jit_set_ip(jit, opline); - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { if (opline->opcode == ZEND_DO_UCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME || opline->opcode == ZEND_DO_FCALL || @@ -10984,7 +10984,7 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, if (may_be_top_frame) { // TODO: try to avoid this check ??? - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { #if 0 /* this check should be handled by the following OPLINE guard */ | cmp IP, zend_jit_halt_op @@ -16566,7 +16566,7 @@ static int zend_jit_start(zend_jit_ctx *jit, const zend_op_array *op_array, zend int i, count; zend_basic_block *bb; - zend_jit_init_ctx(jit, (zend_jit_vm_kind == ZEND_VM_KIND_CALL) ? 0 : (IR_START_BR_TARGET|IR_ENTRY_BR_TARGET)); + zend_jit_init_ctx(jit, (ZEND_VM_KIND == ZEND_VM_KIND_CALL) ? 0 : (IR_START_BR_TARGET|IR_ENTRY_BR_TARGET)); jit->ctx.spill_base = ZREG_FP; @@ -16644,7 +16644,7 @@ static zend_vm_opcode_handler_t zend_jit_finish(zend_jit_ctx *jit) // ir_mem_unprotect(entry, size); if (!(jit->ctx.flags & IR_FUNCTION) - && zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + && ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { #if !defined(ZEND_WIN32) && !defined(IR_TARGET_AARCH64) sp_offset = zend_jit_hybrid_vm_sp_adj; #else @@ -17012,7 +17012,7 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr opline->opcode == ZEND_RETURN_BY_REF || opline->opcode == ZEND_GENERATOR_CREATE) { - if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { if (trace->op != ZEND_JIT_TRACE_END || (trace->stop != ZEND_JIT_TRACE_STOP_RETURN && trace->stop < ZEND_JIT_TRACE_STOP_INTERPRETER)) { @@ -17126,7 +17126,7 @@ static int zend_jit_deoptimizer_start(zend_jit_ctx *jit, uint32_t trace_num, uint32_t exit_num) { - zend_jit_init_ctx(jit, (zend_jit_vm_kind == ZEND_VM_KIND_CALL) ? 0 : IR_START_BR_TARGET); + zend_jit_init_ctx(jit, (ZEND_VM_KIND == ZEND_VM_KIND_CALL) ? 0 : IR_START_BR_TARGET); jit->ctx.spill_base = ZREG_FP; @@ -17147,7 +17147,7 @@ static int zend_jit_trace_start(zend_jit_ctx *jit, zend_jit_trace_info *parent, uint32_t exit_num) { - zend_jit_init_ctx(jit, (zend_jit_vm_kind == ZEND_VM_KIND_CALL) ? 0 : IR_START_BR_TARGET); + zend_jit_init_ctx(jit, (ZEND_VM_KIND == ZEND_VM_KIND_CALL) ? 0 : IR_START_BR_TARGET); jit->ctx.spill_base = ZREG_FP; From 4e219242713058509e0da00ee75c6923e5544eb9 Mon Sep 17 00:00:00 2001 From: Frederik Milling Pytlick Date: Mon, 10 Mar 2025 11:15:52 +0100 Subject: [PATCH 02/25] Fix GH-17951: Addition of max_memory_limit INI Closes GH-18011 --- UPGRADING | 5 +++ main/main.c | 52 ++++++++++++++++++++++- main/php_globals.h | 1 + php.ini-development | 1 + php.ini-production | 1 + tests/basic/gh17951_ini_parse_1.phpt | 15 +++++++ tests/basic/gh17951_ini_parse_2.phpt | 15 +++++++ tests/basic/gh17951_ini_parse_3.phpt | 15 +++++++ tests/basic/gh17951_ini_parse_4.phpt | 15 +++++++ tests/basic/gh17951_ini_parse_5.phpt | 15 +++++++ tests/basic/gh17951_runtime_change_1.phpt | 14 ++++++ tests/basic/gh17951_runtime_change_2.phpt | 14 ++++++ tests/basic/gh17951_runtime_change_3.phpt | 14 ++++++ tests/basic/gh17951_runtime_change_4.phpt | 14 ++++++ tests/basic/gh17951_runtime_change_5.phpt | 19 +++++++++ 15 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 tests/basic/gh17951_ini_parse_1.phpt create mode 100644 tests/basic/gh17951_ini_parse_2.phpt create mode 100644 tests/basic/gh17951_ini_parse_3.phpt create mode 100644 tests/basic/gh17951_ini_parse_4.phpt create mode 100644 tests/basic/gh17951_ini_parse_5.phpt create mode 100644 tests/basic/gh17951_runtime_change_1.phpt create mode 100644 tests/basic/gh17951_runtime_change_2.phpt create mode 100644 tests/basic/gh17951_runtime_change_3.phpt create mode 100644 tests/basic/gh17951_runtime_change_4.phpt create mode 100644 tests/basic/gh17951_runtime_change_5.phpt diff --git a/UPGRADING b/UPGRADING index 65e164fe01a07..f3a7494d3c839 100644 --- a/UPGRADING +++ b/UPGRADING @@ -629,6 +629,11 @@ PHP 8.5 UPGRADE NOTES . Added fatal_error_backtraces to control whether fatal errors should include a backtrace. RFC: https://wiki.php.net/rfc/error_backtraces_v2 + . Added startup-only max_memory_limit INI setting to control the maximum + memory_limit that may be configured at startup or runtime. Exceeding this + value emits a warning, unless set to -1, and sets memory_limit to the + current max_memory_limit instead. + ML discussion: https://externals.io/message/127108 - Opcache: . Added opcache.file_cache_read_only to support a read-only diff --git a/main/main.c b/main/main.c index 04715d1916eec..7e4925da6852d 100644 --- a/main/main.c +++ b/main/main.c @@ -332,6 +332,33 @@ static PHP_INI_MH(OnChangeMemoryLimit) } else { value = Z_L(1)<<30; /* effectively, no limit */ } + + /* If max_memory_limit is not set to unlimited, verify change */ + if (PG(max_memory_limit) != -1) { + if (value == -1) { + zend_error( + E_WARNING, + "Failed to set memory_limit to unlimited. memory_limit (currently: " ZEND_LONG_FMT " bytes) cannot be set to unlimited if max_memory_limit (" ZEND_LONG_FMT " bytes) is not unlimited", + PG(memory_limit), + PG(max_memory_limit) + ); + + return FAILURE; + } + + if (value > PG(max_memory_limit)) { + zend_error( + E_WARNING, + "Failed to set memory_limit to %zd bytes. memory_limit (currently: " ZEND_LONG_FMT " bytes) cannot exceed max_memory_limit (" ZEND_LONG_FMT " bytes)", + value, + PG(memory_limit), + PG(max_memory_limit) + ); + + return FAILURE; + } + } + if (zend_set_memory_limit(value) == FAILURE) { /* When the memory limit is reset to the original level during deactivation, we may be * using more memory than the original limit while shutdown is still in progress. @@ -347,6 +374,26 @@ static PHP_INI_MH(OnChangeMemoryLimit) } /* }}} */ +static PHP_INI_MH(OnChangeMaxMemoryLimit) +{ + size_t value; + if (new_value) { + value = zend_ini_parse_uquantity_warn(new_value, entry->name); + } else { + value = Z_L(1) << 30; /* effectively, no limit */ + } + + if (zend_set_memory_limit(value) == FAILURE) { + zend_error(E_ERROR, "Failed to set memory limit to %zd bytes (Current memory usage is %zd bytes)", value, zend_memory_usage(true)); + return FAILURE; + } + + PG(memory_limit) = value; + PG(max_memory_limit) = value; + + return SUCCESS; +} + /* {{{ PHP_INI_MH */ static PHP_INI_MH(OnSetLogFilter) { @@ -810,7 +857,10 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("mail.mixed_lf_and_crlf", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, mail_mixed_lf_and_crlf, php_core_globals, core_globals) STD_PHP_INI_ENTRY("mail.log", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateMailLog, mail_log, php_core_globals, core_globals) PHP_INI_ENTRY("browscap", NULL, PHP_INI_SYSTEM, OnChangeBrowscap) - PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + + PHP_INI_ENTRY("max_memory_limit", "-1", PHP_INI_SYSTEM, OnChangeMaxMemoryLimit) + PHP_INI_ENTRY("memory_limit", "128M", PHP_INI_ALL, OnChangeMemoryLimit) + PHP_INI_ENTRY("precision", "14", PHP_INI_ALL, OnSetPrecision) PHP_INI_ENTRY("sendmail_from", NULL, PHP_INI_ALL, NULL) PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) diff --git a/main/php_globals.h b/main/php_globals.h index ab7a9a00b2f1d..94e5aa44fc59f 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -72,6 +72,7 @@ struct _php_core_globals { zend_long serialize_precision; zend_long memory_limit; + zend_long max_memory_limit; zend_long max_input_time; char *error_log; diff --git a/php.ini-development b/php.ini-development index 2760f167ba4ea..2d3c8bf32a5d9 100644 --- a/php.ini-development +++ b/php.ini-development @@ -436,6 +436,7 @@ max_input_time = 60 ; Maximum amount of memory a script may consume ; https://php.net/memory-limit memory_limit = 128M +max_memory_limit = -1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; diff --git a/php.ini-production b/php.ini-production index e028990f304cd..13d3f66c93c4f 100644 --- a/php.ini-production +++ b/php.ini-production @@ -438,6 +438,7 @@ max_input_time = 60 ; Maximum amount of memory a script may consume ; https://php.net/memory-limit memory_limit = 128M +max_memory_limit = -1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; diff --git a/tests/basic/gh17951_ini_parse_1.phpt b/tests/basic/gh17951_ini_parse_1.phpt new file mode 100644 index 0000000000000..fe0dea138e4b2 --- /dev/null +++ b/tests/basic/gh17951_ini_parse_1.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-17951 INI Parse 1 +--CREDITS-- +Frederik Milling Pytlick (frederikpyt@protonmail.com) +--INI-- +memory_limit=128M +max_memory_limit=-1 +--FILE-- + +--EXPECT-- +-1 +128M diff --git a/tests/basic/gh17951_ini_parse_2.phpt b/tests/basic/gh17951_ini_parse_2.phpt new file mode 100644 index 0000000000000..e0f290f4c2cf0 --- /dev/null +++ b/tests/basic/gh17951_ini_parse_2.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-17951 INI Parse 2 +--CREDITS-- +Frederik Milling Pytlick (frederikpyt@protonmail.com) +--INI-- +memory_limit=-1 +max_memory_limit=-1 +--FILE-- + +--EXPECT-- +-1 +-1 diff --git a/tests/basic/gh17951_ini_parse_3.phpt b/tests/basic/gh17951_ini_parse_3.phpt new file mode 100644 index 0000000000000..5057671418621 --- /dev/null +++ b/tests/basic/gh17951_ini_parse_3.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-17951 INI Parse 3 +--CREDITS-- +Frederik Milling Pytlick (frederikpyt@protonmail.com) +--INI-- +memory_limit=128M +max_memory_limit=256M +--FILE-- + +--EXPECT-- +256M +128M diff --git a/tests/basic/gh17951_ini_parse_4.phpt b/tests/basic/gh17951_ini_parse_4.phpt new file mode 100644 index 0000000000000..f29207183a1cf --- /dev/null +++ b/tests/basic/gh17951_ini_parse_4.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-17951 INI Parse 4 +--CREDITS-- +Frederik Milling Pytlick (frederikpyt@protonmail.com) +--INI-- +memory_limit=-1 +max_memory_limit=128M +--FILE-- + +--EXPECT-- +256M diff --git a/tests/basic/gh17951_runtime_change_2.phpt b/tests/basic/gh17951_runtime_change_2.phpt new file mode 100644 index 0000000000000..8bcc5ea65dd5c --- /dev/null +++ b/tests/basic/gh17951_runtime_change_2.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-17951 Runtime Change 2 +--CREDITS-- +Frederik Milling Pytlick (frederikpyt@protonmail.com) +--INI-- +memory_limit=128M +max_memory_limit=512M +--FILE-- + +--EXPECT-- +512M diff --git a/tests/basic/gh17951_runtime_change_3.phpt b/tests/basic/gh17951_runtime_change_3.phpt new file mode 100644 index 0000000000000..d37c9da9b8ed4 --- /dev/null +++ b/tests/basic/gh17951_runtime_change_3.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-17951 Runtime Change 3 +--CREDITS-- +Frederik Milling Pytlick (frederikpyt@protonmail.com) +--INI-- +memory_limit=128M +max_memory_limit=512M +--FILE-- + +--EXPECT-- +bool(false) +bool(false) +bool(false) +bool(false) From cd80ed6f7bcec1bd96708cba172255856f13a3be Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 4 Aug 2025 15:58:21 +0200 Subject: [PATCH 03/25] Implement changes to GH-17951 according to ML discussion --- Zend/zend_ini.c | 20 ++++++++++++--- main/main.c | 31 ++++++++--------------- tests/basic/gh17951_ini_parse_4.phpt | 4 +-- tests/basic/gh17951_ini_parse_5.phpt | 5 ++-- tests/basic/gh17951_runtime_change_3.phpt | 5 ++-- tests/basic/gh17951_runtime_change_4.phpt | 6 ++--- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index 199ebfb2e9b45..689e76794df34 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -248,10 +248,16 @@ ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_ zend_unregister_ini_entries_ex(module_number, module_type); return FAILURE; } + + zend_string *prev_value = p->value; + if (((default_value = zend_get_configuration_directive(p->name)) != NULL) && (!p->on_modify || p->on_modify(p, Z_STR_P(default_value), p->mh_arg1, p->mh_arg2, p->mh_arg3, ZEND_INI_STAGE_STARTUP) == SUCCESS)) { - p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value))); + /* Skip assigning the value if the handler has already done so. */ + if (p->value == prev_value) { + p->value = zend_new_interned_string(zend_string_copy(Z_STR_P(default_value))); + } } else { p->value = ini_entry->value ? zend_string_init_interned(ini_entry->value, ini_entry->value_length, 1) : NULL; @@ -389,14 +395,20 @@ ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new zend_hash_add_ptr(EG(modified_ini_directives), ini_entry->name, ini_entry); } + zend_string *prev_value = ini_entry->value; duplicate = zend_string_copy(new_value); if (!ini_entry->on_modify || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) { - if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */ - zend_string_release(ini_entry->value); + if (modified && ini_entry->orig_value != prev_value) { /* we already changed the value, free the changed value */ + zend_string_release(prev_value); + } + /* Skip assigning the value if the handler has already done so. */ + if (ini_entry->value == prev_value) { + ini_entry->value = duplicate; + } else { + zend_string_release(duplicate); } - ini_entry->value = duplicate; } else { zend_string_release(duplicate); return FAILURE; diff --git a/main/main.c b/main/main.c index 7e4925da6852d..1cff1cee27a40 100644 --- a/main/main.c +++ b/main/main.c @@ -333,30 +333,19 @@ static PHP_INI_MH(OnChangeMemoryLimit) value = Z_L(1)<<30; /* effectively, no limit */ } - /* If max_memory_limit is not set to unlimited, verify change */ - if (PG(max_memory_limit) != -1) { - if (value == -1) { - zend_error( - E_WARNING, - "Failed to set memory_limit to unlimited. memory_limit (currently: " ZEND_LONG_FMT " bytes) cannot be set to unlimited if max_memory_limit (" ZEND_LONG_FMT " bytes) is not unlimited", - PG(memory_limit), - PG(max_memory_limit) - ); - - return FAILURE; + /* If memory_limit exceeds max_memory_limit, warn and set to max_memory_limit instead. */ + if (value > PG(max_memory_limit)) { + if (value != -1) { + zend_error(E_WARNING, + "Failed to set memory_limit to %zd bytes. Setting to max_memory_limit instead (currently: " ZEND_LONG_FMT " bytes)", + value, PG(max_memory_limit)); } - if (value > PG(max_memory_limit)) { - zend_error( - E_WARNING, - "Failed to set memory_limit to %zd bytes. memory_limit (currently: " ZEND_LONG_FMT " bytes) cannot exceed max_memory_limit (" ZEND_LONG_FMT " bytes)", - value, - PG(memory_limit), - PG(max_memory_limit) - ); + zend_ini_entry *max_mem_limit_ini = zend_hash_str_find_ptr(EG(ini_directives), ZEND_STRL("max_memory_limit")); + entry->value = zend_string_copy(max_mem_limit_ini->value); + PG(memory_limit) = PG(max_memory_limit); - return FAILURE; - } + return SUCCESS; } if (zend_set_memory_limit(value) == FAILURE) { diff --git a/tests/basic/gh17951_ini_parse_4.phpt b/tests/basic/gh17951_ini_parse_4.phpt index f29207183a1cf..5690a133d7755 100644 --- a/tests/basic/gh17951_ini_parse_4.phpt +++ b/tests/basic/gh17951_ini_parse_4.phpt @@ -9,7 +9,7 @@ max_memory_limit=128M +--EXPECT-- 128M 128M diff --git a/tests/basic/gh17951_ini_parse_5.phpt b/tests/basic/gh17951_ini_parse_5.phpt index ee028caf9a351..20e414526000a 100644 --- a/tests/basic/gh17951_ini_parse_5.phpt +++ b/tests/basic/gh17951_ini_parse_5.phpt @@ -9,7 +9,8 @@ max_memory_limit=128M +--EXPECT-- +Warning: Failed to set memory_limit to 268435456 bytes. Setting to max_memory_limit instead (currently: 134217728 bytes) in Unknown on line 0 128M 128M diff --git a/tests/basic/gh17951_runtime_change_3.phpt b/tests/basic/gh17951_runtime_change_3.phpt index d37c9da9b8ed4..975caad4ff0bf 100644 --- a/tests/basic/gh17951_runtime_change_3.phpt +++ b/tests/basic/gh17951_runtime_change_3.phpt @@ -9,6 +9,7 @@ max_memory_limit=512M --EXPECTF-- -Warning: Failed to set memory_limit to %d bytes. memory_limit (currently: %d bytes) cannot exceed max_memory_limit (%d bytes) in %s -128M +Warning: Failed to set memory_limit to 1073741824 bytes. Setting to max_memory_limit instead (currently: 536870912 bytes) in %s on line %d +512M diff --git a/tests/basic/gh17951_runtime_change_4.phpt b/tests/basic/gh17951_runtime_change_4.phpt index 0cbe3bb5c1040..3b27e2c91a9ab 100644 --- a/tests/basic/gh17951_runtime_change_4.phpt +++ b/tests/basic/gh17951_runtime_change_4.phpt @@ -9,6 +9,6 @@ max_memory_limit=512M +--EXPECT-- +512M From 433c00b34847ff18a339b90277fc076ad4e31148 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:15:52 +0200 Subject: [PATCH 04/25] [skip ci] tree: Fix various typos (#19366) --- ext/gettext/gettext.c | 2 +- ext/opcache/shared_alloc_mmap.c | 4 ++-- ext/pdo_mysql/php_pdo_mysql_int.h | 2 +- sapi/phpdbg/phpdbg_watch.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c index 27f0dfa26da70..6badea4a17698 100644 --- a/ext/gettext/gettext.c +++ b/ext/gettext/gettext.c @@ -194,7 +194,7 @@ PHP_FUNCTION(bindtextdomain) btd_result = bindtextdomain(ZSTR_VAL(domain), NULL); if (btd_result == NULL) { /* POSIX-compliant implementations can return - * NULL if an error occured. On musl you will + * NULL if an error occurred. On musl you will * also get NULL if the domain is not yet * bound, because musl has no default directory * to return in that case. */ diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index 18c7532478f48..cf5dbb29ebfd9 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -52,7 +52,7 @@ #endif #if defined(HAVE_JIT) && (defined(__linux__) || defined(__FreeBSD__)) && (defined(__x86_64__) || defined (__aarch64__)) && !defined(__SANITIZE_ADDRESS__) -static void *find_prefered_mmap_base(size_t requested_size) +static void *find_preferred_mmap_base(size_t requested_size) { size_t huge_page_size = 2 * 1024 * 1024; uintptr_t last_free_addr = huge_page_size; @@ -204,7 +204,7 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_ void *hint; if (JIT_G(enabled) && JIT_G(buffer_size) && zend_jit_check_support() == SUCCESS) { - hint = find_prefered_mmap_base(requested_size); + hint = find_preferred_mmap_base(requested_size); } else { /* Do not use a hint if JIT is not enabled, as this profits only JIT and * this is potentially unsafe when the only suitable candidate is just diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h index ae77193afcfa9..ec49d6c311c34 100644 --- a/ext/pdo_mysql/php_pdo_mysql_int.h +++ b/ext/pdo_mysql/php_pdo_mysql_int.h @@ -78,7 +78,7 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_mysql) /* dummy member so we get at least one member in the struct * and avoids build errors. */ - void *dummymemmber; + void *dummymember; #endif ZEND_END_MODULE_GLOBALS(pdo_mysql) diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c index 4478dca0516ba..14496d865ead4 100644 --- a/sapi/phpdbg/phpdbg_watch.c +++ b/sapi/phpdbg/phpdbg_watch.c @@ -1481,7 +1481,7 @@ void phpdbg_setup_watchpoints(void) { #ifdef HAVE_USERFAULTFD_WRITEFAULT int flags = O_CLOEXEC; #ifdef UFFD_USER_MODE_ONLY - // unpriviliged userfaultfd are disabled by default, + // unprivileged userfaultfd are disabled by default, // with this flag it allows ranges from the user space // being reported. flags |= UFFD_USER_MODE_ONLY; From ac1cd9c26ef90fd2512587c185db2964bc060f77 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 4 Aug 2025 17:26:24 +0300 Subject: [PATCH 05/25] Update IR IR commit: 6e2aea0ebfef2c741ebec30c57aa492df0d4e319 --- ext/opcache/jit/ir/ir.c | 15 +- ext/opcache/jit/ir/ir.h | 17 +- ext/opcache/jit/ir/ir_aarch64.dasc | 94 +++++++++-- ext/opcache/jit/ir/ir_builder.h | 2 + ext/opcache/jit/ir/ir_cfg.c | 247 +++++++++++++++++++++-------- ext/opcache/jit/ir/ir_check.c | 9 +- ext/opcache/jit/ir/ir_emit.c | 4 +- ext/opcache/jit/ir/ir_fold.h | 4 +- ext/opcache/jit/ir/ir_gcm.c | 199 +++++++++++++++++++++-- ext/opcache/jit/ir/ir_private.h | 5 +- ext/opcache/jit/ir/ir_save.c | 11 +- ext/opcache/jit/ir/ir_sccp.c | 38 ++++- ext/opcache/jit/ir/ir_x86.dasc | 167 +++++++++++++++++-- 13 files changed, 685 insertions(+), 127 deletions(-) diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index a9f55cc0e466c..97d32680e4fdd 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -172,7 +172,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted } else if (insn->val.c == '\0') { fprintf(f, "'\\0'"); } else { - fprintf(f, "%u", insn->val.c); + fprintf(f, "%u", (unsigned char)insn->val.c); } break; case IR_I8: @@ -247,6 +247,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_flag_S1X1 (ir_op_flag_S | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_S2 (ir_op_flag_S | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_S2X1 (ir_op_flag_S | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_S3 (ir_op_flag_S | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_SN (ir_op_flag_S | IR_OP_FLAG_VAR_INPUTS) #define ir_op_flag_E (IR_OP_FLAG_CONTROL|IR_OP_FLAG_BB_END) #define ir_op_flag_E1 (ir_op_flag_E | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) @@ -803,7 +804,9 @@ ir_ref ir_proto(ir_ctx *ctx, uint8_t flags, ir_type ret_type, uint32_t params_co proto->flags = flags; proto->ret_type = ret_type; proto->params_count = params_count; - memcpy(proto->param_types, param_types, params_count); + if (params_count) { + memcpy(proto->param_types, param_types, params_count); + } return ir_strl(ctx, (const char *)proto, offsetof(ir_proto_t, param_types) + params_count); } @@ -1080,7 +1083,7 @@ ir_ref ir_emit_N(ir_ctx *ctx, uint32_t opt, int32_t count) ir_ref *p, ref = ctx->insns_count; ir_insn *insn; - IR_ASSERT(count >= 0); + IR_ASSERT(count >= 0 && count < 65536); while (UNEXPECTED(ref + count/4 >= ctx->insns_limit)) { ir_grow_top(ctx); } @@ -2973,6 +2976,12 @@ void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val) ctx->control = ir_emit2(ctx, IR_CASE_VAL, switch_ref, val); } +void _ir_CASE_RANGE(ir_ctx *ctx, ir_ref switch_ref, ir_ref v1, ir_ref v2) +{ + IR_ASSERT(!ctx->control); + ctx->control = ir_emit3(ctx, IR_CASE_RANGE, switch_ref, v1, v2); +} + void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref) { IR_ASSERT(!ctx->control); diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index ec5e57129c9ab..9575348ff5450 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -359,6 +359,7 @@ typedef enum _ir_type { _(IF_TRUE, S1X1, src, prb, ___) /* IF TRUE proj. */ \ _(IF_FALSE, S1X1, src, prb, ___) /* IF FALSE proj. */ \ _(CASE_VAL, S2X1, src, def, prb) /* switch proj. */ \ + _(CASE_RANGE, S3, src, def, def) /* switch proj. */ \ _(CASE_DEFAULT, S1X1, src, prb, ___) /* switch proj. */ \ _(MERGE, SN, src, src, src) /* control merge */ \ _(LOOP_BEGIN, SN, src, src, src) /* loop start */ \ @@ -854,6 +855,9 @@ void ir_gdb_unregister_all(void); bool ir_gdb_present(void); /* IR load API (implementation in ir_load.c) */ +#define IR_RESOLVE_SYM_ADD_THUNK (1<<0) +#define IR_RESOLVE_SYM_SILENT (1<<1) + struct _ir_loader { uint32_t default_func_flags; bool (*init_module) (ir_loader *loader, const char *name, const char *filename, const char *target); @@ -870,7 +874,7 @@ struct _ir_loader { bool (*sym_data_end) (ir_loader *loader, uint32_t flags); bool (*func_init) (ir_loader *loader, ir_ctx *ctx, const char *name); bool (*func_process) (ir_loader *loader, ir_ctx *ctx, const char *name); - void*(*resolve_sym_name) (ir_loader *loader, const char *name, bool add_thunk); + void*(*resolve_sym_name) (ir_loader *loader, const char *name, uint32_t flags); bool (*has_sym) (ir_loader *loader, const char *name); bool (*add_sym) (ir_loader *loader, const char *name, void *addr); }; @@ -884,11 +888,12 @@ int ir_load_llvm_bitcode(ir_loader *loader, const char *filename); int ir_load_llvm_asm(ir_loader *loader, const char *filename); /* IR save API (implementation in ir_save.c) */ -#define IR_SAVE_CFG (1<<0) /* add info about CFG */ -#define IR_SAVE_CFG_MAP (1<<1) /* add info about CFG block assignment */ -#define IR_SAVE_USE_LISTS (1<<2) /* add info about def->use lists */ -#define IR_SAVE_RULES (1<<3) /* add info about selected code-generation rules */ -#define IR_SAVE_REGS (1<<4) /* add info about selected registers */ +#define IR_SAVE_CFG (1<<0) /* add info about CFG */ +#define IR_SAVE_CFG_MAP (1<<1) /* add info about CFG block assignment */ +#define IR_SAVE_USE_LISTS (1<<2) /* add info about def->use lists */ +#define IR_SAVE_RULES (1<<3) /* add info about selected code-generation rules */ +#define IR_SAVE_REGS (1<<4) /* add info about selected registers */ +#define IR_SAVE_SAFE_NAMES (1<<5) /* add '@' prefix to symbol names */ void ir_print_proto(const ir_ctx *ctx, ir_ref proto, FILE *f); void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f); diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 772eea7a5d78a..476529555e93e 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -996,6 +996,7 @@ binop_fp: case IR_IF_TRUE: case IR_IF_FALSE: case IR_CASE_VAL: + case IR_CASE_RANGE: case IR_CASE_DEFAULT: case IR_MERGE: case IR_LOOP_BEGIN: @@ -4366,11 +4367,15 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; ir_type type = insn->type; - ir_reg def_reg = ctx->regs[def][0]; + ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]); ir_reg op2_reg = ctx->regs[def][2]; ir_reg tmp_reg = ctx->regs[def][3]; int32_t offset; + if (ctx->use_lists[def].count == 1) { + /* dead load */ + return; + } IR_ASSERT(def_reg != IR_REG_NONE && tmp_reg != IR_REG_NONE); if (op2_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op2_reg)) { @@ -4394,11 +4399,15 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; ir_type type = insn->type; - ir_reg def_reg = ctx->regs[def][0]; + ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]); ir_reg op2_reg = ctx->regs[def][2]; ir_reg tmp_reg = ctx->regs[def][3]; int32_t offset; + if (ctx->use_lists[def].count == 1) { + /* dead load */ + return; + } IR_ASSERT(def_reg != IR_REG_NONE && tmp_reg != IR_REG_NONE); if (op2_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op2_reg)) { @@ -4465,6 +4474,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) int count = 0; ir_val min, max; ir_reg op1_reg, op2_reg, tmp_reg; + bool has_case_range = 0; type = ctx->ir_base[insn->op2].type; if (IR_IS_TYPE_SIGNED(type)) { @@ -4493,6 +4503,22 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) max.u64 = (int64_t)IR_MAX(max.u64, val->val.u64); } count++; + } else if (use_insn->op == IR_CASE_RANGE) { + has_case_range = 1; + val = &ctx->ir_base[use_insn->op2]; + IR_ASSERT(!IR_IS_SYM_CONST(val->op)); + ir_insn *val2 = &ctx->ir_base[use_insn->op3]; + IR_ASSERT(!IR_IS_SYM_CONST(val2->op)); + if (IR_IS_TYPE_SIGNED(type)) { + IR_ASSERT(IR_IS_TYPE_SIGNED(val->type)); + min.i64 = IR_MIN(min.i64, val->val.i64); + max.i64 = IR_MAX(max.i64, val2->val.i64); + } else { + IR_ASSERT(!IR_IS_TYPE_SIGNED(val->type)); + min.u64 = (int64_t)IR_MIN(min.u64, val->val.u64); + max.u64 = (int64_t)IR_MAX(max.u64, val2->val.u64); + } + count++; } else { IR_ASSERT(use_insn->op == IR_CASE_DEFAULT); default_label = ir_skip_empty_target_blocks(ctx, use_block); @@ -4510,7 +4536,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } /* Generate a table jmp or a sequence of calls */ - if (count > 2 && (max.i64-min.i64) < count * 8) { + if (!has_case_range && count > 2 && (max.i64-min.i64) < count * 8) { int *labels = ir_mem_malloc(sizeof(int) * (max.i64 - min.i64 + 1)); for (i = 0; i <= (max.i64 - min.i64); i++) { @@ -4615,6 +4641,38 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } | beq =>label + } else if (use_insn->op == IR_CASE_RANGE) { + val = &ctx->ir_base[use_insn->op2]; + IR_ASSERT(!IR_IS_SYM_CONST(val->op)); + label = ir_skip_empty_target_blocks(ctx, use_block); + if (aarch64_may_encode_imm12(val->val.i64)) { + | ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i64 + } else { + ir_emit_load_imm_int(ctx, type, tmp_reg, val->val.i64); + | ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg + + } + if (IR_IS_TYPE_SIGNED(type)) { + | blt >1 + } else { + | blo >1 + } + val = &ctx->ir_base[use_insn->op3]; + IR_ASSERT(!IR_IS_SYM_CONST(val->op)); + label = ir_skip_empty_target_blocks(ctx, use_block); + if (aarch64_may_encode_imm12(val->val.i64)) { + | ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i64 + } else { + ir_emit_load_imm_int(ctx, type, tmp_reg, val->val.i64); + | ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg + + } + if (IR_IS_TYPE_SIGNED(type)) { + | ble =>label + } else { + | bls =>label + } + |1: } } if (default_label) { @@ -4935,6 +4993,28 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn) return; } + /* Move op2 to a tmp register before epilogue if it's in + * used_preserved_regs, because it will be overridden. */ + + ir_reg op2_reg = IR_REG_NONE; + if (!IR_IS_CONST_REF(insn->op2)) { + op2_reg = ctx->regs[def][2]; + IR_ASSERT(op2_reg != IR_REG_NONE); + + if (IR_REG_SPILLED(op2_reg)) { + op2_reg = IR_REG_INT_TMP; + ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2); + } else if (IR_REGSET_IN((ir_regset)ctx->used_preserved_regs, IR_REG_NUM(op2_reg))) { + ir_reg orig_op2_reg = op2_reg; + op2_reg = IR_REG_INT_TMP; + + ir_type type = ctx->ir_base[insn->op2].type; + | ASM_REG_REG_OP mov, type, op2_reg, IR_REG_NUM(orig_op2_reg) + } else { + op2_reg = IR_REG_NUM(op2_reg); + } + } + ir_emit_epilogue(ctx); if (IR_IS_CONST_REF(insn->op2)) { @@ -4947,13 +5027,8 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn) | br Rx(IR_REG_INT_TMP) } } else { - ir_reg op2_reg = ctx->regs[def][2]; - IR_ASSERT(op2_reg != IR_REG_NONE); - if (IR_REG_SPILLED(op2_reg)) { - op2_reg = IR_REG_NUM(op2_reg); - ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2); - } + IR_ASSERT(!IR_REGSET_IN((ir_regset)ctx->used_preserved_regs, op2_reg)); | br Rx(op2_reg) } } @@ -5590,6 +5665,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) case IR_IF_TRUE: case IR_IF_FALSE: case IR_CASE_VAL: + case IR_CASE_RANGE: case IR_CASE_DEFAULT: case IR_MERGE: case IR_LOOP_BEGIN: diff --git a/ext/opcache/jit/ir/ir_builder.h b/ext/opcache/jit/ir/ir_builder.h index 4e4ea53683afb..ba1924f8d65e5 100644 --- a/ext/opcache/jit/ir/ir_builder.h +++ b/ext/opcache/jit/ir/ir_builder.h @@ -603,6 +603,7 @@ extern "C" { #define ir_LOOP_END() _ir_LOOP_END(_ir_CTX) #define ir_SWITCH(_val) _ir_SWITCH(_ir_CTX, (_val)) #define ir_CASE_VAL(_switch, _val) _ir_CASE_VAL(_ir_CTX, (_switch), (_val)) +#define ir_CASE_RANGE(_switch, _v1, _v2) _ir_CASE_RANGE(_ir_CTX, (_switch), (_v1), (_v2)) #define ir_CASE_DEFAULT(_switch) _ir_CASE_DEFAULT(_ir_CTX, (_switch)) #define ir_RETURN(_val) _ir_RETURN(_ir_CTX, (_val)) #define ir_IJMP(_addr) _ir_IJMP(_ir_CTX, (_addr)) @@ -682,6 +683,7 @@ ir_ref _ir_TLS(ir_ctx *ctx, ir_ref index, ir_ref offset); void _ir_UNREACHABLE(ir_ctx *ctx); ir_ref _ir_SWITCH(ir_ctx *ctx, ir_ref val); void _ir_CASE_VAL(ir_ctx *ctx, ir_ref switch_ref, ir_ref val); +void _ir_CASE_RANGE(ir_ctx *ctx, ir_ref switch_ref, ir_ref v1, ir_ref v2); void _ir_CASE_DEFAULT(ir_ctx *ctx, ir_ref switch_ref); void _ir_RETURN(ir_ctx *ctx, ir_ref val); void _ir_IJMP(ir_ctx *ctx, ir_ref addr); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 01532c8ea3e30..13d66a7130283 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -244,7 +244,6 @@ int ir_build_cfg(ir_ctx *ctx) _blocks[start] = b; _blocks[end] = b; IR_ASSERT(IR_IS_BB_START(insn->op)); - IR_ASSERT(end > start); bb->start = start; bb->end = end; bb->successors = count; @@ -583,7 +582,6 @@ static int ir_remove_unreachable_blocks(ir_ctx *ctx) return 1; } -#if 0 static void compute_postnum(const ir_ctx *ctx, uint32_t *cur, uint32_t b) { uint32_t i, *p; @@ -607,34 +605,42 @@ static void compute_postnum(const ir_ctx *ctx, uint32_t *cur, uint32_t b) /* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by * Cooper, Harvey and Kennedy. */ -int ir_build_dominators_tree(ir_ctx *ctx) +static int ir_build_dominators_tree_slow(ir_ctx *ctx) { uint32_t blocks_count, b, postnum; ir_block *blocks, *bb; uint32_t *edges; bool changed; + blocks = ctx->cfg_blocks; + edges = ctx->cfg_edges; + blocks_count = ctx->cfg_blocks_count; + + /* Clear the dominators tree */ + for (b = 0, bb = &blocks[0]; b <= blocks_count; b++, bb++) { + bb->idom = 0; + bb->dom_depth = 0; + bb->dom_child = 0; + bb->dom_next_child = 0; + } + ctx->flags2 &= ~IR_NO_LOOPS; postnum = 1; compute_postnum(ctx, &postnum, 1); - /* Find immediate dominators */ - blocks = ctx->cfg_blocks; - edges = ctx->cfg_edges; - blocks_count = ctx->cfg_blocks_count; + /* Find immediate dominators by iterative fixed-point algorithm */ blocks[1].idom = 1; do { changed = 0; /* Iterating in Reverse Post Order */ for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) { IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE)); + IR_ASSERT(bb->predecessors_count > 0); if (bb->predecessors_count == 1) { uint32_t pred_b = edges[bb->predecessors]; - if (blocks[pred_b].idom <= 0) { - //IR_ASSERT("Wrong blocks order: BB is before its single predecessor"); - } else if (bb->idom != pred_b) { + if (blocks[pred_b].idom > 0 && bb->idom != pred_b) { bb->idom = pred_b; changed = 1; } @@ -680,39 +686,53 @@ int ir_build_dominators_tree(ir_ctx *ctx) } } } while (changed); + + /* Build dominators tree */ blocks[1].idom = 0; blocks[1].dom_depth = 0; + for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) { + uint32_t idom = bb->idom; + ir_block *idom_bb = &blocks[idom]; + + bb->dom_depth = 0; + /* Sort by block number to traverse children in pre-order */ + if (idom_bb->dom_child == 0) { + idom_bb->dom_child = b; + } else if (b < idom_bb->dom_child) { + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; + } else { + int child = idom_bb->dom_child; + ir_block *child_bb = &blocks[child]; - /* Construct dominators tree */ + while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { + child = child_bb->dom_next_child; + child_bb = &blocks[child]; + } + bb->dom_next_child = child_bb->dom_next_child; + child_bb->dom_next_child = b; + } + } + + /* Recalculate dom_depth for all blocks */ for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) { - IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE)); - if (bb->idom > 0) { - ir_block *idom_bb = &blocks[bb->idom]; - - bb->dom_depth = idom_bb->dom_depth + 1; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; + uint32_t idom = bb->idom; + uint32_t dom_depth = 0; + while (idom) { + dom_depth++; + if (blocks[idom].dom_depth > 0) { + dom_depth += blocks[idom].dom_depth; + break; } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; - - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; + idom = blocks[idom].idom; } } + bb->dom_depth = dom_depth; } return 1; } -#else + /* A single pass modification of "A Simple, Fast Dominance Algorithm" by * Cooper, Harvey and Kennedy, that relays on IR block ordering. * It may fallback to the general slow fixed-point algorithm. */ @@ -747,7 +767,11 @@ int ir_build_dominators_tree(ir_ctx *ctx) if (UNEXPECTED(idom >= b)) { /* In rare cases, LOOP_BEGIN.op1 may be a back-edge. Skip back-edges. */ ctx->flags2 &= ~IR_NO_LOOPS; - IR_ASSERT(k > 1 && "Wrong blocks order: BB is before its single predecessor"); +// IR_ASSERT(k > 1 && "Wrong blocks order: BB is before its single predecessor"); + if (UNEXPECTED(k <= 1)) { + ir_list_free(&worklist); + return ir_build_dominators_tree_slow(ctx); + } ir_list_push(&worklist, idom); while (1) { k--; @@ -942,7 +966,6 @@ static int ir_build_dominators_tree_iterative(ir_ctx *ctx) return 1; } -#endif static bool ir_dominates(const ir_block *blocks, uint32_t b1, uint32_t b2) { @@ -958,7 +981,7 @@ static bool ir_dominates(const ir_block *blocks, uint32_t b1, uint32_t b2) int ir_find_loops(ir_ctx *ctx) { - uint32_t i, j, n, count; + uint32_t b, j, n, count; uint32_t *entry_times, *exit_times, *sorted_blocks, time = 1; ir_block *blocks = ctx->cfg_blocks; uint32_t *edges = ctx->cfg_edges; @@ -983,13 +1006,13 @@ int ir_find_loops(ir_ctx *ctx) int child; next: - i = ir_worklist_peek(&work); - if (!entry_times[i]) { - entry_times[i] = time++; + b = ir_worklist_peek(&work); + if (!entry_times[b]) { + entry_times[b] = time++; } - /* Visit blocks immediately dominated by i. */ - bb = &blocks[i]; + /* Visit blocks immediately dominated by "b". */ + bb = &blocks[b]; for (child = bb->dom_child; child > 0; child = blocks[child].dom_next_child) { if (ir_worklist_push(&work, child)) { goto next; @@ -999,17 +1022,17 @@ int ir_find_loops(ir_ctx *ctx) /* Visit join edges. */ if (bb->successors_count) { uint32_t *p = edges + bb->successors; - for (j = 0; j < bb->successors_count; j++,p++) { + for (j = 0; j < bb->successors_count; j++, p++) { uint32_t succ = *p; - if (blocks[succ].idom == i) { + if (blocks[succ].idom == b) { continue; } else if (ir_worklist_push(&work, succ)) { goto next; } } } - exit_times[i] = time++; + exit_times[b] = time++; ir_worklist_pop(&work); } @@ -1018,7 +1041,7 @@ int ir_find_loops(ir_ctx *ctx) j = 1; n = 2; while (j != n) { - i = j; + uint32_t i = j; j = n; for (; i < j; i++) { int child; @@ -1030,9 +1053,82 @@ int ir_find_loops(ir_ctx *ctx) count = n; /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ Graphs". */ + uint32_t prev_dom_depth = blocks[sorted_blocks[n - 1]].dom_depth; + uint32_t prev_irreducible = 0; while (n > 1) { - i = sorted_blocks[--n]; - ir_block *bb = &blocks[i]; + b = sorted_blocks[--n]; + ir_block *bb = &blocks[b]; + + IR_ASSERT(bb->dom_depth <= prev_dom_depth); + if (UNEXPECTED(prev_irreducible) && bb->dom_depth != prev_dom_depth) { + /* process delyed irreducible loops */ + do { + b = sorted_blocks[prev_irreducible]; + bb = &blocks[b]; + if ((bb->flags & IR_BB_IRREDUCIBLE_LOOP) && !bb->loop_depth) { + /* process irreducible loop */ + uint32_t hdr = b; + + bb->loop_depth = 1; + if (ctx->ir_base[bb->start].op == IR_MERGE) { + ctx->ir_base[bb->start].op = IR_LOOP_BEGIN; + } + + /* find the closing edge(s) of the irreucible loop */ + IR_ASSERT(bb->predecessors_count > 1); + uint32_t *p = &edges[bb->predecessors]; + j = bb->predecessors_count; + do { + uint32_t pred = *p; + + if (entry_times[pred] > entry_times[b] && exit_times[pred] < exit_times[b]) { + if (!ir_worklist_len(&work)) { + ir_bitset_clear(work.visited, ir_bitset_len(ir_worklist_capasity(&work))); + } + blocks[pred].loop_header = 0; /* support for merged loops */ + ir_worklist_push(&work, pred); + } + p++; + } while (--j); + if (ir_worklist_len(&work) == 0) continue; + + /* collect members of the irreducible loop */ + while (ir_worklist_len(&work)) { + b = ir_worklist_pop(&work); + if (b != hdr) { + ir_block *bb = &blocks[b]; + bb->loop_header = hdr; + if (bb->predecessors_count) { + uint32_t *p = &edges[bb->predecessors]; + uint32_t n = bb->predecessors_count; + do { + uint32_t pred = *p; + while (blocks[pred].loop_header > 0) { + pred = blocks[pred].loop_header; + } + if (pred != hdr) { + if (entry_times[pred] > entry_times[hdr] && exit_times[pred] < exit_times[hdr]) { + /* "pred" is a descendant of "hdr" */ + ir_worklist_push(&work, pred); + } else { + /* another entry to the irreducible loop */ + bb->flags |= IR_BB_IRREDUCIBLE_LOOP; + if (ctx->ir_base[bb->start].op == IR_MERGE) { + ctx->ir_base[bb->start].op = IR_LOOP_BEGIN; + } + } + } + p++; + } while (--n); + } + } + } + } + } while (--prev_irreducible != n); + prev_irreducible = 0; + b = sorted_blocks[n]; + bb = &blocks[b]; + } if (bb->predecessors_count > 1) { bool irreducible = 0; @@ -1047,7 +1143,7 @@ int ir_find_loops(ir_ctx *ctx) if (bb->idom != pred) { /* In a loop back-edge (back-join edge), the successor dominates the predecessor. */ - if (ir_dominates(blocks, i, pred)) { + if (ir_dominates(blocks, b, pred)) { if (!ir_worklist_len(&work)) { ir_bitset_clear(work.visited, ir_bitset_len(ir_worklist_capasity(&work))); } @@ -1056,8 +1152,9 @@ int ir_find_loops(ir_ctx *ctx) } else { /* Otherwise it's a cross-join edge. See if it's a branch to an ancestor on the DJ spanning tree. */ - if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) { + if (entry_times[pred] > entry_times[b] && exit_times[pred] < exit_times[b]) { irreducible = 1; + break; } } } @@ -1065,46 +1162,56 @@ int ir_find_loops(ir_ctx *ctx) } while (--j); if (UNEXPECTED(irreducible)) { - // TODO: Support for irreducible loops ??? - bb->flags |= IR_BB_IRREDUCIBLE_LOOP; - ctx->flags2 |= IR_IRREDUCIBLE_CFG; - while (ir_worklist_len(&work)) { - ir_worklist_pop(&work); + bb->flags |= IR_BB_LOOP_HEADER | IR_BB_IRREDUCIBLE_LOOP; + ctx->flags2 |= IR_CFG_HAS_LOOPS | IR_IRREDUCIBLE_CFG; + /* Remember the position of the first irreducible loop to process all the irreducible loops + * after the reducible loops with the same dominator tree depth + */ + if (!prev_irreducible) { + prev_irreducible = n; + prev_dom_depth = bb->dom_depth; } + ir_list_clear(&work.l); } else if (ir_worklist_len(&work)) { + /* collect members of the reducible loop */ + uint32_t hdr = b; + bb->flags |= IR_BB_LOOP_HEADER; ctx->flags2 |= IR_CFG_HAS_LOOPS; bb->loop_depth = 1; + if (ctx->ir_base[bb->start].op == IR_MERGE) { + ctx->ir_base[bb->start].op = IR_LOOP_BEGIN; + } while (ir_worklist_len(&work)) { - j = ir_worklist_pop(&work); - while (blocks[j].loop_header > 0) { - j = blocks[j].loop_header; - } - if (j != i) { - ir_block *bb = &blocks[j]; - if (bb->idom == 0 && j != 1) { - /* Ignore blocks that are unreachable or only abnormally reachable. */ - continue; - } - bb->loop_header = i; + b = ir_worklist_pop(&work); + if (b != hdr) { + ir_block *bb = &blocks[b]; + bb->loop_header = hdr; if (bb->predecessors_count) { uint32_t *p = &edges[bb->predecessors]; - j = bb->predecessors_count; + uint32_t n = bb->predecessors_count; do { - ir_worklist_push(&work, *p); + uint32_t pred = *p; + while (blocks[pred].loop_header > 0) { + pred = blocks[pred].loop_header; + } + if (pred != hdr) { + ir_worklist_push(&work, pred); + } p++; - } while (--j); + } while (--n); } } } } } } + IR_ASSERT(!prev_irreducible); if (ctx->flags2 & IR_CFG_HAS_LOOPS) { for (n = 1; n < count; n++) { - i = sorted_blocks[n]; - ir_block *bb = &blocks[i]; + b = sorted_blocks[n]; + ir_block *bb = &blocks[b]; if (bb->loop_header > 0) { ir_block *loop = &blocks[bb->loop_header]; uint32_t loop_depth = loop->loop_depth; @@ -1389,7 +1496,7 @@ static int ir_schedule_blocks_bottom_up(ir_ctx *ctx) goto restart; } } else if (b != predecessor && ctx->cfg_blocks[predecessor].loop_header != b) { - ir_dump_cfg(ctx, stderr); + /* not a loop back-edge */ IR_ASSERT(b == predecessor || ctx->cfg_blocks[predecessor].loop_header == b); } } diff --git a/ext/opcache/jit/ir/ir_check.c b/ext/opcache/jit/ir/ir_check.c index f12b4776fa1e0..a791baef5db92 100644 --- a/ext/opcache/jit/ir/ir_check.c +++ b/ext/opcache/jit/ir/ir_check.c @@ -213,13 +213,18 @@ bool ir_check(const ir_ctx *ctx) ok = 0; } } - break; - case IR_OPND_CONTROL_DEP: if ((ctx->flags2 & IR_LINEAR) && use >= i && !(insn->op == IR_LOOP_BEGIN)) { fprintf(stderr, "ir_base[%d].ops[%d] invalid forward reference (%d)\n", i, j, use); ok = 0; + } + break; + case IR_OPND_CONTROL_DEP: + if ((ctx->flags2 & IR_LINEAR) + && use >= i) { + fprintf(stderr, "ir_base[%d].ops[%d] invalid forward reference (%d)\n", i, j, use); + ok = 0; } else if (insn->op == IR_PHI) { ir_insn *merge_insn = &ctx->ir_base[insn->op1]; if (merge_insn->op != IR_MERGE && merge_insn->op != IR_LOOP_BEGIN) { diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index c82655daf48db..fab9f56228d80 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -309,7 +309,7 @@ static void* ir_sym_addr(ir_ctx *ctx, const ir_insn *addr_insn) { const char *name = ir_get_str(ctx, addr_insn->val.name); void *addr = (ctx->loader && ctx->loader->resolve_sym_name) ? - ctx->loader->resolve_sym_name(ctx->loader, name, 0) : + ctx->loader->resolve_sym_name(ctx->loader, name, IR_RESOLVE_SYM_SILENT) : ir_resolve_sym_name(name); return addr; @@ -320,7 +320,7 @@ static void* ir_sym_val(ir_ctx *ctx, const ir_insn *addr_insn) { const char *name = ir_get_str(ctx, addr_insn->val.name); void *addr = (ctx->loader && ctx->loader->resolve_sym_name) ? - ctx->loader->resolve_sym_name(ctx->loader, name, addr_insn->op == IR_FUNC) : + ctx->loader->resolve_sym_name(ctx->loader, name, addr_insn->op == IR_FUNC ? IR_RESOLVE_SYM_ADD_THUNK : 0) : ir_resolve_sym_name(name); IR_ASSERT(addr); diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 88539e52ab085..90112214d0c8b 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -1909,7 +1909,9 @@ IR_FOLD(SUB(_, SUB)) IR_FOLD(SUB(ADD, ADD)) { if (IR_IS_TYPE_INT(IR_OPT_TYPE(opt))) { - if (op1_insn->op1 == op2_insn->op1) { + if (op1 == op2) { + IR_FOLD_CONST_U(0); + } else if (op1_insn->op1 == op2_insn->op1) { /* (a + b) - (a + c) => b - c */ op1 = op1_insn->op2; op2 = op2_insn->op2; diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index 8bd6be5d10aa0..4d518d20079fb 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -785,6 +785,139 @@ IR_ALWAYS_INLINE ir_ref ir_count_constant(ir_ref *_xlat, ir_ref ref) return 0; } +IR_ALWAYS_INLINE bool ir_is_good_bb_order(ir_ctx *ctx, uint32_t b, ir_block *bb, ir_ref start) +{ + ir_insn *insn = &ctx->ir_base[start]; + uint32_t n = insn->inputs_count; + ir_ref *p = insn->ops + 1; + + if (n == 1) { + return *p < start; + } else { + IR_ASSERT(n > 1); + for (; n > 0; p++, n--) { + ir_ref input = *p; + if (input < start) { + /* ordered */ + } else if ((bb->flags & IR_BB_LOOP_HEADER) + && (ctx->cfg_map[input] == b || ctx->cfg_blocks[ctx->cfg_map[input]].loop_header == b)) { + /* back-edge of reducible loop */ + } else if ((bb->flags & IR_BB_IRREDUCIBLE_LOOP) + && (ctx->cfg_blocks[ctx->cfg_map[input]].loop_header == ctx->cfg_blocks[b].loop_header)) { + /* closing edge of irreducible loop */ + } else { + return 0; + } + } + return 1; + } +} + +static IR_NEVER_INLINE void ir_fix_bb_order(ir_ctx *ctx, ir_ref *_prev, ir_ref *_next) +{ + uint32_t b, succ, count, *q, *xlat; + ir_block *bb; + ir_ref ref, n, prev; + ir_worklist worklist; + ir_block *new_blocks; + +#if 0 + for (b = 1, bb = ctx->cfg_blocks + 1; b <= ctx->cfg_blocks_count; b++, bb++) { + if (!ir_is_good_bb_order(ctx, b, bb, bb->start)) { + goto fix; + } + } + return; + +fix: +#endif + count = ctx->cfg_blocks_count + 1; + new_blocks = ir_mem_malloc(count * sizeof(ir_block)); + xlat = ir_mem_malloc(count * sizeof(uint32_t)); + ir_worklist_init(&worklist, count); + ir_worklist_push(&worklist, 1); + while (ir_worklist_len(&worklist) != 0) { +next: + b = ir_worklist_peek(&worklist); + bb = &ctx->cfg_blocks[b]; + n = bb->successors_count; + if (n == 1) { + succ = ctx->cfg_edges[bb->successors]; + if (ir_worklist_push(&worklist, succ)) { + goto next; + } + } else if (n > 1) { + uint32_t best = 0; + uint32_t best_loop_depth = 0; + + q = ctx->cfg_edges + bb->successors + n; + do { + q--; + succ = *q; + if (ir_bitset_in(worklist.visited, succ)) { + /* already processed */ + } else if ((ctx->cfg_blocks[succ].flags & IR_BB_LOOP_HEADER) + && (succ == b || ctx->cfg_blocks[b].loop_header == succ)) { + /* back-edge of reducible loop */ + } else if ((ctx->cfg_blocks[succ].flags & IR_BB_IRREDUCIBLE_LOOP) + && (ctx->cfg_blocks[succ].loop_header == ctx->cfg_blocks[b].loop_header)) { + /* closing edge of irreducible loop */ + } else if (!best) { + best = succ; + best_loop_depth = ctx->cfg_blocks[best].loop_depth; + } else if (ctx->cfg_blocks[succ].loop_depth < best_loop_depth) { + /* prefer deeper loop */ + best = succ; + best_loop_depth = ctx->cfg_blocks[best].loop_depth; + } + n--; + } while (n > 0); + if (best) { + ir_worklist_push(&worklist, best); + goto next; + } + } + ir_worklist_pop(&worklist); + count--; + new_blocks[count] = *bb; + xlat[b] = count; + } + IR_ASSERT(count == 1); + xlat[0] = 0; + ir_worklist_free(&worklist); + + prev = 0; + for (b = 1, bb = new_blocks + 1; b <= ctx->cfg_blocks_count; b++, bb++) { + bb->idom = xlat[bb->idom]; + bb->loop_header = xlat[bb->loop_header]; + n = bb->successors_count; + if (n > 0) { + for (q = ctx->cfg_edges + bb->successors; n > 0; q++, n--) { + *q = xlat[*q]; + } + } + n = bb->predecessors_count; + if (n > 0) { + for (q = ctx->cfg_edges + bb->predecessors; n > 0; q++, n--) { + *q = xlat[*q]; + } + } + _next[prev] = bb->start; + _prev[bb->start] = prev; + prev = bb->end; + } + _next[0] = 0; + _next[prev] = 0; + + for (ref = 2; ref < ctx->insns_count; ref++) { + ctx->cfg_map[ref] = xlat[ctx->cfg_map[ref]]; + } + ir_mem_free(xlat); + + ir_mem_free(ctx->cfg_blocks); + ctx->cfg_blocks = new_blocks; +} + int ir_schedule(ir_ctx *ctx) { ir_ctx new_ctx; @@ -800,6 +933,7 @@ int ir_schedule(ir_ctx *ctx) ir_block *bb; ir_insn *insn, *new_insn; ir_use_list *lists, *use_list, *new_list; + bool bad_bb_order = 0; /* Create a double-linked list of nodes ordered by BB, respecting BB->start and BB->end */ IR_ASSERT(_blocks[1] == 1); @@ -818,27 +952,61 @@ int ir_schedule(ir_ctx *ctx) } else if (b > prev_b) { bb = &ctx->cfg_blocks[b]; if (i == bb->start) { - IR_ASSERT(bb->end > bb->start); - prev_b = b; - prev_b_end = bb->end; - _prev[bb->end] = 0; - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else { - IR_ASSERT(i != bb->end); + if (bb->end > bb->start) { + prev_b = b; + prev_b_end = bb->end; + /* add to the end of the list */ + _next[j] = i; + _prev[i] = j; + j = i; + } else { + prev_b = 0; + prev_b_end = 0; + k = bb->end; + while (_blocks[_prev[k]] == b) { + k = _prev[k]; + } + /* insert before "k" */ + _prev[i] = _prev[k]; + _next[i] = k; + _next[_prev[k]] = i; + _prev[k] = i; + } + if (!ir_is_good_bb_order(ctx, b, bb, i)) { + bad_bb_order = 1; + } + } else if (i != bb->end) { /* move down late (see the following loop) */ _next[i] = _move_down; _move_down = i; + } else { + prev_b = 0; + prev_b_end = 0; + if (bb->start > bb->end) { + /* add to the end of the list */ + _next[j] = i; + _prev[i] = j; + j = i; + } else { + k = bb->start; + while (_blocks[_next[k]] == b) { + k = _next[k]; + } + /* insert after "k" */ + _next[i] = _next[k]; + _prev[i] = k; + _prev[_next[k]] = i; + _next[k] = i; + } } } else if (b) { bb = &ctx->cfg_blocks[b]; IR_ASSERT(i != bb->start); - if (_prev[bb->end]) { + if (i > bb->end) { /* move up, insert before the end of the already scheduled BB */ k = bb->end; } else { + IR_ASSERT(i > bb->start); /* move up, insert at the end of the block */ k = ctx->cfg_blocks[b + 1].start; } @@ -883,6 +1051,10 @@ int ir_schedule(ir_ctx *ctx) } #endif + if (bad_bb_order) { + ir_fix_bb_order(ctx, _prev, _next); + } + _xlat = ir_mem_calloc((ctx->consts_count + ctx->insns_count), sizeof(ir_ref)); _xlat += ctx->consts_count; _xlat[IR_TRUE] = IR_TRUE; @@ -904,6 +1076,11 @@ int ir_schedule(ir_ctx *ctx) if (insn->op == IR_CASE_VAL) { IR_ASSERT(insn->op2 < IR_TRUE); consts_count += ir_count_constant(_xlat, insn->op2); + } else if (insn->op == IR_CASE_RANGE) { + IR_ASSERT(insn->op2 < IR_TRUE); + consts_count += ir_count_constant(_xlat, insn->op2); + IR_ASSERT(insn->op3 < IR_TRUE); + consts_count += ir_count_constant(_xlat, insn->op3); } n = insn->inputs_count; insns_count += ir_insn_inputs_to_len(n); diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 69a0101d24ee2..369b4c34e3793 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -9,6 +9,7 @@ #define IR_PRIVATE_H #include #include +#include #ifdef IR_DEBUG # include @@ -62,7 +63,7 @@ #define IR_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define IR_MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define IR_IS_POWER_OF_TWO(x) (!((x) & ((x) - 1))) +#define IR_IS_POWER_OF_TWO(x) ((x) && (!((x) & ((x) - 1)))) #define IR_LOG2(x) ir_ntzl(x) @@ -257,7 +258,7 @@ IR_ALWAYS_INLINE void* ir_arena_alloc(ir_arena **arena_ptr, size_t size) ir_arena *arena = *arena_ptr; char *ptr = (char*)IR_ALIGNED_SIZE((uintptr_t)arena->ptr, 8); - if (EXPECTED(size <= (size_t)(arena->end - ptr))) { + if (EXPECTED((ptrdiff_t)size <= (ptrdiff_t)(arena->end - ptr))) { arena->ptr = ptr + size; } else { size_t arena_size = diff --git a/ext/opcache/jit/ir/ir_save.c b/ext/opcache/jit/ir/ir_save.c index b12cc267af607..ea787f162ec1f 100644 --- a/ext/opcache/jit/ir/ir_save.c +++ b/ext/opcache/jit/ir/ir_save.c @@ -97,10 +97,14 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) { fprintf(f, "\t%s c_%d = ", ir_type_cname[insn->type], i); if (insn->op == IR_FUNC) { - fprintf(f, "func %s", ir_get_str(ctx, insn->val.name)); + fprintf(f, "func %s%s", + (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "", + ir_get_str(ctx, insn->val.name)); ir_print_proto(ctx, insn->proto, f); } else if (insn->op == IR_SYM) { - fprintf(f, "sym(%s)", ir_get_str(ctx, insn->val.name)); + fprintf(f, "sym(%s%s)", + (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "", + ir_get_str(ctx, insn->val.name)); } else if (insn->op == IR_FUNC_ADDR) { fprintf(f, "func *"); ir_print_const(ctx, insn, f, true); @@ -140,6 +144,9 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) fprintf(f, ", loop=BB%d(%d)", bb->loop_header, bb->loop_depth); } } + if (bb->flags & IR_BB_IRREDUCIBLE_LOOP) { + fprintf(f, ", IRREDUCIBLE"); + } if (bb->predecessors_count) { uint32_t i; diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 2e006516df818..ae0eebadee54a 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -458,6 +458,22 @@ static bool ir_sccp_is_equal(ir_ctx *ctx, ir_insn *_values, ir_ref a, ir_ref b) return v1->val.u64 == v2->val.u64; } +static bool ir_sccp_in_range(ir_ctx *ctx, ir_insn *_values, ir_ref a, ir_ref b, ir_ref c) +{ + ir_insn *v1 = IR_IS_CONST_REF(a) ? &ctx->ir_base[a] : &_values[a]; + ir_insn *v2 = IR_IS_CONST_REF(b) ? &ctx->ir_base[b] : &_values[b]; + ir_insn *v3 = IR_IS_CONST_REF(c) ? &ctx->ir_base[c] : &_values[c]; + + IR_ASSERT(!IR_IS_SYM_CONST(v1->op)); + IR_ASSERT(!IR_IS_SYM_CONST(v2->op)); + IR_ASSERT(!IR_IS_SYM_CONST(v3->op)); + if (IR_IS_TYPE_SIGNED(v1->type)) { + return v1->val.i64 >= v2->val.i64 && v1->val.i64 <= v3->val.i64; + } else { + return v1->val.u64 >= v2->val.u64 && v1->val.u64 <= v3->val.u64; + } +} + #ifdef IR_SCCP_TRACE static void ir_sccp_trace_val(ir_ctx *ctx, ir_insn *_values, ir_ref i) { @@ -676,6 +692,11 @@ static IR_NEVER_INLINE void ir_sccp_analyze(ir_ctx *ctx, ir_insn *_values, ir_bi } } else if (use_insn->op == IR_CASE_DEFAULT) { use_case = use; + } else if (use_insn->op == IR_CASE_RANGE) { + if (ir_sccp_in_range(ctx, _values, insn->op2, use_insn->op2, use_insn->op3)) { + use_case = use; + break; + } } } if (use_case) { @@ -1732,7 +1753,20 @@ static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use, ir_ref *p, n, input; if (IR_IS_CONST_REF(ref)) { - return ir_const(ctx, insn->val, type); + ir_val val; + + switch (type) { + case IR_I8: val.i64 = insn->val.i8; break; + case IR_U8: val.u64 = insn->val.u8; break; + case IR_I16: val.i64 = insn->val.i16; break; + case IR_U16: val.u64 = insn->val.u16; break; + case IR_I32: val.i64 = insn->val.i32; break; + case IR_U32: val.u64 = insn->val.u32; break; + case IR_CHAR:val.i64 = insn->val.i8; break; + case IR_BOOL:val.u64 = insn->val.u8 != 0; break; + default: IR_ASSERT(0); val.u64 = 0; + } + return ir_const(ctx, val, type); } else { ir_bitqueue_add(worklist, ref); switch (insn->op) { @@ -2391,7 +2425,7 @@ static bool ir_try_remove_empty_diamond(ir_ctx *ctx, ir_ref ref, ir_insn *insn, } start_ref = end->op1; start = &ctx->ir_base[start_ref]; - if (start->op != IR_CASE_VAL && start->op != IR_CASE_DEFAULT) { + if (start->op != IR_CASE_VAL && start->op != IR_CASE_RANGE && start->op != IR_CASE_DEFAULT) { return 0; } if (ctx->use_lists[start_ref].count != 1) { diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index 76602c2b4bcf5..d9967d24dbea2 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -1569,6 +1569,20 @@ op2_const: constraints->tmp_regs[0] = IR_TMP_REG(1, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n = 1; break; + case IR_SSE_SQRT: + case IR_SSE_RINT: + case IR_SSE_FLOOR: + case IR_SSE_CEIL: + case IR_SSE_TRUNC: + case IR_SSE_NEARBYINT: + insn = &ctx->ir_base[ref]; + flags = IR_USE_MUST_BE_IN_REG | IR_OP3_MUST_BE_IN_REG; + if (IR_IS_CONST_REF(insn->op3)) { + const ir_insn *val_insn = &ctx->ir_base[insn->op3]; + constraints->tmp_regs[n] = IR_TMP_REG(3, val_insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n = 1; + } + break; } constraints->tmps_count = n; @@ -2630,6 +2644,7 @@ store_int: case IR_IF_TRUE: case IR_IF_FALSE: case IR_CASE_VAL: + case IR_CASE_RANGE: case IR_CASE_DEFAULT: case IR_MERGE: case IR_LOOP_BEGIN: @@ -6868,7 +6883,24 @@ static void ir_emit_return_fp(ir_ctx *ctx, ir_ref ref, ir_insn *insn) ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; - if (op2_reg == IR_REG_NONE || IR_REG_SPILLED(op2_reg)) { + if (IR_IS_CONST_REF(insn->op2)) { + ir_insn *value = &ctx->ir_base[insn->op2]; + + if ((type == IR_FLOAT && value->val.f == 0.0) || (type == IR_DOUBLE && value->val.d == 0.0)) { + | fldz + } else if ((type == IR_FLOAT && value->val.f == 1.0) || (type == IR_DOUBLE && value->val.d == 1.0)) { + | fld1 + } else { + int label = ir_const_label(ctx, insn->op2); + + if (type == IR_DOUBLE) { + | fld qword [=>label] + } else { + IR_ASSERT(type == IR_FLOAT); + | fld dword [=>label] + } + } + } else if (op2_reg == IR_REG_NONE || IR_REG_SPILLED(op2_reg)) { ir_reg fp; int32_t offset = ir_ref_spill_slot_offset(ctx, insn->op2, &fp); @@ -8442,11 +8474,15 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; ir_type type = insn->type; - ir_reg def_reg = ctx->regs[def][0]; + ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]); ir_reg op2_reg = ctx->regs[def][2]; ir_reg tmp_reg = ctx->regs[def][3]; int32_t offset; + if (ctx->use_lists[def].count == 1) { + /* dead load */ + return; + } IR_ASSERT(def_reg != IR_REG_NONE && tmp_reg != IR_REG_NONE); if (op2_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op2_reg)) { @@ -8471,11 +8507,15 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) ir_backend_data *data = ctx->data; dasm_State **Dst = &data->dasm_state; ir_type type = insn->type; - ir_reg def_reg = ctx->regs[def][0]; + ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]); ir_reg op2_reg = ctx->regs[def][2]; ir_reg tmp_reg = ctx->regs[def][3]; int32_t offset; + if (ctx->use_lists[def].count == 1) { + /* dead load */ + return; + } IR_ASSERT(def_reg != IR_REG_NONE&& tmp_reg != IR_REG_NONE); if (op2_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op2_reg)) { @@ -8541,6 +8581,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) ir_val min, max; ir_reg op2_reg = ctx->regs[def][2]; ir_reg tmp_reg = ctx->regs[def][3]; + bool has_case_range = 0; type = ctx->ir_base[insn->op2].type; IR_ASSERT(tmp_reg != IR_REG_NONE); @@ -8570,6 +8611,21 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) max.u64 = (int64_t)IR_MAX(max.u64, val->val.u64); } count++; + } else if (use_insn->op == IR_CASE_RANGE) { + has_case_range = 1; + val = &ctx->ir_base[use_insn->op2]; + IR_ASSERT(!IR_IS_SYM_CONST(val->op)); + ir_insn *val2 = &ctx->ir_base[use_insn->op3]; + IR_ASSERT(!IR_IS_SYM_CONST(val2->op)); + if (IR_IS_TYPE_SIGNED(type)) { + IR_ASSERT(IR_IS_TYPE_SIGNED(val->type)); + min.i64 = IR_MIN(min.i64, val->val.i64); + max.i64 = IR_MAX(max.i64, val2->val.i64); + } else { + IR_ASSERT(!IR_IS_TYPE_SIGNED(val->type)); + min.u64 = (int64_t)IR_MIN(min.u64, val->val.u64); + max.u64 = (int64_t)IR_MAX(max.u64, val2->val.u64); + } } else { IR_ASSERT(use_insn->op == IR_CASE_DEFAULT); default_label = ir_skip_empty_target_blocks(ctx, use_block); @@ -8583,7 +8639,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } /* Generate a table jmp or a seqence of calls */ - if (count > 2 && (max.i64-min.i64) < count * 8) { + if (!has_case_range && count > 2 && (max.i64-min.i64) < count * 8) { int *labels = ir_mem_malloc(sizeof(int) * (size_t)(max.i64 - min.i64 + 1)); for (i = 0; i <= (max.i64 - min.i64); i++) { @@ -8747,6 +8803,42 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) |.endif } | je =>label + } else if (use_insn->op == IR_CASE_RANGE) { + val = &ctx->ir_base[use_insn->op2]; + IR_ASSERT(!IR_IS_SYM_CONST(val->op)); + label = ir_skip_empty_target_blocks(ctx, use_block); + if (IR_IS_32BIT(type, val->val)) { + | ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i32 + } else { + IR_ASSERT(sizeof(void*) == 8); +|.if X64 + | mov64 Ra(tmp_reg), val->val.i64 + | ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg +|.endif + } + if (IR_IS_TYPE_SIGNED(type)) { + | jl >1 + } else { + | jb >1 + } + val = &ctx->ir_base[use_insn->op3]; + IR_ASSERT(!IR_IS_SYM_CONST(val->op3)); + label = ir_skip_empty_target_blocks(ctx, use_block); + if (IR_IS_32BIT(type, val->val)) { + | ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i32 + } else { + IR_ASSERT(sizeof(void*) == 8); +|.if X64 + | mov64 Ra(tmp_reg), val->val.i64 + | ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg +|.endif + } + if (IR_IS_TYPE_SIGNED(type)) { + | jle =>label + } else { + | jbe =>label + } + |1: } } if (default_label) { @@ -9221,6 +9313,58 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn) return; } + /* Move op2 to a tmp register before epilogue if it's in + * used_preserved_regs, because it will be overridden. */ + + ir_reg op2_reg = IR_REG_NONE; + ir_mem mem = IR_MEM_B(IR_REG_NONE); + if (!IR_IS_CONST_REF(insn->op2)) { + op2_reg = ctx->regs[def][2]; + + ir_regset preserved_regs = (ir_regset)ctx->used_preserved_regs | IR_REGSET(IR_REG_STACK_POINTER); + if (ctx->flags & IR_USE_FRAME_POINTER) { + preserved_regs |= IR_REGSET(IR_REG_FRAME_POINTER); + } + + bool is_spill_slot = op2_reg != IR_REG_NONE + && IR_REG_SPILLED(op2_reg) + && ctx->vregs[insn->op2]; + + if (op2_reg != IR_REG_NONE && !is_spill_slot) { + if (IR_REGSET_IN(preserved_regs, IR_REG_NUM(op2_reg))) { + ir_ref orig_op2_reg = op2_reg; + op2_reg = IR_REG_RAX; + + if (IR_REG_SPILLED(orig_op2_reg)) { + ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2); + } else { + ir_type type = ctx->ir_base[insn->op2].type; + | ASM_REG_REG_OP mov, type, op2_reg, IR_REG_NUM(orig_op2_reg) + } + } else { + op2_reg = IR_REG_NUM(op2_reg); + } + } else { + if (ir_rule(ctx, insn->op2) & IR_FUSED) { + IR_ASSERT(op2_reg == IR_REG_NONE); + mem = ir_fuse_load(ctx, def, insn->op2); + } else { + mem = ir_ref_spill_slot(ctx, insn->op2); + } + ir_reg base = IR_MEM_BASE(mem); + ir_reg index = IR_MEM_INDEX(mem); + if ((base != IR_REG_NONE && IR_REGSET_IN(preserved_regs, base)) || + (index != IR_REG_NONE && IR_REGSET_IN(preserved_regs, index))) { + op2_reg = IR_REG_RAX; + + ir_type type = ctx->ir_base[insn->op2].type; + ir_emit_load_mem_int(ctx, type, op2_reg, mem); + } else { + op2_reg = IR_REG_NONE; + } + } + } + ir_emit_epilogue(ctx); if (IR_IS_CONST_REF(insn->op2)) { @@ -9246,22 +9390,10 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn) |.endif } } else { - ir_reg op2_reg = ctx->regs[def][2]; - if (op2_reg != IR_REG_NONE) { - if (IR_REG_SPILLED(op2_reg)) { - op2_reg = IR_REG_NUM(op2_reg); - ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2); - } + IR_ASSERT(!IR_REGSET_IN((ir_regset)ctx->used_preserved_regs, op2_reg)); | jmp Ra(op2_reg) } else { - ir_mem mem; - - if (ir_rule(ctx, insn->op2) & IR_FUSED) { - mem = ir_fuse_load(ctx, def, insn->op2); - } else { - mem = ir_ref_spill_slot(ctx, insn->op2); - } | ASM_TMEM_OP jmp, aword, mem } } @@ -10314,6 +10446,7 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) case IR_IF_TRUE: case IR_IF_FALSE: case IR_CASE_VAL: + case IR_CASE_RANGE: case IR_CASE_DEFAULT: case IR_MERGE: case IR_LOOP_BEGIN: From 17b8706bf62a65c5e2e0690f7c18807a8553962f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 11:56:39 +0100 Subject: [PATCH 06/25] streams: add const specifier --- ext/standard/streamsfuncs.c | 17 ++++++++--------- main/php_streams.h | 2 +- main/streams/filter.c | 2 +- main/streams/php_stream_context.h | 2 +- main/streams/php_stream_filter_api.h | 2 +- main/streams/streams.c | 19 ++++++++++--------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index bf7cbc9fa4623..a9a024b1df0f3 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -568,13 +568,13 @@ PHP_FUNCTION(stream_get_meta_data) /* {{{ Retrieves list of registered socket transports */ PHP_FUNCTION(stream_get_transports) { - HashTable *stream_xport_hash; - zend_string *stream_xport; ZEND_PARSE_PARAMETERS_NONE(); - stream_xport_hash = php_stream_xport_get_hash(); array_init(return_value); + + const HashTable *stream_xport_hash = php_stream_xport_get_hash(); + zend_string *stream_xport; ZEND_HASH_MAP_FOREACH_STR_KEY(stream_xport_hash, stream_xport) { add_next_index_str(return_value, zend_string_copy(stream_xport)); } ZEND_HASH_FOREACH_END(); @@ -584,13 +584,12 @@ PHP_FUNCTION(stream_get_transports) /* {{{ Retrieves list of registered stream wrappers */ PHP_FUNCTION(stream_get_wrappers) { - HashTable *url_stream_wrappers_hash; - zend_string *stream_protocol; - ZEND_PARSE_PARAMETERS_NONE(); - url_stream_wrappers_hash = php_stream_get_url_stream_wrappers_hash(); array_init(return_value); + + const HashTable *url_stream_wrappers_hash = php_stream_get_url_stream_wrappers_hash(); + zend_string *stream_protocol; ZEND_HASH_MAP_FOREACH_STR_KEY(url_stream_wrappers_hash, stream_protocol) { if (stream_protocol) { add_next_index_str(return_value, zend_string_copy(stream_protocol)); @@ -882,7 +881,7 @@ static void user_space_stream_notifier_dtor(php_stream_notifier *notifier) notifier->ptr = NULL; } -static zend_result parse_context_options(php_stream_context *context, HashTable *options) +static zend_result parse_context_options(php_stream_context *context, const HashTable *options) { zval *wval, *oval; zend_string *wkey, *okey; @@ -906,7 +905,7 @@ static zend_result parse_context_options(php_stream_context *context, HashTable return SUCCESS; } -static zend_result parse_context_params(php_stream_context *context, HashTable *params) +static zend_result parse_context_params(php_stream_context *context, const HashTable *params) { zval *tmp; diff --git a/main/php_streams.h b/main/php_streams.h index 81fba301c6834..ca6172e764bd4 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -361,7 +361,7 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence); #define php_stream_rewind(stream) _php_stream_seek((stream), 0L, SEEK_SET) #define php_stream_seek(stream, offset, whence) _php_stream_seek((stream), (offset), (whence)) -PHPAPI zend_off_t _php_stream_tell(php_stream *stream); +PHPAPI zend_off_t _php_stream_tell(const php_stream *stream); #define php_stream_tell(stream) _php_stream_tell((stream)) PHPAPI ssize_t _php_stream_read(php_stream *stream, char *buf, size_t count); diff --git a/main/streams/filter.c b/main/streams/filter.c index abfc5c26ae12d..a68da91c4851e 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -68,7 +68,7 @@ PHPAPI int php_stream_filter_register_factory_volatile(zend_string *filterpatter /* Buckets */ -PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent) +PHPAPI php_stream_bucket *php_stream_bucket_new(const php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent) { int is_persistent = php_stream_is_persistent(stream); php_stream_bucket *bucket; diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index b983bbb10efe2..56574704704cb 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -59,7 +59,7 @@ BEGIN_EXTERN_C() PHPAPI int php_le_stream_context(void); PHPAPI void php_stream_context_free(php_stream_context *context); PHPAPI php_stream_context *php_stream_context_alloc(void); -PHPAPI zval *php_stream_context_get_option(php_stream_context *context, +PHPAPI zval *php_stream_context_get_option(const php_stream_context *context, const char *wrappername, const char *optionname); PHPAPI void php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); diff --git a/main/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h index ee6ab75f38c23..3144697455a08 100644 --- a/main/streams/php_stream_filter_api.h +++ b/main/streams/php_stream_filter_api.h @@ -63,7 +63,7 @@ typedef enum { /* Buckets API. */ BEGIN_EXTERN_C() -PHPAPI php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent); +PHPAPI php_stream_bucket *php_stream_bucket_new(const php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent); PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length); PHPAPI void php_stream_bucket_delref(php_stream_bucket *bucket); #define php_stream_bucket_addref(bucket) (bucket)->refcount++ diff --git a/main/streams/streams.c b/main/streams/streams.c index 2eef790863f61..4488aba59b2ee 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1041,12 +1041,13 @@ PHPAPI char *_php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, #define STREAM_BUFFERED_AMOUNT(stream) \ ((size_t)(((stream)->writepos) - (stream)->readpos)) -static const char *_php_stream_search_delim(php_stream *stream, - size_t maxlen, - size_t skiplen, - const char *delim, /* non-empty! */ - size_t delim_len) -{ +static const char *_php_stream_search_delim( + const php_stream *stream, + size_t maxlen, + size_t skiplen, + const char *delim, /* non-empty! */ + size_t delim_len +) { size_t seek_len; /* set the maximum number of bytes we're allowed to read from buffer */ @@ -1340,7 +1341,7 @@ PHPAPI ssize_t _php_stream_printf(php_stream *stream, const char *fmt, ...) return count; } -PHPAPI zend_off_t _php_stream_tell(php_stream *stream) +PHPAPI zend_off_t _php_stream_tell(const php_stream *stream) { return stream->position; } @@ -1975,7 +1976,7 @@ PHPAPI zend_result php_unregister_url_stream_wrapper_volatile(zend_string *proto /* {{{ php_stream_locate_url_wrapper */ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const char **path_for_open, int options) { - HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); + const HashTable *wrapper_hash = (FG(stream_wrappers) ? FG(stream_wrappers) : &url_stream_wrappers_hash); php_stream_wrapper *wrapper = NULL; const char *p, *protocol = NULL; size_t n = 0; @@ -2416,7 +2417,7 @@ PHPAPI void php_stream_notification_free(php_stream_notifier *notifier) efree(notifier); } -PHPAPI zval *php_stream_context_get_option(php_stream_context *context, +PHPAPI zval *php_stream_context_get_option(const php_stream_context *context, const char *wrappername, const char *optionname) { zval *wrapperhash; From b35dbe474ee114ebca3e58b8351a150b2a788bc6 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 11:57:11 +0100 Subject: [PATCH 07/25] streams: reduce scope of variables --- ext/standard/streamsfuncs.c | 6 ++---- main/streams/streams.c | 13 ++++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index a9a024b1df0f3..1070e3be5d428 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -99,7 +99,6 @@ PHP_FUNCTION(stream_socket_client) zval *zerrno = NULL, *zerrstr = NULL, *zcontext = NULL; double timeout; bool timeout_is_null = 1; - php_timeout_ull conv; struct timeval tv; char *hashkey = NULL; php_stream *stream = NULL; @@ -138,7 +137,7 @@ PHP_FUNCTION(stream_socket_client) if (timeout < 0.0 || timeout >= (double) PHP_TIMEOUT_ULL_MAX / 1000000.0) { tv_pointer = NULL; } else { - conv = (php_timeout_ull) (timeout * 1000000.0); + php_timeout_ull conv = (php_timeout_ull) (timeout * 1000000.0); #ifdef PHP_WIN32 tv.tv_sec = (long)(conv / 1000000); tv.tv_usec = (long)(conv % 1000000); @@ -262,7 +261,6 @@ PHP_FUNCTION(stream_socket_accept) bool timeout_is_null = 1; zval *zpeername = NULL; zend_string *peername = NULL; - php_timeout_ull conv; struct timeval tv; php_stream *stream = NULL, *clistream = NULL; zend_string *errstr = NULL; @@ -286,7 +284,7 @@ PHP_FUNCTION(stream_socket_accept) if (timeout < 0.0 || timeout >= (double) PHP_TIMEOUT_ULL_MAX / 1000000.0) { tv_pointer = NULL; } else { - conv = (php_timeout_ull) (timeout * 1000000.0); + php_timeout_ull conv = (php_timeout_ull) (timeout * 1000000.0); #ifdef PHP_WIN32 tv.tv_sec = (long)(conv / 1000000); tv.tv_usec = (long)(conv % 1000000); diff --git a/main/streams/streams.c b/main/streams/streams.c index 4488aba59b2ee..7fe2ef215b80d 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -880,7 +880,7 @@ PHPAPI int _php_stream_stat(php_stream *stream, php_stream_statbuf *ssb) PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf) { size_t avail; - const char *cr, *lf, *eol = NULL; + const char *eol = NULL; const char *readptr; if (!buf) { @@ -893,8 +893,8 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf) /* Look for EOL */ if (stream->flags & PHP_STREAM_FLAG_DETECT_EOL) { - cr = memchr(readptr, '\r', avail); - lf = memchr(readptr, '\n', avail); + const char *cr = memchr(readptr, '\r', avail); + const char *lf = memchr(readptr, '\n', avail); if (cr && lf != cr + 1 && !(lf && lf < cr)) { /* mac */ @@ -1218,16 +1218,15 @@ static ssize_t _php_stream_write_filtered(php_stream *stream, const char *buf, s size_t consumed = 0; php_stream_bucket *bucket; php_stream_bucket_brigade brig_in = { NULL, NULL }, brig_out = { NULL, NULL }; - php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out, *brig_swap; + php_stream_bucket_brigade *brig_inp = &brig_in, *brig_outp = &brig_out; php_stream_filter_status_t status = PSFS_ERR_FATAL; - php_stream_filter *filter; if (buf) { bucket = php_stream_bucket_new(stream, (char *)buf, count, 0, 0); php_stream_bucket_append(&brig_in, bucket); } - for (filter = stream->writefilters.head; filter; filter = filter->next) { + for (php_stream_filter *filter = stream->writefilters.head; filter; filter = filter->next) { /* for our return value, we are interested in the number of bytes consumed from * the first filter in the chain */ status = filter->fops->filter(stream, filter, brig_inp, brig_outp, @@ -1239,7 +1238,7 @@ static ssize_t _php_stream_write_filtered(php_stream *stream, const char *buf, s /* brig_out becomes brig_in. * brig_in will always be empty here, as the filter MUST attach any un-consumed buckets * to its own brigade */ - brig_swap = brig_inp; + php_stream_bucket_brigade *brig_swap = brig_inp; brig_inp = brig_outp; brig_outp = brig_swap; memset(brig_outp, 0, sizeof(*brig_outp)); From b3551cc31f6b168ecdebf74b0222ed8c0def5e62 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:02:49 +0100 Subject: [PATCH 08/25] streams: use type bool instead of type int --- ext/standard/streamsfuncs.c | 2 +- main/php_streams.h | 2 +- main/streams/filter.c | 20 ++++++++++---------- main/streams/php_stream_filter_api.h | 8 ++++---- main/streams/streams.c | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 1070e3be5d428..2754545149070 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -1215,7 +1215,7 @@ PHP_FUNCTION(stream_context_create) /* }}} */ /* {{{ streams filter functions */ -static void apply_filter_to_stream(int append, INTERNAL_FUNCTION_PARAMETERS) +static void apply_filter_to_stream(bool append, INTERNAL_FUNCTION_PARAMETERS) { php_stream *stream; char *filtername; diff --git a/main/php_streams.h b/main/php_streams.h index ca6172e764bd4..0391a2b7a649f 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -531,7 +531,7 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de /* read all data from stream and put into a buffer. Caller must free buffer * when done. */ -PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int persistent STREAMS_DC); +PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, bool persistent STREAMS_DC); #define php_stream_copy_to_mem(src, maxlen, persistent) _php_stream_copy_to_mem((src), (maxlen), (persistent) STREAMS_CC) /* output all data from a stream */ diff --git a/main/streams/filter.c b/main/streams/filter.c index a68da91c4851e..304eac0a155f5 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -43,9 +43,9 @@ PHPAPI HashTable *_php_get_stream_filters_hash(void) PHPAPI int php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory) { int ret; - zend_string *str = zend_string_init_interned(filterpattern, strlen(filterpattern), 1); + zend_string *str = zend_string_init_interned(filterpattern, strlen(filterpattern), true); ret = zend_hash_add_ptr(&stream_filters_hash, str, (void*)factory) ? SUCCESS : FAILURE; - zend_string_release_ex(str, 1); + zend_string_release_ex(str, true); return ret; } @@ -70,7 +70,7 @@ PHPAPI int php_stream_filter_register_factory_volatile(zend_string *filterpatter PHPAPI php_stream_bucket *php_stream_bucket_new(const php_stream *stream, char *buf, size_t buflen, uint8_t own_buf, uint8_t buf_persistent) { - int is_persistent = php_stream_is_persistent(stream); + bool is_persistent = php_stream_is_persistent(stream); php_stream_bucket *bucket; bucket = (php_stream_bucket*)pemalloc(sizeof(php_stream_bucket), is_persistent); @@ -78,10 +78,10 @@ PHPAPI php_stream_bucket *php_stream_bucket_new(const php_stream *stream, char * if (is_persistent && !buf_persistent) { /* all data in a persistent bucket must also be persistent */ - bucket->buf = pemalloc(buflen, 1); + bucket->buf = pemalloc(buflen, true); memcpy(bucket->buf, buf, buflen); bucket->buflen = buflen; - bucket->own_buf = 1; + bucket->own_buf = true; } else { bucket->buf = buf; bucket->buflen = buflen; @@ -118,7 +118,7 @@ PHPAPI php_stream_bucket *php_stream_bucket_make_writeable(php_stream_bucket *bu memcpy(retval->buf, bucket->buf, retval->buflen); retval->refcount = 1; - retval->own_buf = 1; + retval->own_buf = true; php_stream_bucket_delref(bucket); @@ -134,14 +134,14 @@ PHPAPI int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **le (*left)->buflen = length; memcpy((*left)->buf, in->buf, length); (*left)->refcount = 1; - (*left)->own_buf = 1; + (*left)->own_buf = true; (*left)->is_persistent = in->is_persistent; (*right)->buflen = in->buflen - length; (*right)->buf = pemalloc((*right)->buflen, in->is_persistent); memcpy((*right)->buf, in->buf + length, (*right)->buflen); (*right)->refcount = 1; - (*right)->own_buf = 1; + (*right)->own_buf = true; (*right)->is_persistent = in->is_persistent; return SUCCESS; @@ -395,7 +395,7 @@ PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream } } -PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish) +PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, bool finish) { php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp; php_stream_bucket *bucket; @@ -480,7 +480,7 @@ PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish) return SUCCESS; } -PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor) +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, bool call_dtor) { if (filter->prev) { filter->prev->next = filter->next; diff --git a/main/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h index 3144697455a08..9d9ed24f861dc 100644 --- a/main/streams/php_stream_filter_api.h +++ b/main/streams/php_stream_filter_api.h @@ -44,8 +44,8 @@ struct _php_stream_bucket { char *buf; size_t buflen; /* if non-zero, buf should be pefreed when the bucket is destroyed */ - uint8_t own_buf; - uint8_t is_persistent; + bool own_buf; + bool is_persistent; /* destroy this struct when refcount falls to zero */ int refcount; @@ -124,8 +124,8 @@ PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_strea PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter); -PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, int finish); -PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, int call_dtor); +PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, bool finish); +PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, bool call_dtor); PHPAPI void php_stream_filter_free(php_stream_filter *filter); PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops, void *abstract, uint8_t persistent STREAMS_DC); END_EXTERN_C() diff --git a/main/streams/streams.c b/main/streams/streams.c index 7fe2ef215b80d..45bfaecf45eeb 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1529,7 +1529,7 @@ PHPAPI ssize_t _php_stream_passthru(php_stream * stream STREAMS_DC) } -PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, int persistent STREAMS_DC) +PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, bool persistent STREAMS_DC) { ssize_t ret = 0; char *ptr; @@ -2023,16 +2023,16 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const php_stream_wrapper *plain_files_wrapper = (php_stream_wrapper*)&php_plain_files_wrapper; if (protocol) { - int localhost = 0; + bool localhost = false; if (!strncasecmp(path, "file://localhost/", 17)) { - localhost = 1; + localhost = true; } #ifdef PHP_WIN32 - if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':') { + if (!localhost && path[n+3] != '\0' && path[n+3] != '/' && path[n+4] != ':') { #else - if (localhost == 0 && path[n+3] != '\0' && path[n+3] != '/') { + if (!localhost && path[n+3] != '\0' && path[n+3] != '/') { #endif if (options & REPORT_ERRORS) { php_error_docref(NULL, E_WARNING, "Remote host file access not supported, %s", path); @@ -2043,7 +2043,7 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const if (path_for_open) { /* skip past protocol and :/, but handle windows correctly */ *path_for_open = (char*)path + n + 1; - if (localhost == 1) { + if (localhost) { (*path_for_open) += 11; } while (*(++*path_for_open)=='/') { From 841afdc4866b9a1f30681bddc90aa97c0bda8e23 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:08:04 +0100 Subject: [PATCH 09/25] streams: use type zend_result instead of type int --- ext/pgsql/pgsql.c | 2 +- ext/sockets/sockets.c | 2 +- main/php_streams.h | 4 +-- main/streams/cast.c | 6 ++-- main/streams/filter.c | 14 ++++----- main/streams/php_stream_filter_api.h | 12 +++---- main/streams/streams.c | 2 +- main/streams/userspace.c | 47 ++++++++++------------------ 8 files changed, 38 insertions(+), 51 deletions(-) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index d6a9e26503601..c697776dade64 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -6388,7 +6388,7 @@ PHP_FUNCTION(pg_socket_poll) Z_PARAM_LONG(ts) ZEND_PARSE_PARAMETERS_END(); - if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void **)&socket, 0)) { + if (UNEXPECTED(php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void **)&socket, 0) == FAILURE)) { zend_argument_type_error(1, "invalid resource socket"); RETURN_THROWS(); } diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 3ed7d17bba217..3cae726838a64 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -2585,7 +2585,7 @@ PHP_FUNCTION(socket_import_stream) ZEND_PARSE_PARAMETERS_END(); php_stream_from_zval(stream, zstream); - if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void**)&socket, 1)) { + if (php_stream_cast(stream, PHP_STREAM_AS_SOCKETD, (void**)&socket, 1) == FAILURE) { /* error supposedly already shown */ RETURN_FALSE; } diff --git a/main/php_streams.h b/main/php_streams.h index 0391a2b7a649f..d3a6660c6ca7c 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -561,7 +561,7 @@ END_EXTERN_C() #define PHP_STREAM_CAST_INTERNAL 0x20000000 /* stream cast for internal use */ #define PHP_STREAM_CAST_MASK (PHP_STREAM_CAST_TRY_HARD | PHP_STREAM_CAST_RELEASE | PHP_STREAM_CAST_INTERNAL) BEGIN_EXTERN_C() -PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err); +PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err); END_EXTERN_C() /* use this to check if a stream can be cast into another form */ #define php_stream_can_cast(stream, as) _php_stream_cast((stream), (as), NULL, 0) @@ -626,7 +626,7 @@ END_EXTERN_C() /* this flag is only used by include/require functions */ #define STREAM_OPEN_FOR_ZEND_STREAM 0x00010000 -int php_init_stream_wrappers(int module_number); +zend_result php_init_stream_wrappers(int module_number); void php_shutdown_stream_wrappers(int module_number); void php_shutdown_stream_hashes(void); PHP_RSHUTDOWN_FUNCTION(streams); diff --git a/main/streams/cast.c b/main/streams/cast.c index 6e5c63fb3b292..5c78b560945eb 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -191,7 +191,7 @@ void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *resul /* }}} */ /* {{{ php_stream_cast */ -PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) +PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) { int flags = castas & PHP_STREAM_CAST_MASK; castas &= ~PHP_STREAM_CAST_MASK; @@ -273,12 +273,12 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show newstream = php_stream_fopen_tmpfile(); if (newstream) { - int retcopy = php_stream_copy_to_stream_ex(stream, newstream, PHP_STREAM_COPY_ALL, NULL); + zend_result retcopy = php_stream_copy_to_stream_ex(stream, newstream, PHP_STREAM_COPY_ALL, NULL); if (retcopy != SUCCESS) { php_stream_close(newstream); } else { - int retcast = php_stream_cast(newstream, castas | flags, (void **)ret, show_err); + zend_result retcast = php_stream_cast(newstream, castas | flags, (void **)ret, show_err); if (retcast == SUCCESS) { rewind(*(FILE**)ret); diff --git a/main/streams/filter.c b/main/streams/filter.c index 304eac0a155f5..2a48fae79f04c 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -40,22 +40,22 @@ PHPAPI HashTable *_php_get_stream_filters_hash(void) } /* API for registering GLOBAL filters */ -PHPAPI int php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory) +PHPAPI zend_result php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory) { - int ret; + zend_result ret; zend_string *str = zend_string_init_interned(filterpattern, strlen(filterpattern), true); ret = zend_hash_add_ptr(&stream_filters_hash, str, (void*)factory) ? SUCCESS : FAILURE; zend_string_release_ex(str, true); return ret; } -PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern) +PHPAPI zend_result php_stream_filter_unregister_factory(const char *filterpattern) { return zend_hash_str_del(&stream_filters_hash, filterpattern, strlen(filterpattern)); } /* API for registering VOLATILE wrappers */ -PHPAPI int php_stream_filter_register_factory_volatile(zend_string *filterpattern, const php_stream_filter_factory *factory) +PHPAPI zend_result php_stream_filter_register_factory_volatile(zend_string *filterpattern, const php_stream_filter_factory *factory) { if (!FG(stream_filters)) { ALLOC_HASHTABLE(FG(stream_filters)); @@ -282,7 +282,7 @@ PHPAPI void php_stream_filter_free(php_stream_filter *filter) pefree(filter, filter->is_persistent); } -PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter) +PHPAPI zend_result php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter) { filter->next = chain->head; filter->prev = NULL; @@ -303,7 +303,7 @@ PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_strea php_stream_filter_prepend_ex(chain, filter); } -PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter) +PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter) { php_stream *stream = chain->stream; @@ -395,7 +395,7 @@ PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream } } -PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, bool finish) +PHPAPI zend_result _php_stream_filter_flush(php_stream_filter *filter, bool finish) { php_stream_bucket_brigade brig_a = { NULL, NULL }, brig_b = { NULL, NULL }, *inp = &brig_a, *outp = &brig_b, *brig_temp; php_stream_bucket *bucket; diff --git a/main/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h index 9d9ed24f861dc..8e697c2ad0194 100644 --- a/main/streams/php_stream_filter_api.h +++ b/main/streams/php_stream_filter_api.h @@ -121,10 +121,10 @@ struct _php_stream_filter { /* stack filter onto a stream */ BEGIN_EXTERN_C() PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter); -PHPAPI int php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter); +PHPAPI zend_result php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter); -PHPAPI int php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter); -PHPAPI int _php_stream_filter_flush(php_stream_filter *filter, bool finish); +PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter); +PHPAPI zend_result _php_stream_filter_flush(php_stream_filter *filter, bool finish); PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, bool call_dtor); PHPAPI void php_stream_filter_free(php_stream_filter *filter); PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops, void *abstract, uint8_t persistent STREAMS_DC); @@ -142,8 +142,8 @@ typedef struct _php_stream_filter_factory { } php_stream_filter_factory; BEGIN_EXTERN_C() -PHPAPI int php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory); -PHPAPI int php_stream_filter_unregister_factory(const char *filterpattern); -PHPAPI int php_stream_filter_register_factory_volatile(zend_string *filterpattern, const php_stream_filter_factory *factory); +PHPAPI zend_result php_stream_filter_register_factory(const char *filterpattern, const php_stream_filter_factory *factory); +PHPAPI zend_result php_stream_filter_unregister_factory(const char *filterpattern); +PHPAPI zend_result php_stream_filter_register_factory_volatile(zend_string *filterpattern, const php_stream_filter_factory *factory); PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval *filterparams, uint8_t persistent); END_EXTERN_C() diff --git a/main/streams/streams.c b/main/streams/streams.c index 45bfaecf45eeb..fddca6f84a94e 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1869,7 +1869,7 @@ void php_shutdown_stream_hashes(void) } } -int php_init_stream_wrappers(int module_number) +zend_result php_init_stream_wrappers(int module_number) { le_stream = zend_register_list_destructors_ex(stream_resource_regular_dtor, NULL, "stream", module_number); le_pstream = zend_register_list_destructors_ex(NULL, stream_resource_persistent_dtor, "persistent stream", module_number); diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 0bb80acf6b7c2..baf6c96605726 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -297,7 +297,6 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * php_userstream_data_t *us; zval zretval, zfuncname; zval args[4]; - int call_result; php_stream *stream = NULL; bool old_in_user_include; @@ -340,6 +339,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * ZVAL_STRING(&zfuncname, USERSTREAM_OPEN); + zend_result call_result; zend_try { call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 4, args); } zend_catch { @@ -398,7 +398,6 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char php_userstream_data_t *us; zval zretval, zfuncname; zval args[2]; - int call_result; php_stream *stream = NULL; /* Try to catch bad usage without preventing flexibility */ @@ -426,7 +425,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char ZVAL_STRING(&zfuncname, USERSTREAM_DIR_OPEN); - call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 2, args); + zend_result call_result = call_method_if_exists(&us->object, &zfuncname, &zretval, 2, args); if (call_result == SUCCESS && Z_TYPE(zretval) != IS_UNDEF && zval_is_true(&zretval)) { /* the stream is now open! */ @@ -564,7 +563,6 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ { zval func_name; zval retval; - int call_result; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; zval args[1]; ssize_t didwrite; @@ -575,7 +573,7 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ ZVAL_STRINGL(&args[0], (char*)buf, count); - call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args); zval_ptr_dtor(&args[0]); zval_ptr_dtor(&func_name); @@ -614,7 +612,6 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count zval func_name; zval retval; zval args[1]; - int call_result; size_t didread = 0; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; @@ -624,7 +621,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count ZVAL_LONG(&args[0], count); - call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args); zval_ptr_dtor(&args[0]); zval_ptr_dtor(&func_name); @@ -714,14 +711,13 @@ static int php_userstreamop_flush(php_stream *stream) { zval func_name; zval retval; - int call_result; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; assert(us != NULL); ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1); - call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); if (call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zval_is_true(&retval)) call_result = 0; @@ -738,7 +734,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when { zval func_name; zval retval; - int call_result, ret; + int ret; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; zval args[2]; @@ -749,7 +745,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when ZVAL_LONG(&args[0], offset); ZVAL_LONG(&args[1], whence); - call_result = call_method_if_exists(&us->object, &func_name, &retval, 2, args); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 2, args); zval_ptr_dtor(&args[0]); zval_ptr_dtor(&args[1]); @@ -838,13 +834,12 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) { zval func_name; zval retval; - int call_result; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; int ret = -1; ZVAL_STRINGL(&func_name, USERSTREAM_STAT, sizeof(USERSTREAM_STAT)-1); - call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) { statbuf_from_array(&retval, ssb); @@ -866,7 +861,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) static int php_userstreamop_set_option(php_stream *stream, int option, int value, void *ptrparam) { zval func_name; zval retval; - int call_result; + zend_result call_result; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL; zval args[3]; @@ -1034,7 +1029,6 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval zfuncname, zretval; zval args[1]; - int call_result; zval object; int ret = 0; @@ -1049,7 +1043,7 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int ZVAL_STRING(&zfuncname, USERSTREAM_UNLINK); - call_result = call_method_if_exists(&object, &zfuncname, &zretval, 1, args); + zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 1, args); if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) { ret = (Z_TYPE(zretval) == IS_TRUE); @@ -1073,7 +1067,6 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval zfuncname, zretval; zval args[2]; - int call_result; zval object; int ret = 0; @@ -1089,7 +1082,7 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from ZVAL_STRING(&zfuncname, USERSTREAM_RENAME); - call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args); + zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args); if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) { ret = (Z_TYPE(zretval) == IS_TRUE); @@ -1114,7 +1107,6 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval zfuncname, zretval; zval args[3]; - int call_result; zval object; int ret = 0; @@ -1131,7 +1123,7 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int ZVAL_STRING(&zfuncname, USERSTREAM_MKDIR); - call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args); + zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args); if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) { ret = (Z_TYPE(zretval) == IS_TRUE); @@ -1157,7 +1149,6 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval zfuncname, zretval; zval args[2]; - int call_result; zval object; int ret = 0; @@ -1173,7 +1164,7 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, ZVAL_STRING(&zfuncname, USERSTREAM_RMDIR); - call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args); + zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args); if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) { ret = (Z_TYPE(zretval) == IS_TRUE); @@ -1198,7 +1189,6 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval zfuncname, zretval; zval args[3]; - int call_result; zval object; int ret = 0; @@ -1239,7 +1229,7 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i ZVAL_STRING(&zfuncname, USERSTREAM_METADATA); - call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args); + zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 3, args); if (call_result == SUCCESS && (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE)) { ret = Z_TYPE(zretval) == IS_TRUE; @@ -1266,7 +1256,6 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; zval zfuncname, zretval; zval args[2]; - int call_result; zval object; int ret = -1; @@ -1282,7 +1271,7 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i ZVAL_STRING(&zfuncname, USERSTREAM_STATURL); - call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args); + zend_result call_result = call_method_if_exists(&object, &zfuncname, &zretval, 2, args); if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) { /* We got the info we needed */ @@ -1311,7 +1300,6 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co { zval func_name; zval retval; - int call_result; size_t didread = 0; php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; php_stream_dirent *ent = (php_stream_dirent*)buf; @@ -1322,7 +1310,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co ZVAL_STRINGL(&func_name, USERSTREAM_DIR_READ, sizeof(USERSTREAM_DIR_READ)-1); - call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) { convert_to_string(&retval); @@ -1387,7 +1375,6 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) zval retval; zval args[1]; php_stream * intstream = NULL; - int call_result; int ret = FAILURE; /* If we are checking if the stream can cast, no return pointer is provided, so do not emit errors */ bool report_errors = retptr; @@ -1403,7 +1390,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) break; } - call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args); + zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 1, args); do { if (call_result == FAILURE) { From c9836b03ceff5bf5d8c8cd9e1aaa337ae622d5e5 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:09:49 +0100 Subject: [PATCH 10/25] streams: use type size_t instead of type unsigned int --- main/streams/streams.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/main/streams/streams.c b/main/streams/streams.c index fddca6f84a94e..0e1e26831960a 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1903,11 +1903,9 @@ void php_shutdown_stream_wrappers(int module_number) /* Validate protocol scheme names during registration * Must conform to /^[a-zA-Z0-9+.-]+$/ */ -static inline zend_result php_stream_wrapper_scheme_validate(const char *protocol, unsigned int protocol_len) +static inline zend_result php_stream_wrapper_scheme_validate(const char *protocol, size_t protocol_len) { - unsigned int i; - - for(i = 0; i < protocol_len; i++) { + for (size_t i = 0; i < protocol_len; i++) { if (!isalnum((int)protocol[i]) && protocol[i] != '+' && protocol[i] != '-' && From 67d6160462a3a7ad7dedffda44b0770f62d6a7de Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 14:27:31 +0100 Subject: [PATCH 11/25] streams: use int type instead of long The flags variable is passed to the filter virtual function that requires an int --- main/streams/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/streams/filter.c b/main/streams/filter.c index 2a48fae79f04c..0a698737b6de3 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -403,7 +403,7 @@ PHPAPI zend_result _php_stream_filter_flush(php_stream_filter *filter, bool fini php_stream_filter *current; php_stream *stream; size_t flushed_size = 0; - long flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC); + int flags = (finish ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC); if (!filter->chain || !filter->chain->stream) { /* Filter is not attached to a chain, or chain is somehow not part of a stream */ From 02af9acfca4bc5f223209b0180d865f004d89521 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:20:51 +0100 Subject: [PATCH 12/25] streams: use precomputed persistant variable --- main/streams/streams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/streams/streams.c b/main/streams/streams.c index 0e1e26831960a..b872cbaedd3d1 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2260,7 +2260,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod /* if the caller asked for a persistent stream but the wrapper did not * return one, force an error here */ - if (stream && (options & STREAM_OPEN_PERSISTENT) && !stream->is_persistent) { + if (stream && persistent && !stream->is_persistent) { php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, "wrapper does not support persistent streams"); php_stream_close(stream); From 4b6ebcc6e9cf12ba8fe92532372361560e0a3a3b Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:06:48 +0100 Subject: [PATCH 13/25] streams: remove confusing step variable --- main/streams/streams.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/main/streams/streams.c b/main/streams/streams.c index b872cbaedd3d1..37eb253a1e5c9 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1534,8 +1534,6 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, bool ssize_t ret = 0; char *ptr; size_t len = 0, buflen; - int step = CHUNK_SIZE; - int min_room = CHUNK_SIZE / 4; php_stream_statbuf ssbuf; zend_string *result; @@ -1578,20 +1576,21 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, bool * we can. Note that the stream may be filtered, in which case the stat * result may be inaccurate, as the filter may inflate or deflate the * number of bytes that we can read. In order to avoid an upsize followed - * by a downsize of the buffer, overestimate by the step size (which is + * by a downsize of the buffer, overestimate by the CHUNK_SIZE size (which is * 8K). */ if (php_stream_stat(src, &ssbuf) == 0 && ssbuf.sb.st_size > 0) { - buflen = MAX(ssbuf.sb.st_size - src->position, 0) + step; + buflen = MAX(ssbuf.sb.st_size - src->position, 0) + CHUNK_SIZE; if (maxlen > 0 && buflen > maxlen) { buflen = maxlen; } } else { - buflen = step; + buflen = CHUNK_SIZE; } result = zend_string_alloc(buflen, persistent); ptr = ZSTR_VAL(result); + const int min_room = CHUNK_SIZE / 4; // TODO: Propagate error? while ((ret = php_stream_read(src, ptr, buflen - len)) > 0) { len += ret; @@ -1599,10 +1598,10 @@ PHPAPI zend_string *_php_stream_copy_to_mem(php_stream *src, size_t maxlen, bool if (maxlen == len) { break; } - if (maxlen > 0 && buflen + step > maxlen) { + if (maxlen > 0 && buflen + CHUNK_SIZE > maxlen) { buflen = maxlen; } else { - buflen += step; + buflen += CHUNK_SIZE; } result = zend_string_extend(result, buflen, persistent); ptr = ZSTR_VAL(result) + len; From fb2585f4e23485bfab840617334f2f3391bc8288 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:21:52 +0100 Subject: [PATCH 14/25] streams: use -1 directly instead of FAILURE macro The result of the functions returning those globals is never checked anyway --- main/streams/streams.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/streams/streams.c b/main/streams/streams.c index 37eb253a1e5c9..42e08e910771d 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -35,9 +35,9 @@ /* {{{ resource and registration code */ /* Global wrapper hash, copied to FG(stream_wrappers) on registration of volatile wrapper */ static HashTable url_stream_wrappers_hash; -static int le_stream = FAILURE; /* true global */ -static int le_pstream = FAILURE; /* true global */ -static int le_stream_filter = FAILURE; /* true global */ +static int le_stream = -1; /* true global */ +static int le_pstream = -1; /* true global */ +static int le_stream_filter = -1; /* true global */ PHPAPI int php_file_le_stream(void) { From 0d0f774a6ad4dc168dc1a235819809cbcd64b473 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:27:37 +0100 Subject: [PATCH 15/25] streams: use call in if statement directly --- ext/standard/streamsfuncs.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 2754545149070..96903f48abb72 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -482,7 +482,6 @@ PHP_FUNCTION(stream_copy_to_stream) zend_long maxlen, pos = 0; bool maxlen_is_null = 1; size_t len; - int ret; ZEND_PARSE_PARAMETERS_START(2, 4) PHP_Z_PARAM_STREAM(src) @@ -501,9 +500,7 @@ PHP_FUNCTION(stream_copy_to_stream) RETURN_FALSE; } - ret = php_stream_copy_to_stream_ex(src, dest, maxlen, &len); - - if (ret != SUCCESS) { + if (php_stream_copy_to_stream_ex(src, dest, maxlen, &len) != SUCCESS) { RETURN_FALSE; } RETURN_LONG(len); From c50a715a5c609d584d85023a1bda711d511707c0 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 12:29:38 +0100 Subject: [PATCH 16/25] streams: remove useless casts --- ext/standard/streamsfuncs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 96903f48abb72..07f4c1b31e2de 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -529,9 +529,9 @@ PHP_FUNCTION(stream_get_meta_data) add_assoc_zval(return_value, "wrapper_data", &stream->wrapperdata); } if (stream->wrapper) { - add_assoc_string(return_value, "wrapper_type", (char *)stream->wrapper->wops->label); + add_assoc_string(return_value, "wrapper_type", stream->wrapper->wops->label); } - add_assoc_string(return_value, "stream_type", (char *)stream->ops->label); + add_assoc_string(return_value, "stream_type", stream->ops->label); add_assoc_string(return_value, "mode", stream->mode); @@ -543,7 +543,7 @@ PHP_FUNCTION(stream_get_meta_data) array_init(newval); for (filter = stream->filterhead; filter != NULL; filter = filter->next) { - add_next_index_string(newval, (char *)filter->fops->label); + add_next_index_string(newval, filter->fops->label); } add_assoc_zval(return_value, "filters", newval); From b46c5e618679fba5d131d55f3a56e2b6413c0507 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 13:13:14 +0100 Subject: [PATCH 17/25] streams: refactor implementation of stream_select() --- ext/standard/streamsfuncs.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 07f4c1b31e2de..912cf6503bfd9 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -595,17 +595,13 @@ PHP_FUNCTION(stream_get_wrappers) /* }}} */ /* {{{ stream_select related functions */ -static int stream_array_to_fd_set(zval *stream_array, fd_set *fds, php_socket_t *max_fd) +static int stream_array_to_fd_set(const HashTable *stream_array, fd_set *fds, php_socket_t *max_fd) { zval *elem; php_stream *stream; int cnt = 0; - if (Z_TYPE_P(stream_array) != IS_ARRAY) { - return 0; - } - - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(stream_array), elem) { + ZEND_HASH_FOREACH_VAL(stream_array, elem) { /* Temporary int fd is needed for the STREAM data type on windows, passing this_fd directly to php_stream_cast() would eventually bring a wrong result on x64. php_stream_cast() casts to int internally, and this will leave the higher bits of a SOCKET variable uninitialized on systems with little endian. */ @@ -634,7 +630,7 @@ static int stream_array_to_fd_set(zval *stream_array, fd_set *fds, php_socket_t return cnt ? 1 : 0; } -static int stream_array_from_fd_set(zval *stream_array, fd_set *fds) +static int stream_array_from_fd_set(zval *stream_array, const fd_set *fds) { zval *elem, *dest_elem; HashTable *ht; @@ -643,9 +639,7 @@ static int stream_array_from_fd_set(zval *stream_array, fd_set *fds) zend_string *key; zend_ulong num_ind; - if (Z_TYPE_P(stream_array) != IS_ARRAY) { - return 0; - } + ZEND_ASSERT(Z_TYPE_P(stream_array) == IS_ARRAY); ht = zend_new_array(zend_hash_num_elements(Z_ARRVAL_P(stream_array))); ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) { @@ -671,7 +665,6 @@ static int stream_array_from_fd_set(zval *stream_array, fd_set *fds) zval_add_ref(dest_elem); ret++; - continue; } } } ZEND_HASH_FOREACH_END(); @@ -692,9 +685,7 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) zend_ulong num_ind; zend_string *key; - if (Z_TYPE_P(stream_array) != IS_ARRAY) { - return 0; - } + ZEND_ASSERT(Z_TYPE_P(stream_array) == IS_ARRAY); ht = zend_new_array(zend_hash_num_elements(Z_ARRVAL_P(stream_array))); ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) { @@ -717,7 +708,6 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) } zval_add_ref(dest_elem); ret++; - continue; } } ZEND_HASH_FOREACH_END(); @@ -760,21 +750,21 @@ PHP_FUNCTION(stream_select) FD_ZERO(&efds); if (r_array != NULL) { - set_count = stream_array_to_fd_set(r_array, &rfds, &max_fd); + set_count = stream_array_to_fd_set(Z_ARR_P(r_array), &rfds, &max_fd); if (set_count > max_set_count) max_set_count = set_count; sets += set_count; } if (w_array != NULL) { - set_count = stream_array_to_fd_set(w_array, &wfds, &max_fd); + set_count = stream_array_to_fd_set(Z_ARR_P(w_array), &wfds, &max_fd); if (set_count > max_set_count) max_set_count = set_count; sets += set_count; } if (e_array != NULL) { - set_count = stream_array_to_fd_set(e_array, &efds, &max_fd); + set_count = stream_array_to_fd_set(Z_ARR_P(e_array), &efds, &max_fd); if (set_count > max_set_count) max_set_count = set_count; sets += set_count; From cd13ba73e2c99eeb4d412182506b7ea04931c899 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 13:23:37 +0100 Subject: [PATCH 18/25] streams: use RETURN_BOOL() when possible --- ext/standard/streamsfuncs.c | 51 ++++++++----------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 912cf6503bfd9..cc7fc0406555e 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -1362,11 +1362,7 @@ PHP_FUNCTION(stream_set_blocking) Z_PARAM_BOOL(block) ZEND_PARSE_PARAMETERS_END(); - if (php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL) == -1) { - RETURN_FALSE; - } - - RETURN_TRUE; + RETURN_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); } /* }}} */ @@ -1407,11 +1403,7 @@ PHP_FUNCTION(stream_set_timeout) } #endif - if (PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)) { - RETURN_TRUE; - } - - RETURN_FALSE; + RETURN_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); } #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */ /* }}} */ @@ -1587,11 +1579,7 @@ PHP_FUNCTION(stream_is_local) wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0); } - if (!wrapper) { - RETURN_FALSE; - } - - RETURN_BOOL(wrapper->is_url==0); + RETURN_BOOL(wrapper && wrapper->is_url == 0); } /* }}} */ @@ -1604,11 +1592,7 @@ PHP_FUNCTION(stream_supports_lock) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - if (!php_stream_supports_lock(stream)) { - RETURN_FALSE; - } - - RETURN_TRUE; + RETURN_BOOL(php_stream_supports_lock(stream)); } /* {{{ Check if a stream is a TTY. */ @@ -1635,15 +1619,13 @@ PHP_FUNCTION(stream_isatty) #ifdef PHP_WIN32 /* Check if the Windows standard handle is redirected to file */ - RETVAL_BOOL(php_win32_console_fileno_is_console(fileno)); + RETURN_BOOL(php_win32_console_fileno_is_console(fileno)); #elif defined(HAVE_UNISTD_H) /* Check if the file descriptor identifier is a terminal */ - RETVAL_BOOL(isatty(fileno)); + RETURN_BOOL(isatty(fileno)); #else - { - zend_stat_t stat = {0}; - RETVAL_BOOL(zend_fstat(fileno, &stat) == 0 && (stat.st_mode & /*S_IFMT*/0170000) == /*S_IFCHR*/0020000); - } + zend_stat_t stat = {0}; + RETURN_BOOL(zend_fstat(fileno, &stat) == 0 && (stat.st_mode & /*S_IFMT*/0170000) == /*S_IFCHR*/0020000); #endif } @@ -1689,21 +1671,10 @@ PHP_FUNCTION(sapi_windows_vt100_support) if (enable_is_null) { /* Check if the Windows standard handle has VT100 control codes enabled */ - if (php_win32_console_fileno_has_vt100(fileno)) { - RETURN_TRUE; - } - else { - RETURN_FALSE; - } - } - else { + RETURN_BOOL(php_win32_console_fileno_has_vt100(fileno)); + } else { /* Enable/disable VT100 control codes support for the specified Windows standard handle */ - if (php_win32_console_fileno_set_vt100(fileno, enable ? TRUE : FALSE)) { - RETURN_TRUE; - } - else { - RETURN_FALSE; - } + RETURN_BOOL(php_win32_console_fileno_set_vt100(fileno, enable ? TRUE : FALSE)); } } #endif From fa85b38560a36f4f9781a64753b809387cdaa6c6 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 14:29:54 +0100 Subject: [PATCH 19/25] streams: voidify php_stream_filter_prepend_ex() This only ever returned SUCCESS --- ext/standard/streamsfuncs.c | 25 ++++++++++++------------- main/streams/filter.c | 4 +--- main/streams/php_stream_filter_api.h | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index cc7fc0406555e..506ce0dafed8b 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -1210,7 +1210,6 @@ static void apply_filter_to_stream(bool append, INTERNAL_FUNCTION_PARAMETERS) zend_long read_write = 0; zval *filterparams = NULL; php_stream_filter *filter = NULL; - int ret; ZEND_PARSE_PARAMETERS_START(2, 4) PHP_Z_PARAM_STREAM(stream) @@ -1241,13 +1240,13 @@ static void apply_filter_to_stream(bool append, INTERNAL_FUNCTION_PARAMETERS) } if (append) { - ret = php_stream_filter_append_ex(&stream->readfilters, filter); + zend_result ret = php_stream_filter_append_ex(&stream->readfilters, filter); + if (ret != SUCCESS) { + php_stream_filter_remove(filter, 1); + RETURN_FALSE; + } } else { - ret = php_stream_filter_prepend_ex(&stream->readfilters, filter); - } - if (ret != SUCCESS) { - php_stream_filter_remove(filter, 1); - RETURN_FALSE; + php_stream_filter_prepend_ex(&stream->readfilters, filter); } } @@ -1258,13 +1257,13 @@ static void apply_filter_to_stream(bool append, INTERNAL_FUNCTION_PARAMETERS) } if (append) { - ret = php_stream_filter_append_ex(&stream->writefilters, filter); + zend_result ret = php_stream_filter_append_ex(&stream->writefilters, filter); + if (ret != SUCCESS) { + php_stream_filter_remove(filter, 1); + RETURN_FALSE; + } } else { - ret = php_stream_filter_prepend_ex(&stream->writefilters, filter); - } - if (ret != SUCCESS) { - php_stream_filter_remove(filter, 1); - RETURN_FALSE; + php_stream_filter_prepend_ex(&stream->writefilters, filter); } } diff --git a/main/streams/filter.c b/main/streams/filter.c index 0a698737b6de3..0f0e650046585 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -282,7 +282,7 @@ PHPAPI void php_stream_filter_free(php_stream_filter *filter) pefree(filter, filter->is_persistent); } -PHPAPI zend_result php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter) +PHPAPI void php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter) { filter->next = chain->head; filter->prev = NULL; @@ -294,8 +294,6 @@ PHPAPI zend_result php_stream_filter_prepend_ex(php_stream_filter_chain *chain, } chain->head = filter; filter->chain = chain; - - return SUCCESS; } PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter) diff --git a/main/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h index 8e697c2ad0194..e224b85b2d9a2 100644 --- a/main/streams/php_stream_filter_api.h +++ b/main/streams/php_stream_filter_api.h @@ -121,7 +121,7 @@ struct _php_stream_filter { /* stack filter onto a stream */ BEGIN_EXTERN_C() PHPAPI void _php_stream_filter_prepend(php_stream_filter_chain *chain, php_stream_filter *filter); -PHPAPI zend_result php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter); +PHPAPI void php_stream_filter_prepend_ex(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI void _php_stream_filter_append(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, php_stream_filter *filter); PHPAPI zend_result _php_stream_filter_flush(php_stream_filter *filter, bool finish); From 93ec0ac0f3f309e3b72f07cda6701beee5fe4338 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 14:42:53 +0100 Subject: [PATCH 20/25] streams: drop unused includes --- main/streams/filter.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main/streams/filter.c b/main/streams/filter.c index 0f0e650046585..beaba503cbe16 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -15,10 +15,8 @@ */ #include "php.h" -#include "php_globals.h" #include "php_network.h" -#include "php_open_temporary_file.h" -#include "ext/standard/file.h" +#include "ext/standard/file.h" /* For FG(stream_filters) */ #include #include From 051414bec6bdc43b8345e317b680d2d063b28067 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 14:52:32 +0100 Subject: [PATCH 21/25] streams: use an enum for return type of _php_stream_make_seekable() --- main/php_streams.h | 13 ++++++++----- main/streams/cast.c | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/main/php_streams.h b/main/php_streams.h index d3a6660c6ca7c..1c52539cfcaee 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -646,15 +646,18 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf); /* pushes an error message onto the stack for a wrapper instance */ PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); -#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */ -#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */ -#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */ -#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */ +typedef enum { + PHP_STREAM_UNCHANGED = 0, /* orig stream was seekable anyway */ + PHP_STREAM_RELEASED = 1, /* newstream should be used; origstream is no longer valid */ + PHP_STREAM_FAILED = 2, /* an error occurred while attempting conversion */ + PHP_STREAM_CRITICAL = 3, /* an error occurred; origstream is in an unknown state; you should close origstream */ +} php_stream_make_seekable_status; + #define PHP_STREAM_NO_PREFERENCE 0 #define PHP_STREAM_PREFER_STDIO 1 #define PHP_STREAM_FORCE_CONVERSION 2 /* DO NOT call this on streams that are referenced by resources! */ -PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC); +PHPAPI php_stream_make_seekable_status _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC); #define php_stream_make_seekable(origstream, newstream, flags) _php_stream_make_seekable((origstream), (newstream), (flags) STREAMS_CC) /* Give other modules access to the url_stream_wrappers_hash and stream_filters_hash */ diff --git a/main/streams/cast.c b/main/streams/cast.c index 5c78b560945eb..4b7183024571b 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -370,7 +370,7 @@ PHPAPI FILE * _php_stream_open_wrapper_as_file(char *path, char *mode, int optio /* }}} */ /* {{{ php_stream_make_seekable */ -PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC) +PHPAPI php_stream_make_seekable_status _php_stream_make_seekable(php_stream *origstream, php_stream **newstream, int flags STREAMS_DC) { if (newstream == NULL) { return PHP_STREAM_FAILED; From a02b2b8eb6e26514bf69e5284950164f8e43d0e3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 15:12:01 +0100 Subject: [PATCH 22/25] streams: use %zu printf specifier for size_t Rather than casting to zend_long --- main/streams/xp_socket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index f843fe83e27ff..fd063260296b9 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -107,8 +107,8 @@ static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t coun if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { estr = php_socket_strerror(err, NULL, 0); php_error_docref(NULL, E_NOTICE, - "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s", - (zend_long)count, err, estr); + "Send of %zu bytes failed with errno=%d %s", + count, err, estr); efree(estr); } } From 9e334af6e4e790cc11b4a0dc68f740d9a5b9add0 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 15:14:23 +0100 Subject: [PATCH 23/25] streams: use type php_socket_t instead of type int --- main/streams/xp_socket.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index fd063260296b9..ae8cf24c39221 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -857,7 +857,6 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_ static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t *sock, php_stream_xport_param *xparam STREAMS_DC) { - int clisock; bool nodelay = 0; zval *tmpzval = NULL; @@ -869,7 +868,7 @@ static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t nodelay = 1; } - clisock = php_network_accept_incoming(sock->socket, + php_socket_t clisock = php_network_accept_incoming(sock->socket, xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, xparam->want_addr ? &xparam->outputs.addr : NULL, xparam->want_addr ? &xparam->outputs.addrlen : NULL, From 4d5bdef21a96cd22d84956abca57e13a6f4467b4 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 12 Jul 2025 15:26:49 +0100 Subject: [PATCH 24/25] streams: refactor statbuf_from_array() --- main/streams/userspace.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/streams/userspace.c b/main/streams/userspace.c index baf6c96605726..a25900eda3054 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -794,12 +794,12 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when /* parse the return value from one of the stat functions and store the * relevant fields into the statbuf provided */ -static void statbuf_from_array(zval *array, php_stream_statbuf *ssb) +static void statbuf_from_array(const HashTable *array, php_stream_statbuf *ssb) { zval *elem; #define STAT_PROP_ENTRY_EX(name, name2) \ - if (NULL != (elem = zend_hash_str_find(Z_ARRVAL_P(array), #name, sizeof(#name)-1))) { \ + if (NULL != (elem = zend_hash_str_find(array, #name, sizeof(#name)-1))) { \ ssb->sb.st_##name2 = zval_get_long(elem); \ } @@ -842,7 +842,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) zend_result call_result = call_method_if_exists(&us->object, &func_name, &retval, 0, NULL); if (call_result == SUCCESS && Z_TYPE(retval) == IS_ARRAY) { - statbuf_from_array(&retval, ssb); + statbuf_from_array(Z_ARR(retval), ssb); ret = 0; } else { if (call_result == FAILURE) { @@ -1275,7 +1275,7 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i if (call_result == SUCCESS && Z_TYPE(zretval) == IS_ARRAY) { /* We got the info we needed */ - statbuf_from_array(&zretval, ssb); + statbuf_from_array(Z_ARR(zretval), ssb); ret = 0; } else { if (call_result == FAILURE) { From 9d29283392191f660ad767b246d0f4e064b5d174 Mon Sep 17 00:00:00 2001 From: Arndt Kaiser Date: Fri, 1 Aug 2025 19:51:09 +0200 Subject: [PATCH 25/25] Fix filtering of INI directives to respect leading whitespaces Directives are now correctly filtered out if the line in the php.ini file begins with whitespace characters. Closes GH-19348 --- build/Makefile.global | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Makefile.global b/build/Makefile.global index ec19efcbc5894..49d4f8bcb3cfe 100644 --- a/build/Makefile.global +++ b/build/Makefile.global @@ -90,7 +90,7 @@ PHP_TEST_SHARED_EXTENSIONS = ` \ . $$i; $(top_srcdir)/build/shtool echo -n -- " -d zend_extension=$(top_builddir)/modules/$$dlname"; \ done; \ fi` -PHP_DEPRECATED_DIRECTIVES_REGEX = '^(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?)[\t\ ]*=' +PHP_DEPRECATED_DIRECTIVES_REGEX = '^[\t\ ]*(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?)[\t\ ]*=' test: all @if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \