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

[email protected] (Arnaud Le Blanc)
Newsgroups php.cvs
Message-ID <[email protected]>
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"
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.