Commit 853b89c092f for php.net

commit 853b89c092f0adb9c16f3fd597c4c4bff82c025f
Author: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com>
Date:   Wed Jul 22 09:31:44 2026 +0200

    Clear ZREG_TYPE_ONLY flag for in-register vars in zend_jit_snapshot_handler() (#22764)

    Fixes GH-22763.

    The reproducer triggers an assertion failure in `zend_jit_use_reg()` because a var has `jit->ra[var].ref == IR_NULL` but `jit->ra[var].flags` doesn't contain `ZREG_LOAD`.

    This happens because of the following events:
     * An in-register variable is spilled to the VM stack frame by IR (IR_REG_SPILL_SPECIAL)
     * zend_jit_snapshot_handler() set the stack descriptor to .flags = ZREG_TYPE_ONLY, .reg = ZREG_NONE
     * During a subsequent call to zend_jit_snapshot_handler(), the reg is not IR_REG_SPILL_SPECIAL anymore, so .reg is set but .flags remains ZREG_TYPE_ONLY which is inconsistent
     * A side trace inherits from this stack descriptor
     * zend_jit_trace_deoptimization() doesn't set `jit->ra[var].ref` for this var because `.flags == ZREG_TYPE_ONLY`, which causes the assertion failure later

    Fix by removing irrelevant flags when setting reg in zend_jit_snapshot_handler().

diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index f8503a13173..ae9088878b6 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -859,12 +859,14 @@ void *zend_jit_snapshot_handler(ir_ctx *ctx, ir_ref snapshot_ref, ir_insn *snaps
 							t->stack_map[t->exit_info[exit_point].stack_offset + var].flags = ZREG_TYPE_ONLY;
 						} else {
 							if ((exit_flags & ZEND_JIT_EXIT_FIXED)
-							 && t->stack_map[t->exit_info[exit_point].stack_offset + var].reg != IR_REG_NUM(reg)) {
+							 && (t->stack_map[t->exit_info[exit_point].stack_offset + var].reg != IR_REG_NUM(reg)
+							 || (t->stack_map[t->exit_info[exit_point].stack_offset + var].flags & ~(ZREG_LOAD|ZREG_STORE|ZREG_LAST_USE)))) {
 								exit_point = zend_jit_duplicate_exit_point(ctx, t, exit_point, snapshot_ref);
 								addr = (void*)zend_jit_trace_get_exit_addr(exit_point);
 								exit_flags &= ~ZEND_JIT_EXIT_FIXED;
 							}
 							t->stack_map[t->exit_info[exit_point].stack_offset + var].reg = IR_REG_NUM(reg);
+							t->stack_map[t->exit_info[exit_point].stack_offset + var].flags &= (ZREG_LOAD|ZREG_STORE|ZREG_LAST_USE);
 						}
 					} else {
 						if ((exit_flags & ZEND_JIT_EXIT_FIXED)
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 5cc3dbd3aa0..c8ef22c8806 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -3558,6 +3558,8 @@ static int zend_jit_trace_deoptimization(
 				}
 			}
 		} else if (STACK_FLAGS(parent_stack, i) == ZREG_TYPE_ONLY) {
+			ZEND_ASSERT(reg == ZREG_NONE);
+
 			uint8_t type = STACK_TYPE(parent_stack, i);

 			if (!zend_jit_store_type(jit, i, type)) {
diff --git a/ext/opcache/tests/jit/gh22763.phpt b/ext/opcache/tests/jit/gh22763.phpt
new file mode 100644
index 00000000000..67d7caa308e
--- /dev/null
+++ b/ext/opcache/tests/jit/gh22763.phpt
@@ -0,0 +1,58 @@
+--TEST--
+GH-22763: JIT fails to clear ZREG_TYPE_ONLY after setting reg
+--FILE--
+<?php
+
+function findMiddleSnake(array $a, int $aLo, int $aHi, array $b, int $bLo, int $bHi): array
+{
+    $n          = $aHi - $aLo;
+    $m          = $bHi - $bLo;
+    $delta      = $n - $m;
+    $deltaIsOdd = ($delta & 1) !== 0;
+    $offset     = 4;
+    $size       = 2 * 3 + 2;
+    $vf         = array_fill(0, $size, 0);
+    $vb         = array_fill(0, $size, 0);
+
+    $d = 2;
+    $x = 0;
+
+    for ($k = -$d; $k <= $d; $k += 2) {
+        if ($k === -$d || ($k !== $d && $vb[$offset + $k - 1] < $vb[$offset + $k + 1])) {
+            $x = $vb[$offset + $k + 1];
+            var_dump($x);
+        } else {
+            $x = $vb[$offset + $k - 1] + 1;
+        }
+
+        $y  = $x - $k;
+        $xs = $x;
+        $ys = $y;
+
+        while ($x < $n && $y < $m && $a[$aLo + $n - 1 - $x] === $b[$bLo + $m - 1 - $y]) {
+            $x++;
+            $y++;
+        }
+
+        $vb[$offset + $k] = $x;
+
+        if (!$deltaIsOdd) {
+            $kf = $delta - $k;
+
+            if ($kf >= -$d && $kf <= $d && $vf[$offset + $kf] + $vb[$offset + $k] >= $n) {
+                return [$n - $x, $m - $y, $n - $xs, $m - $ys];
+            }
+        }
+    }
+
+    return [];
+}
+
+$from = [3,2,1];
+$to = [1,99,3];
+findMiddleSnake($from, 0, count($from), $to, 0, count($to));
+?>
+--EXPECTF--
+int(0)
+
+Warning: Undefined array key 3 in %s on line %d