1
0
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:
Bob Weinand
2024-09-24 14:20:38 +02:00
committed by GitHub
parent f89eb15f72
commit 654b787ee1
11 changed files with 101 additions and 14 deletions

1
NEWS
View File

@@ -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)

View File

@@ -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.

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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)

View File

@@ -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;