Skip to content

Commit 896f76d

Browse files
committed
Fix preloaded functions not JIT-compiled again after OPcache restart
zend_jit_restart_hot_counters() restored the hot counters but kept ZEND_FUNC_JITED, so zend_jit_hot_func() never compiled the function again. With opcache.jit_hot_func=1 this loops forever. Closes GH-25043
1 parent ce899c8 commit 896f76d

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ PHP NEWS
202202
(Pavel Gushcha)
203203
. Fixed potential infinite recursion/hang (HYBRID/TAILCALL respectively)
204204
when zend_interrupt_function resets EG(vm_interrupt). (Levi Morrison)
205+
. Fixed preloaded functions not JIT-compiled again after OPcache restart.
206+
(Jorg Sowa)
205207

206208
- OpenSSL:
207209
. Fixed bug GH-24784 (openssl_pkcs7_read() and openssl_cms_read() overwrite

‎ext/opcache/jit/zend_jit.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,6 +3124,8 @@ static int zend_jit_restart_hot_counters(zend_op_array *op_array)
31243124
uint32_t i;
31253125

31263126
jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array);
3127+
/* The JIT-ed code is gone, so allow compiling it again */
3128+
jit_extension->func_info.flags &= ~ZEND_FUNC_JITED;
31273129
for (i = 0; i < op_array->last; i++) {
31283130
op_array->opcodes[i].handler = jit_extension->orig_handlers[i];
31293131
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--TEST--
2+
Preloaded functions JIT-compiled by hot counters must be JIT-compiled again after an OPcache restart
3+
--EXTENSIONS--
4+
opcache
5+
--SKIPIF--
6+
<?php
7+
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
8+
if (ini_get('opcache.jit') === false) die('skip PHP is compiled without JIT');
9+
?>
10+
--INI--
11+
opcache.enable=1
12+
opcache.enable_cli=1
13+
opcache.jit=1235
14+
opcache.jit_buffer_size=16M
15+
--CONFLICTS--
16+
server
17+
--FILE--
18+
<?php
19+
$dir = __DIR__ . '/preload_restart_hot_counters';
20+
@mkdir($dir);
21+
22+
file_put_contents($dir . '/preload.php', <<<'PHP'
23+
<?php
24+
function preloaded_sum(int $n): int {
25+
$s = 0;
26+
for ($i = 0; $i < $n; $i++) {
27+
$s += ($i * 3) ^ ($i >> 2);
28+
}
29+
return $s;
30+
}
31+
PHP);
32+
33+
/* A dynamic call keeps the function out of the call graph of the calling script */
34+
file_put_contents($dir . '/call.php', <<<'PHP'
35+
<?php
36+
$f = 'preloaded_sum';
37+
echo $f(1000), "\n";
38+
PHP);
39+
40+
file_put_contents($dir . '/reset.php', <<<'PHP'
41+
<?php
42+
var_dump(opcache_get_status()['jit']['on']);
43+
var_dump(opcache_reset());
44+
PHP);
45+
46+
include __DIR__ . '/../php_cli_server.inc';
47+
48+
$ini = '-d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.jit=1235 -d opcache.jit_buffer_size=16M'
49+
. ' -d opcache.jit_hot_func=1 -d opcache.validate_timestamps=0 -d opcache.file_update_protection=0'
50+
. ' -d opcache.preload_user=root -d opcache.preload=' . $dir . '/preload.php';
51+
$opcache = ini_get('extension_dir') . '/opcache.' . PHP_SHLIB_SUFFIX;
52+
if (file_exists($opcache)) {
53+
$ini .= ' -d zend_extension=' . $opcache;
54+
}
55+
php_cli_server_start($ini);
56+
57+
$base = 'http://' . PHP_CLI_SERVER_ADDRESS . '/jit/preload_restart_hot_counters/';
58+
$context = stream_context_create(['http' => ['timeout' => 5]]);
59+
foreach (['call', 'reset', 'call', 'call'] as $script) {
60+
echo @file_get_contents($base . $script . '.php', false, $context) ?: "no response\n";
61+
}
62+
?>
63+
--CLEAN--
64+
<?php
65+
$dir = __DIR__ . '/preload_restart_hot_counters';
66+
foreach (['preload', 'call', 'reset'] as $script) {
67+
@unlink("$dir/$script.php");
68+
}
69+
@rmdir($dir);
70+
?>
71+
--EXPECT--
72+
1502868
73+
bool(true)
74+
bool(true)
75+
1502868
76+
1502868

0 commit comments

Comments
 (0)