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