[php-src] master: Merge branch 'PHP-8.5'

From: Date: Fri, 07 Aug 2026 14:19:26 +0000
Subject: [php-src] master: Merge branch 'PHP-8.5'
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-140533@lists.php.net to get a copy of this message
Author: Arnaud Le Blanc (arnaud-lb) Date: 2026-08-07T16:17:02+02:00 Commit: https://github.com/php/php-src/commit/4b4fb492471943cdd851a66aadd68593baf3e4d7 Raw diff: https://github.com/php/php-src/commit/4b4fb492471943cdd851a66aadd68593baf3e4d7.diff Merge branch 'PHP-8.5' * PHP-8.5: JIT: Preserve parent regs in zend_jit_deoptimizer_start() (#22916) Changed paths: A ext/opcache/tests/jit/gh22915.phpt M ext/opcache/jit/zend_jit_ir.c M ext/opcache/jit/zend_jit_trace.c Diff: diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 2c904edd4cfc..2bbd7b0e3f4d 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -360,6 +360,11 @@ static int zend_jit_assign_to_variable(zend_jit_ctx *jit, static ir_ref jit_CONST_FUNC(zend_jit_ctx *jit, uintptr_t addr, uint16_t flags); +static void zend_jit_preserve_parent_regs(zend_jit_ctx *jit, + zend_ssa *ssa, + zend_jit_trace_info *parent, + uint32_t exit_num); + typedef struct _zend_jit_stub { const char *name; int (*stub)(zend_jit_ctx *jit); @@ -17351,6 +17356,7 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr static int zend_jit_deoptimizer_start(zend_jit_ctx *jit, zend_string *name, uint32_t trace_num, + zend_jit_trace_info *parent, uint32_t exit_num) { zend_jit_init_ctx(jit, (ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) ? 0 : IR_START_BR_TARGET); @@ -17363,6 +17369,8 @@ static int zend_jit_deoptimizer_start(zend_jit_ctx *jit, jit->ctx.flags |= IR_SKIP_PROLOGUE; + zend_jit_preserve_parent_regs(jit, NULL, parent, exit_num); + return 1; } @@ -17399,6 +17407,21 @@ static int zend_jit_trace_start(zend_jit_ctx *jit, jit->ctx.flags |= IR_SKIP_PROLOGUE; } + zend_jit_preserve_parent_regs(jit, ssa, parent, exit_num); + + ir_STORE(jit_EG(jit_trace_num), ir_CONST_U32(trace_num)); + + return 1; +} + +static void zend_jit_preserve_parent_regs(zend_jit_ctx *jit, + zend_ssa *ssa, + zend_jit_trace_info *parent, + uint32_t exit_num) +{ + /* Emit early RLOADs of registers used for deoptimization to prevent + * clobbering. zend_jit_deopt_rload() will reference these. */ + if (parent) { int i; int parent_vars_count = parent->exit_info[exit_num].stack_size; @@ -17406,7 +17429,6 @@ static int zend_jit_trace_start(zend_jit_ctx *jit, parent->stack_map + parent->exit_info[exit_num].stack_offset; - /* prevent clobbering of registers used for deoptimization */ for (i = 0; i < parent_vars_count; i++) { if (STACK_FLAGS(parent_stack, i) != ZREG_CONST && STACK_REG(parent_stack, i) != ZREG_NONE) { @@ -17450,10 +17472,6 @@ static int zend_jit_trace_start(zend_jit_ctx *jit, ir_RLOAD_A(parent->exit_info[exit_num].poly_this.reg); } } - - ir_STORE(jit_EG(jit_trace_num), ir_CONST_U32(trace_num)); - - return 1; } static int zend_jit_trace_begin_loop(zend_jit_ctx *jit) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 505427890129..a47ef18db337 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -7455,7 +7455,7 @@ static zend_vm_opcode_handler_t zend_jit_trace_exit_to_vm(uint32_t trace_num, ui name = zend_jit_trace_escape_name(trace_num, exit_num); - if (!zend_jit_deoptimizer_start(&ctx, name, trace_num, exit_num)) { + if (!zend_jit_deoptimizer_start(&ctx, name, trace_num, &zend_jit_traces[trace_num], exit_num)) { zend_string_release(name); return NULL; } diff --git a/ext/opcache/tests/jit/gh22915.phpt b/ext/opcache/tests/jit/gh22915.phpt new file mode 100644 index 000000000000..cea291d311b1 --- /dev/null +++ b/ext/opcache/tests/jit/gh22915.phpt @@ -0,0 +1,75 @@ +--TEST-- +GH-22915: compiled exit clobbers registers before saving +--EXTENSIONS-- +opcache +--INI-- +opcache.jit_max_side_traces=0 +opcache.jit_blacklist_side_trace=0 +--ENV-- +F=iter +--FILE-- +<?php + +final class It implements Iterator { + public readonly array $values; + public int $position = 0; + public function __construct(array $values) { + $this->values = $values; + } + + public function rewind(): void {} + + public function valid(): bool { + return $this->position === 0; + } + + public function current(): mixed { + if (!isset($this->values[$this->position])) { + throw new Exception(); + } + + return $this->values[$this->position]; + } + + public function key(): mixed { + return $this->position; + } + + public function next(): void { + $this->position++; + } +} + +function iter(It $it) { + foreach ($it as $value) { + var_dump($value); + if (!$value instanceof stdClass) { + continue; + } + } +} + +echo "# First run\n"; +for ($i = 0; $i < 5; $i++) { + getenv('F')(new It([getenv('F')])); // non-immutable, packed array +} + +echo "# Second run\n"; +for ($i = 0; $i < 5; $i++) { + getenv('F')(new It([getenv('F'), 'map' => true])); // non-immutable, map, triggers exit +} + +?> +--EXPECT-- +# First run +string(4) "iter" +string(4) "iter" +string(4) "iter" +string(4) "iter" +string(4) "iter" +# Second run +string(4) "iter" +string(4) "iter" +string(4) "iter" +string(4) "iter" +string(4) "iter"

« previous php.cvs (#140533) next »