mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Add API to exempt function from being traced in JIT (#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.
This commit is contained in:
1
NEWS
1
NEWS
@@ -27,6 +27,7 @@ PHP NEWS
|
||||
|
||||
- Opcache:
|
||||
. Fixed bug GH-15657 (Segmentation fault in dasm_x86.h). (nielsdos)
|
||||
. Added opcache_jit_blacklist() function. (Bob)
|
||||
|
||||
- PHPDBG:
|
||||
. Fixed bug GH-15901 (phpdbg: Assertion failure on i funcs). (cmb)
|
||||
|
||||
@@ -820,6 +820,10 @@ PHP 8.4 UPGRADE NOTES
|
||||
. Added mb_ucfirst and mb_lcfirst functions.
|
||||
RFC: https://wiki.php.net/rfc/mb_ucfirst
|
||||
|
||||
- OPCache:
|
||||
. Added opcache_jit_blacklist function. It allows skipping the tracing JIT
|
||||
execution of select functions.
|
||||
|
||||
- PCNTL:
|
||||
. Added pcntl_setns allowing a process to be reassociated with a namespace in order
|
||||
to share resources with other processes within this context.
|
||||
|
||||
@@ -706,7 +706,7 @@ static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *memb
|
||||
# endif
|
||||
#endif
|
||||
|
||||
void zend_jit_status(zval *ret)
|
||||
ZEND_EXT_API void zend_jit_status(zval *ret)
|
||||
{
|
||||
zval stats;
|
||||
array_init(&stats);
|
||||
|
||||
@@ -162,7 +162,8 @@ void zend_jit_startup(void *jit_buffer, size_t size, bool reattached);
|
||||
void zend_jit_shutdown(void);
|
||||
void zend_jit_activate(void);
|
||||
void zend_jit_deactivate(void);
|
||||
void zend_jit_status(zval *ret);
|
||||
ZEND_EXT_API void zend_jit_status(zval *ret);
|
||||
ZEND_EXT_API void zend_jit_blacklist_function(zend_op_array *op_array);
|
||||
void zend_jit_restart(void);
|
||||
|
||||
#define ZREG_LOAD (1<<0)
|
||||
|
||||
@@ -10102,6 +10102,20 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
|
||||
ir_STORE(jit_EX(opline), jit_IP(jit));
|
||||
}
|
||||
jit_observer_fcall_begin(jit, rx, observer_handler);
|
||||
|
||||
if (trace) {
|
||||
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM);
|
||||
|
||||
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
|
||||
if (!exit_addr) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
exit_addr = NULL;
|
||||
}
|
||||
|
||||
zend_jit_check_timeout(jit, NULL /* we're inside the called function */, exit_addr);
|
||||
|
||||
jit_observer_fcall_is_unobserved_end(jit, &unobserved_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -7656,6 +7656,24 @@ static void zend_jit_blacklist_root_trace(const zend_op *opline, size_t offset)
|
||||
zend_shared_alloc_unlock();
|
||||
}
|
||||
|
||||
ZEND_EXT_API void zend_jit_blacklist_function(zend_op_array *op_array) {
|
||||
zend_jit_op_array_trace_extension *jit_extension = (zend_jit_op_array_trace_extension *)ZEND_FUNC_INFO(op_array);
|
||||
if (!jit_extension || !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
zend_shared_alloc_lock();
|
||||
SHM_UNPROTECT();
|
||||
zend_jit_unprotect();
|
||||
|
||||
zend_jit_stop_persistent_op_array(op_array);
|
||||
jit_extension->func_info.flags &= ~ZEND_FUNC_JIT_ON_HOT_TRACE;
|
||||
|
||||
zend_jit_protect();
|
||||
SHM_PROTECT();
|
||||
zend_shared_alloc_unlock();
|
||||
}
|
||||
|
||||
static bool zend_jit_trace_is_bad_root(const zend_op *opline, zend_jit_trace_stop stop, size_t offset)
|
||||
{
|
||||
const zend_op **cache_opline = JIT_G(bad_root_cache_opline);
|
||||
|
||||
@@ -521,16 +521,17 @@ static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend
|
||||
&& (func->op_array.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE))) {
|
||||
return -1;
|
||||
}
|
||||
if (func->type == ZEND_USER_FUNCTION
|
||||
&& (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
|
||||
if (func->type == ZEND_USER_FUNCTION) {
|
||||
jit_extension =
|
||||
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&func->op_array);
|
||||
if (UNEXPECTED(!jit_extension
|
||||
|| !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE)
|
||||
|| (func->op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE))) {
|
||||
if (UNEXPECTED(!jit_extension && (func->op_array.fn_flags & ZEND_ACC_CLOSURE))
|
||||
|| (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE))
|
||||
|| (func->op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
|
||||
return -1;
|
||||
}
|
||||
func = (zend_function*)jit_extension->op_array;
|
||||
if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) {
|
||||
func = (zend_function*)jit_extension->op_array;
|
||||
}
|
||||
}
|
||||
if (is_megamorphic == ZEND_JIT_EXIT_POLYMORPHISM
|
||||
/* TODO: use more accurate check ??? */
|
||||
@@ -1100,17 +1101,18 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
||||
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
|
||||
break;
|
||||
}
|
||||
if (func->type == ZEND_USER_FUNCTION
|
||||
&& (func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
|
||||
if (func->type == ZEND_USER_FUNCTION) {
|
||||
jit_extension =
|
||||
(zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(&func->op_array);
|
||||
if (UNEXPECTED(!jit_extension)
|
||||
|| !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE)
|
||||
if (UNEXPECTED(!jit_extension && (func->op_array.fn_flags & ZEND_ACC_CLOSURE))
|
||||
|| (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE))
|
||||
|| (func->op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
|
||||
stop = ZEND_JIT_TRACE_STOP_INTERPRETER;
|
||||
break;
|
||||
}
|
||||
func = (zend_function*)jit_extension->op_array;
|
||||
if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) {
|
||||
func = (zend_function*)jit_extension->op_array;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_GCC_GLOBAL_REGS
|
||||
|
||||
@@ -14,6 +14,8 @@ function opcache_compile_file(string $filename): bool {}
|
||||
|
||||
function opcache_invalidate(string $filename, bool $force = false): bool {}
|
||||
|
||||
function opcache_jit_blacklist(Closure $closure): void {}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|false
|
||||
* @refcount 1
|
||||
|
||||
8
ext/opcache/opcache_arginfo.h
generated
8
ext/opcache/opcache_arginfo.h
generated
@@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 81f337ea4ac5361ca4a0873fcd3b033beaf524c6 */
|
||||
* Stub hash: c416c231c5d1270b7e5961f84cc3ca3e29db4959 */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_opcache_reset, 0, 0, _IS_BOOL, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
@@ -17,6 +17,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_opcache_invalidate, 0, 1, _IS_BO
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, force, _IS_BOOL, 0, "false")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_opcache_jit_blacklist, 0, 1, IS_VOID, 0)
|
||||
ZEND_ARG_OBJ_INFO(0, closure, Closure, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_opcache_get_configuration, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
@@ -26,6 +30,7 @@ ZEND_FUNCTION(opcache_reset);
|
||||
ZEND_FUNCTION(opcache_get_status);
|
||||
ZEND_FUNCTION(opcache_compile_file);
|
||||
ZEND_FUNCTION(opcache_invalidate);
|
||||
ZEND_FUNCTION(opcache_jit_blacklist);
|
||||
ZEND_FUNCTION(opcache_get_configuration);
|
||||
ZEND_FUNCTION(opcache_is_script_cached);
|
||||
|
||||
@@ -34,6 +39,7 @@ static const zend_function_entry ext_functions[] = {
|
||||
ZEND_FE(opcache_get_status, arginfo_opcache_get_status)
|
||||
ZEND_FE(opcache_compile_file, arginfo_opcache_compile_file)
|
||||
ZEND_FE(opcache_invalidate, arginfo_opcache_invalidate)
|
||||
ZEND_FE(opcache_jit_blacklist, arginfo_opcache_jit_blacklist)
|
||||
ZEND_FE(opcache_get_configuration, arginfo_opcache_get_configuration)
|
||||
ZEND_FE(opcache_is_script_cached, arginfo_opcache_is_script_cached)
|
||||
ZEND_FE_END
|
||||
|
||||
23
ext/opcache/tests/jit/opcache_jit_blacklist.phpt
Normal file
23
ext/opcache/tests/jit/opcache_jit_blacklist.phpt
Normal file
@@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
Basic usage of opcache_jit_blacklist()
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.file_update_protection=0
|
||||
opcache.protect_memory=1
|
||||
opcache.jit=tracing
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--FILE--
|
||||
<?php
|
||||
function foo() {
|
||||
$x = 1;
|
||||
$x += 0;
|
||||
++$x;
|
||||
var_dump($x);
|
||||
}
|
||||
opcache_jit_blacklist(foo(...));
|
||||
foo();
|
||||
?>
|
||||
--EXPECT--
|
||||
int(2)
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "php.h"
|
||||
#include "ZendAccelerator.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_closures.h"
|
||||
#include "zend_shared_alloc.h"
|
||||
#include "zend_accelerator_blacklist.h"
|
||||
#include "php_ini.h"
|
||||
@@ -924,6 +925,21 @@ ZEND_FUNCTION(opcache_invalidate)
|
||||
}
|
||||
}
|
||||
|
||||
/* {{{ Prevents JIT on function. Call it before the first invocation of the given function. */
|
||||
ZEND_FUNCTION(opcache_jit_blacklist)
|
||||
{
|
||||
zval *closure;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &closure, zend_ce_closure) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
const zend_function *func = zend_get_closure_method_def(Z_OBJ_P(closure));
|
||||
if (ZEND_USER_CODE(func->type)) {
|
||||
zend_jit_blacklist_function((zend_op_array *)&func->op_array);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_FUNCTION(opcache_compile_file)
|
||||
{
|
||||
zend_string *script_name;
|
||||
|
||||
Reference in New Issue
Block a user