Commit 40c3e247f9b for php.net

commit 40c3e247f9b7e282cd4e474777d1fc506e4b5373
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date:   Thu Jul 30 09:41:42 2026 +0200

    CS

diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index f4fce5e0dd9..a958a2453c4 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -2371,28 +2371,28 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
 							break;
 						}
 						if (opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) {
-							/* FETCH_OBJ_FUNC_ARG's by-value fetch dispatches into the */
-							/* FETCH_OBJ_R handler, which may take the SIMPLE_GET hook fast */
-							/* path and push a getter frame; by-ref dispatches into */
-							/* FETCH_OBJ_W. The function JIT may keep values solely in */
-							/* registers, so we must NOT exit to the VM (stale stack slots). */
-							/* Inline the by-value path through zend_jit_fetch_obj, which runs */
-							/* the hook getter inside a helper and keeps all registers live. */
-							/* The by-ref path has no SIMPLE_GET fast path, so the generic */
-							/* handler (a full C call, safe under register allocation) is used. */
-							/* This mirrors the tracing JIT fix for GH-21006 (GH-21369); the */
-							/* runtime by-ref check is required because the passing mode is */
-							/* only known once the callee is resolved via namespace fallback. */
-							/* See GH-22857. */
+							/* FETCH_OBJ_FUNC_ARG's by-value fetch dispatches into the
+							 * FETCH_OBJ_R handler, which may take the SIMPLE_GET hook fast
+							 * path and push a getter frame; by-ref dispatches into
+							 * FETCH_OBJ_W. The function JIT may keep values solely in
+							 * registers, so we must NOT exit to the VM (stale stack slots).
+							 * Inline the by-value path through zend_jit_fetch_obj, which runs
+							 * the hook getter inside a helper and keeps all registers live.
+							 * The by-ref path has no SIMPLE_GET fast path, so the generic
+							 * handler (a full C call, safe under register allocation) is used.
+							 * This mirrors the tracing JIT fix for GH-21006 (GH-21369); the
+							 * runtime by-ref check is required because the passing mode is
+							 * only known once the callee is resolved via namespace fallback.
+							 * See GH-22857. */
 							if (!zend_jit_fetch_obj_func_arg(jit, opline, op_array, ssa, ssa_op,
-								op1_info, op1_addr, ce, ce_is_instanceof, on_this, RES_REG_ADDR())) {
+									op1_info, op1_addr, ce, ce_is_instanceof, on_this, RES_REG_ADDR())) {
 								goto jit_failure;
 							}
 						} else {
 							if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op,
-								op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
-								RES_REG_ADDR(), IS_UNKNOWN,
-								zend_may_throw(opline, ssa_op, op_array, ssa))) {
+									op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
+									RES_REG_ADDR(), IS_UNKNOWN,
+									zend_may_throw(opline, ssa_op, op_array, ssa))) {
 								goto jit_failure;
 							}
 						}
diff --git a/ext/opcache/tests/jit/gh22857.phpt b/ext/opcache/tests/jit/gh22857.phpt
index 2efd761f2fe..fde36d19b39 100644
--- a/ext/opcache/tests/jit/gh22857.phpt
+++ b/ext/opcache/tests/jit/gh22857.phpt
@@ -1,125 +1,125 @@
---TEST--
-GH-22857: Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on a property hook (SIMPLE_GET fast path)
---INI--
-opcache.enable=1
-opcache.enable_cli=1
-opcache.jit_buffer_size=64M
-opcache.jit=1205
-opcache.jit_hot_func=1
---EXTENSIONS--
-opcache
---FILE--
-<?php
-namespace Test;
-
-interface HandlerInterface { public function noop(): void; }
-
-final class DefaultHandler implements HandlerInterface {
-    private static ?self $i = null;
-    public static function getInstance(): self { return self::$i ??= new self(); }
-    public function noop(): void {}
-}
-
-/* Original issue: virtual property hook read via FETCH_OBJ_FUNC_ARG under
- * function JIT. The getter frame is pushed by the SIMPLE_GET fast path in the
- * shared FETCH_OBJ_R handler, but the JIT-compiled FUNC_ARG opcode had no
- * hook-enter guard, so the argument slot was read before the getter ran.
- *
- * The assertion is deterministic: the getter sets a static flag as a side
- * effect, so we check whether the getter actually ran when the property is
- * passed through FETCH_OBJ_FUNC_ARG. This avoids the previous data-dependent
- * failure mode (relying on file_get_contents() fataling on whatever garbage
- * the unguarded JIT read happened to land on), which made the regression
- * test flaky. */
-class Container {
-    private static bool $getterRan = false;
-
-    public protected(set) HandlerInterface $handler;
-
-    public string $path {
-        get => (self::$getterRan = true)
-            ? self::build($this->kind, $this->id)
-            : self::build($this->kind, $this->id);
-    }
-
-    protected mixed $prev = null;
-
-    public function __construct(
-        public protected(set) string $kind,
-        public protected(set) string $id,
-    ) {
-        $this->handler = DefaultHandler::getInstance();
-    }
-
-    public static function build(string $k, string $i): string {
-        return "/nonexistent/gh22857_{$k}_{$i}.dat";
-    }
-
-    public function step(): void {
-        /* Unqualified namespaced-fallback call (INIT_NS_FCALL_BY_NAME) keeps
-         * the FETCH_OBJ_FUNC_ARG opcode instead of letting the optimizer
-         * rewrite it to FETCH_OBJ_R. @ preserves the opcode shape that
-         * triggers the bug. */
-        @file_get_contents($this->path);
-        if (!self::$getterRan) {
-            throw new \RuntimeException('getter did not run via FUNC_ARG');
-        }
-    }
-}
-
-$c = new Container('alpha', 'beta');
-$c->step();
-$c->step();
-$c->step();
-
-/* Sibling-slot variant: a preceding plain FETCH_OBJ_R primes the
- * SIMPLE_GET bit on the property cache slot; compact_literals shares the
- * slot between FETCH_OBJ_R and FETCH_OBJ_FUNC_ARG for the same property,
- * so the following FETCH_OBJ_FUNC_ARG consumes that bit and hits the
- * SIMPLE_GET fast path. Without the hook-enter guard it passes whatever
- * sits in an adjacent property slot instead of running the getter. The
- * flag is reset after the priming FETCH_OBJ_R so the assertion reflects
- * only whether the getter ran during the FETCH_OBJ_FUNC_ARG read. */
-class Container2 {
-    private static bool $getterRan = false;
-
-    public protected(set) HandlerInterface $handler;
-
-    public string $path {
-        get => (self::$getterRan = true)
-            ? self::build($this->kind, $this->id)
-            : self::build($this->kind, $this->id);
-    }
-
-    protected mixed $prev = null;
-
-    public function __construct(
-        public protected(set) string $kind,
-        public protected(set) string $id,
-    ) {
-        $this->handler = DefaultHandler::getInstance();
-    }
-
-    public static function build(string $k, string $i): string {
-        return "/nonexistent/gh22857b_{$k}_{$i}.dat";
-    }
-
-    public function step(): void {
-        $this->prev = $this->path;   // FETCH_OBJ_R primes SIMPLE_GET on shared slot
-        self::$getterRan = false;     // reset; flag now reflects only the FUNC_ARG read below
-        @file_get_contents($this->path); // FETCH_OBJ_FUNC_ARG consumes the primed bit
-        if (!self::$getterRan) {
-            throw new \RuntimeException('sibling-slot variant: getter did not run via FUNC_ARG');
-        }
-    }
-}
-
-$c2 = new Container2('alpha', 'beta');
-$c2->step();
-$c2->step();
-$c2->step();
-
-echo "OK\n";
-?>
---EXPECT--
-OK
+--TEST--
+GH-22857: Function JIT emits wrong code for FETCH_OBJ_FUNC_ARG on a property hook (SIMPLE_GET fast path)
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.jit_buffer_size=64M
+opcache.jit=1205
+opcache.jit_hot_func=1
+--EXTENSIONS--
+opcache
+--FILE--
+<?php
+namespace Test;
+
+interface HandlerInterface { public function noop(): void; }
+
+final class DefaultHandler implements HandlerInterface {
+    private static ?self $i = null;
+    public static function getInstance(): self { return self::$i ??= new self(); }
+    public function noop(): void {}
+}
+
+/* Original issue: virtual property hook read via FETCH_OBJ_FUNC_ARG under
+ * function JIT. The getter frame is pushed by the SIMPLE_GET fast path in the
+ * shared FETCH_OBJ_R handler, but the JIT-compiled FUNC_ARG opcode had no
+ * hook-enter guard, so the argument slot was read before the getter ran.
+ *
+ * The assertion is deterministic: the getter sets a static flag as a side
+ * effect, so we check whether the getter actually ran when the property is
+ * passed through FETCH_OBJ_FUNC_ARG. This avoids the previous data-dependent
+ * failure mode (relying on file_get_contents() fataling on whatever garbage
+ * the unguarded JIT read happened to land on), which made the regression
+ * test flaky. */
+class Container {
+    private static bool $getterRan = false;
+
+    public protected(set) HandlerInterface $handler;
+
+    public string $path {
+        get => (self::$getterRan = true)
+            ? self::build($this->kind, $this->id)
+            : self::build($this->kind, $this->id);
+    }
+
+    protected mixed $prev = null;
+
+    public function __construct(
+        public protected(set) string $kind,
+        public protected(set) string $id,
+    ) {
+        $this->handler = DefaultHandler::getInstance();
+    }
+
+    public static function build(string $k, string $i): string {
+        return "/nonexistent/gh22857_{$k}_{$i}.dat";
+    }
+
+    public function step(): void {
+        /* Unqualified namespaced-fallback call (INIT_NS_FCALL_BY_NAME) keeps
+         * the FETCH_OBJ_FUNC_ARG opcode instead of letting the optimizer
+         * rewrite it to FETCH_OBJ_R. @ preserves the opcode shape that
+         * triggers the bug. */
+        @file_get_contents($this->path);
+        if (!self::$getterRan) {
+            throw new \RuntimeException('getter did not run via FUNC_ARG');
+        }
+    }
+}
+
+$c = new Container('alpha', 'beta');
+$c->step();
+$c->step();
+$c->step();
+
+/* Sibling-slot variant: a preceding plain FETCH_OBJ_R primes the
+ * SIMPLE_GET bit on the property cache slot; compact_literals shares the
+ * slot between FETCH_OBJ_R and FETCH_OBJ_FUNC_ARG for the same property,
+ * so the following FETCH_OBJ_FUNC_ARG consumes that bit and hits the
+ * SIMPLE_GET fast path. Without the hook-enter guard it passes whatever
+ * sits in an adjacent property slot instead of running the getter. The
+ * flag is reset after the priming FETCH_OBJ_R so the assertion reflects
+ * only whether the getter ran during the FETCH_OBJ_FUNC_ARG read. */
+class Container2 {
+    private static bool $getterRan = false;
+
+    public protected(set) HandlerInterface $handler;
+
+    public string $path {
+        get => (self::$getterRan = true)
+            ? self::build($this->kind, $this->id)
+            : self::build($this->kind, $this->id);
+    }
+
+    protected mixed $prev = null;
+
+    public function __construct(
+        public protected(set) string $kind,
+        public protected(set) string $id,
+    ) {
+        $this->handler = DefaultHandler::getInstance();
+    }
+
+    public static function build(string $k, string $i): string {
+        return "/nonexistent/gh22857b_{$k}_{$i}.dat";
+    }
+
+    public function step(): void {
+        $this->prev = $this->path;   // FETCH_OBJ_R primes SIMPLE_GET on shared slot
+        self::$getterRan = false;     // reset; flag now reflects only the FUNC_ARG read below
+        @file_get_contents($this->path); // FETCH_OBJ_FUNC_ARG consumes the primed bit
+        if (!self::$getterRan) {
+            throw new \RuntimeException('sibling-slot variant: getter did not run via FUNC_ARG');
+        }
+    }
+}
+
+$c2 = new Container2('alpha', 'beta');
+$c2->step();
+$c2->step();
+$c2->step();
+
+echo "OK\n";
+?>
+--EXPECT--
+OK