|
| 1 | +--TEST-- |
| 2 | +GH-24683 (B): tracing JIT must preserve overwritten runtime CV values |
| 3 | +--EXTENSIONS-- |
| 4 | +opcache |
| 5 | +--SKIPIF-- |
| 6 | +<?php if (PHP_INT_SIZE != 8) die("skip 64-bit only"); ?> |
| 7 | +--INI-- |
| 8 | +opcache.enable=1 |
| 9 | +opcache.enable_cli=1 |
| 10 | +opcache.jit=tracing |
| 11 | +opcache.jit_buffer_size=64M |
| 12 | +opcache.jit_hot_loop=61 |
| 13 | +opcache.jit_hot_func=127 |
| 14 | +opcache.jit_hot_return=8 |
| 15 | +opcache.jit_hot_side_exit=8 |
| 16 | +opcache.file_update_protection=0 |
| 17 | +--FILE-- |
| 18 | +<?php |
| 19 | +// php -d opcache.enable_cli=1 -d opcache.jit=tracing -d opcache.jit_buffer_size=64M repro.php |
| 20 | +// |
| 21 | +// Case 1 stores to $ram and moves on to case 2. Case 2 sets $x to a value it |
| 22 | +// only knows at run time (0x800E0000 | $zero, where $zero is 0), uses it as an |
| 23 | +// address, and overwrites $x with the byte loaded from there (4). Once the loop |
| 24 | +// trace is compiled, $x sometimes comes back as 0x800E0000 instead. |
| 25 | + |
| 26 | +final class Bus |
| 27 | +{ |
| 28 | + public function read8(int $address): int { return 0; } |
| 29 | +} |
| 30 | + |
| 31 | +$fn = static function (Bus $bus, array &$r, array &$ram, array &$sp, int $pc): int { |
| 32 | + $value = $r[4]; |
| 33 | + $base = $r[15]; |
| 34 | + $zero = $r[0]; |
| 35 | + while (true) { |
| 36 | + switch ($pc) { |
| 37 | + case 1: |
| 38 | + $a = ($base + 2) & 0xFFFFFFFF; |
| 39 | + { |
| 40 | + $p = $a & 0x1FFFFFFF; |
| 41 | + $inRam = $a < 0xC0000000 && $p < 0x00800000; |
| 42 | + if ($inRam) { $k = ($p & 0x1FFFFF) >> 2; $ram[$k] = ($ram[$k] & ~(65535 << (($p & 3) << 3))) | (($value & 65535) << (($p & 3) << 3)); } |
| 43 | + } |
| 44 | + $pc = 2; |
| 45 | + continue 2; |
| 46 | + case 2: |
| 47 | + $x = 0x800E0000 | $zero; |
| 48 | + $a = ($x + 0xFFFFEDED) & 0xFFFFFFFF; |
| 49 | + $p = $a & 0x1FFFFFFF; |
| 50 | + $byte = ((($inRam ? ($ram[($p & 0x1FFFFF) >> 2] >> (($p & 3) << 3)) & 0xFF : ($inSp ? ($sp[($p - 0x1F800000) >> 2] >> (($p & 3) << 3)) & 0xFF : $bus->read8($a))) ^ 0x80) - 0x80) & 0xFFFFFFFF; |
| 51 | + $x = $byte; |
| 52 | + $pc = 3; |
| 53 | + continue 2; |
| 54 | + default: |
| 55 | + goto leave; |
| 56 | + } |
| 57 | + } |
| 58 | + leave: |
| 59 | + $r[12] = $x; |
| 60 | + return $pc; |
| 61 | +}; |
| 62 | + |
| 63 | +$bus = new Bus(); |
| 64 | +$ram = array_fill(0, 0x80000, 0); |
| 65 | +$ram[0xDEDED >> 2] = 0x0003047F; // the byte at 0xDEDED is 4 |
| 66 | +$sp = []; |
| 67 | +$wrong = 0; |
| 68 | +for ($k = 0; $k < 300; $k++) { |
| 69 | + $r = array_fill(0, 32, 0); |
| 70 | + $r[4] = 2; |
| 71 | + $fn($bus, $r, $ram, $sp, 1); |
| 72 | + if ($r[12] !== 4) { |
| 73 | + if ($wrong++ === 0) { |
| 74 | + printf("call %d: \$x is 0x%X, expected 4\n", $k, $r[12]); |
| 75 | + } |
| 76 | + } |
| 77 | +} |
| 78 | +echo "wrong: $wrong\n"; |
| 79 | +--EXPECT-- |
| 80 | +wrong: 0 |
0 commit comments