[php-src] Issue #24482: Tracing JIT: second `($d & 1) === 0` in a loop iteration evaluates to true for odd `$d` after side traces are compiled

From: Date: Wed, 21 Oct 2026 15:36:28 +0000
Subject: [php-src] Issue #24482: Tracing JIT: second `($d & 1) === 0` in a loop iteration evaluates to true for odd `$d` after side traces are compiled
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253054@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24482 Author: gitKBDL ### Description The following code: ```php <?php function f(array $k): array { $out = [0, 0, 0, 0]; for ($i = 0; $i < 1000; ++$i) { for ($d = 0; $d < 4; ++$d) { $a = $k[$d < 2 ? 0 : 1]; $b = $k[($d & 1) === 0 ? 2 : 3]; if (($d & 1) === 0) { ++$out[$d]; // only $d = 0 and $d = 2 can get here } } } return $out; } echo json_encode(f([0, 1, 2, 3])), "\n"; ``` Run with: ``` php -d opcache.enable_cli=1 -d opcache.jit=tracing -d opcache.jit_buffer_size=64M test.php ``` Resulted in this output: ``` [1000,0,1000,984] ``` But I expected this output instead: ``` [1000,0,1000,0] ``` The second ($d & 1) === 0 of an iteration comes out true for $d = 3 once the side traces are compiled (984 of the 1000 iterations; the first 16 are right). What we found while narrowing it down (official Docker images php:8.5-cli, php:8.4-cli, php:8.3-cli, x86_64): - PHP 8.5.11 (NTS and ZTS): wrong with opcache.jit=tracing, 1254, 1253, 1255, 1154. Right with 1054 (no register allocation) and with the JIT off. opcache.optimization_level makes no difference, even 0. - PHP 8.4.26: wrong too ([1000,983,1000,987]). - PHP 8.3.35: right. - The wrong second test is always the same arithmetic result compared with the literal 0: ($d & 1) === 0, !== 0, == 0, > 0, <= 0, 0 === ($d & 1), $d % 2 === 0, ($d ^ 1) === 0, ($d - 1) === 0. It is right when it is compared with another constant (=== 1), when the result is kept in a variable and that is tested twice, when the loop variable itself is tested ($d === 0), and when the tests are in different functions. - Something must read an array in a branch of the first test ($k[... ? 2 : 3], or if (...) { $b = $k[2]; } else { $b = $k[3]; }). With $b = (...) ? 2 : 3 or if (...) { $b = 1; } it is right. - The $d < 2 branch before it is not always needed: with ($d ^ 1) === 0 the second test is wrong without it. ### PHP Version ``` PHP 8.5.11 (cli) (NTS), official php:8.5-cli image PHP 8.4.26 (cli) (NTS), official php:8.4-cli image ``` ### Operating System Debian (official Docker images), x86_64

« previous php.bugs (#253054) next »