Skip to content

Commit 654b787

Browse files
authored
Add API to exempt function from being traced in JIT (php#15559)
Internally accessible via zend_jit_blacklist_function / externally via opcache_jit_blacklist. The functionality currently only affects tracing JIT, but may be extended to other JIT modes in future.
1 parent f89eb15 commit 654b787

11 files changed

+101
-14
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ PHP NEWS
2727

2828
- Opcache:
2929
. Fixed bug GH-15657 (Segmentation fault in dasm_x86.h). (nielsdos)
30+
. Added opcache_jit_blacklist() function. (Bob)
3031

3132
- PHPDBG:
3233
. Fixed bug GH-15901 (phpdbg: Assertion failure on i funcs). (cmb)

UPGRADING

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,10 @@ PHP 8.4 UPGRADE NOTES
820820
. Added mb_ucfirst and mb_lcfirst functions.
821821
RFC: https://wiki.php.net/rfc/mb_ucfirst
822822

823+
- OPCache:
824+
. Added opcache_jit_blacklist function. It allows skipping the tracing JIT
825+
execution of select functions.
826+
823827
- PCNTL:
824828
. Added pcntl_setns allowing a process to be reassociated with a namespace in order
825829
to share resources with other processes within this context.

ext/opcache/jit/zend_jit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *memb
706706
# endif
707707
#endif
708708

709-
void zend_jit_status(zval *ret)
709+
ZEND_EXT_API void zend_jit_status(zval *ret)
710710
{
711711
zval stats;
712712
array_init(&stats);

ext/opcache/jit/zend_jit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ void zend_jit_startup(void *jit_buffer, size_t size, bool reattached);
162162
void zend_jit_shutdown(void);
163163
void zend_jit_activate(void);
164164
void zend_jit_deactivate(void);
165-
void zend_jit_status(zval *ret);
165+
ZEND_EXT_API void zend_jit_status(zval *ret);
166+
ZEND_EXT_API void zend_jit_blacklist_function(zend_op_array *op_array);
166167
void zend_jit_restart(void);
167168

168169
#define ZREG_LOAD (1<<0)

ext/opcache/jit/zend_jit_ir.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10102,6 +10102,20 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
1010210102
ir_STORE(jit_EX(opline), jit_IP(jit));
1010310103
}
1010410104
jit_observer_fcall_begin(jit, rx, observer_handler);
10105+
10106+
if (trace) {
10107+
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
10108+
10109+
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
10110+
if (!exit_addr) {
10111+
return 0;
10112+
}
10113+
} else {
10114+
exit_addr = NULL;
10115+
}
10116+
10117+
zend_jit_check_timeout(jit, NULL /* we're inside the called function */, exit_addr);
10118+
1010510119
jit_observer_fcall_is_unobserved_end(jit, &unobserved_data);
1010610120
}
1010710121

ext/opcache/jit/zend_jit_trace.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7656,6 +7656,24 @@ static void zend_jit_blacklist_root_trace(const zend_op *opline, size_t offset)
76567656
zend_shared_alloc_unlock();
76577657
}
76587658

7659+
ZEND_EXT_API void zend_jit_blacklist_function(zend_op_array *op_array) {
7660+
zend_jit_op_array_trace_extension *jit_extension = (zend_jit_op_array_trace_extension *)ZEND_FUNC_INFO(op_array);
7661+
if (!jit_extension || !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE)) {
7662+
return;
7663+
}
7664+
7665+
zend_shared_alloc_lock();
7666+
SHM_UNPROTECT();
7667+
zend_jit_unprotect();
7668+
7669+
zend_jit_stop_persistent_op_array(op_array);
7670+
jit_extension->func_info.flags &= ~ZEND_FUNC_JIT_ON_HOT_TRACE;
7671+
7672+
zend_jit_protect();
7673+
SHM_PROTECT();
7674+
zend_shared_alloc_unlock();
7675+
}
7676+
76597677
static bool zend_jit_trace_is_bad_root(const zend_op *opline, zend_jit_trace_stop stop, size_t offset)
76607678
{
76617679
const zend_op **cache_opline = JIT_G(bad_root_cache_opline);

ext/opcache/jit/zend_jit_vm_helpers.c

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -521,16 +521,17 @@ static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend
521521
&& (func->op_array.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE))) {
522522
return -1;
523523
}
524-
if (func->type == ZEND_USER_FUNCTION
525-
&& (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
524+
if (func->type == ZEND_USER_FUNCTION) {
526525
jit_extension =
527526
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&func->op_array);
528-
if (UNEXPECTED(!jit_extension
529-
|| !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE)
530-
|| (func->op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE))) {
527+
if (UNEXPECTED(!jit_extension && (func->op_array.fn_flags & ZEND_ACC_CLOSURE))
528+
|| (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE))
529+
|| (func->op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
531530
return -1;
532531
}
533-
func = (zend_function*)jit_extension->op_array;
532+
if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) {
533+
func = (zend_function*)jit_extension->op_array;
534+
}
534535
}
535536
if (is_megamorphic == ZEND_JIT_EXIT_POLYMORPHISM
536537
/* TODO: use more accurate check ??? */
@@ -1100,17 +1101,18 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
11001101
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
11011102
break;
11021103
}
1103-
if (func->type == ZEND_USER_FUNCTION
1104-
&& (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
1104+
if (func->type == ZEND_USER_FUNCTION) {
11051105
jit_extension =
11061106
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&func->op_array);
1107-
if (UNEXPECTED(!jit_extension)
1108-
|| !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE)
1107+
if (UNEXPECTED(!jit_extension && (func->op_array.fn_flags & ZEND_ACC_CLOSURE))
1108+
|| (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE))
11091109
|| (func->op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
11101110
stop = ZEND_JIT_TRACE_STOP_INTERPRETER;
11111111
break;
11121112
}
1113-
func = (zend_function*)jit_extension->op_array;
1113+
if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) {
1114+
func = (zend_function*)jit_extension->op_array;
1115+
}
11141116
}
11151117

11161118
#ifndef HAVE_GCC_GLOBAL_REGS

ext/opcache/opcache.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ function opcache_compile_file(string $filename): bool {}
1414

1515
function opcache_invalidate(string $filename, bool $force = false): bool {}
1616

17+
function opcache_jit_blacklist(Closure $closure): void {}
18+
1719
/**
1820
* @return array<string, mixed>|false
1921
* @refcount 1

ext/opcache/opcache_arginfo.h

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Basic usage of opcache_jit_blacklist()
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.file_update_protection=0
7+
opcache.protect_memory=1
8+
opcache.jit=tracing
9+
--EXTENSIONS--
10+
opcache
11+
--FILE--
12+
<?php
13+
function foo() {
14+
$x = 1;
15+
$x += 0;
16+
++$x;
17+
var_dump($x);
18+
}
19+
opcache_jit_blacklist(foo(...));
20+
foo();
21+
?>
22+
--EXPECT--
23+
int(2)

ext/opcache/zend_accelerator_module.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "php.h"
2525
#include "ZendAccelerator.h"
2626
#include "zend_API.h"
27+
#include "zend_closures.h"
2728
#include "zend_shared_alloc.h"
2829
#include "zend_accelerator_blacklist.h"
2930
#include "php_ini.h"
@@ -924,6 +925,21 @@ ZEND_FUNCTION(opcache_invalidate)
924925
}
925926
}
926927

928+
/* {{{ Prevents JIT on function. Call it before the first invocation of the given function. */
929+
ZEND_FUNCTION(opcache_jit_blacklist)
930+
{
931+
zval *closure;
932+
933+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &closure, zend_ce_closure) == FAILURE) {
934+
RETURN_THROWS();
935+
}
936+
937+
const zend_function *func = zend_get_closure_method_def(Z_OBJ_P(closure));
938+
if (ZEND_USER_CODE(func->type)) {
939+
zend_jit_blacklist_function((zend_op_array *)&func->op_array);
940+
}
941+
}
942+
927943
ZEND_FUNCTION(opcache_compile_file)
928944
{
929945
zend_string *script_name;

0 commit comments

Comments
 (0)