mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Merge branch 'PHP-8.0' into PHP-8.1
* PHP-8.0: JIT: Fixed megamorphic call detection
This commit is contained in:
commit
7e53b08cb1
4 changed files with 66 additions and 33 deletions
|
@ -551,6 +551,7 @@ struct _zend_execute_data {
|
|||
#define ZEND_CALL_OBSERVED (1 << 28) /* "fcall_begin" observer handler may set this flag */
|
||||
/* to prevent optimization in RETURN handler and */
|
||||
/* keep all local variables for "fcall_end" handler */
|
||||
#define ZEND_CALL_JIT_RESERVED (1 << 29) /* reserved for tracing JIT */
|
||||
#define ZEND_CALL_SEND_ARG_BY_REF (1u << 31)
|
||||
|
||||
#define ZEND_CALL_NESTED_FUNCTION (ZEND_CALL_FUNCTION | ZEND_CALL_NESTED)
|
||||
|
|
|
@ -449,7 +449,9 @@ static uint8_t zend_jit_trace_bad_stop_event(const zend_op *opline, int count)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic, uint32_t *megamorphic, uint32_t level, uint32_t init_level, uint32_t *call_level)
|
||||
#define ZEND_CALL_MEGAMORPHIC ZEND_CALL_JIT_RESERVED
|
||||
|
||||
static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic, uint32_t init_level)
|
||||
{
|
||||
zend_jit_trace_stop stop ZEND_ATTRIBUTE_UNUSED = ZEND_JIT_TRACE_STOP_ERROR;
|
||||
|
||||
|
@ -458,7 +460,7 @@ static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend
|
|||
zend_jit_op_array_trace_extension *jit_extension;
|
||||
|
||||
if (call->prev_execute_data) {
|
||||
idx = zend_jit_trace_record_fake_init_call_ex(call->prev_execute_data, trace_buffer, idx, is_megamorphic, megamorphic, level, init_level + 1, call_level);
|
||||
idx = zend_jit_trace_record_fake_init_call_ex(call->prev_execute_data, trace_buffer, idx, is_megamorphic, init_level + 1);
|
||||
if (idx < 0) {
|
||||
return idx;
|
||||
}
|
||||
|
@ -489,32 +491,16 @@ static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend
|
|||
&& ((ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC)
|
||||
|| func->common.scope)) {
|
||||
func = NULL;
|
||||
*megamorphic |= (1 << (level + *call_level));
|
||||
} else {
|
||||
*megamorphic &= ~(1 << (level + *call_level));
|
||||
ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_MEGAMORPHIC);
|
||||
}
|
||||
(*call_level)++;
|
||||
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_FAKE_INFO(init_level), func);
|
||||
} while (0);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic, uint32_t *megamorphic, uint32_t level)
|
||||
static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic)
|
||||
{
|
||||
uint32_t call_level = 0;
|
||||
|
||||
return zend_jit_trace_record_fake_init_call_ex(call, trace_buffer, idx, is_megamorphic, megamorphic, level, 0, &call_level);
|
||||
}
|
||||
|
||||
static int zend_jit_trace_call_level(const zend_execute_data *call)
|
||||
{
|
||||
int call_level = 0;
|
||||
|
||||
while (call->prev_execute_data) {
|
||||
call_level++;
|
||||
call = call->prev_execute_data;
|
||||
}
|
||||
return call_level;
|
||||
return zend_jit_trace_record_fake_init_call_ex(call, trace_buffer, idx, is_megamorphic, 0);
|
||||
}
|
||||
|
||||
static int zend_jit_trace_subtrace(zend_jit_trace_rec *trace_buffer, int start, int end, uint8_t event, const zend_op_array *op_array, const zend_op *opline)
|
||||
|
@ -568,7 +554,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
|||
zend_jit_trace_stop halt = 0;
|
||||
int level = 0;
|
||||
int ret_level = 0;
|
||||
int call_level;
|
||||
zend_vm_opcode_handler_t handler;
|
||||
const zend_op_array *op_array;
|
||||
zend_jit_op_array_trace_extension *jit_extension;
|
||||
|
@ -585,7 +570,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
|||
int last_loop = -1;
|
||||
int last_loop_level = -1;
|
||||
const zend_op *last_loop_opline = NULL;
|
||||
uint32_t megamorphic = 0;
|
||||
const zend_op_array *unrolled_calls[ZEND_JIT_TRACE_MAX_CALL_DEPTH + ZEND_JIT_TRACE_MAX_RET_DEPTH];
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
zend_execute_data *prev_execute_data = ex;
|
||||
|
@ -623,7 +607,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
|||
}
|
||||
|
||||
if (prev_call) {
|
||||
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, is_megamorphic, &megamorphic, ret_level + level);
|
||||
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, is_megamorphic);
|
||||
if (ret < 0) {
|
||||
TRACE_END(ZEND_JIT_TRACE_END, ZEND_JIT_TRACE_STOP_BAD_FUNC, opline);
|
||||
#ifdef HAVE_GCC_GLOBAL_REGS
|
||||
|
@ -831,8 +815,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
|||
|| opline->opcode == ZEND_DO_ICALL
|
||||
|| opline->opcode == ZEND_DO_UCALL
|
||||
|| opline->opcode == ZEND_DO_FCALL_BY_NAME) {
|
||||
call_level = zend_jit_trace_call_level(EX(call));
|
||||
if (megamorphic & (1 << (ret_level + level + call_level))) {
|
||||
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_MEGAMORPHIC) {
|
||||
stop = ZEND_JIT_TRACE_STOP_INTERPRETER;
|
||||
break;
|
||||
}
|
||||
|
@ -962,7 +945,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
|||
last_loop_opline = NULL;
|
||||
|
||||
if (prev_call) {
|
||||
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, 0, &megamorphic, ret_level + level);
|
||||
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, 0);
|
||||
if (ret < 0) {
|
||||
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
|
||||
break;
|
||||
|
@ -1045,12 +1028,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
|
|||
&& trace_buffer[1].opline == opline - 1) {
|
||||
func = NULL;
|
||||
}
|
||||
call_level = zend_jit_trace_call_level(EX(call));
|
||||
ZEND_ASSERT(ret_level + level + call_level < 32);
|
||||
if (func) {
|
||||
megamorphic &= ~(1 << (ret_level + level + call_level));
|
||||
} else {
|
||||
megamorphic |= (1 << (ret_level + level + call_level));
|
||||
if (!func) {
|
||||
ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_MEGAMORPHIC);
|
||||
}
|
||||
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, 0, func);
|
||||
}
|
||||
|
|
22
ext/opcache/tests/jit/init_fcall_001.phpt
Normal file
22
ext/opcache/tests/jit/init_fcall_001.phpt
Normal file
|
@ -0,0 +1,22 @@
|
|||
--TEST--
|
||||
JIT INIT_FCALL: 001 too deep nesting level
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.file_update_protection=0
|
||||
opcache.jit_buffer_size=1M
|
||||
--FILE--
|
||||
<?php
|
||||
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
|
||||
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
|
||||
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
|
||||
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
|
||||
)))))))))))))))))))))))))))))))));
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught ArgumentCountError: ini_set() expects exactly 2 arguments, 0 given in %sinit_fcall_001.php:5
|
||||
Stack trace:
|
||||
#0 %sinit_fcall_001.php(5): ini_set()
|
||||
#1 {main}
|
||||
thrown in %sinit_fcall_001.php on line 5
|
||||
|
31
ext/opcache/tests/jit/init_fcall_002.phpt
Normal file
31
ext/opcache/tests/jit/init_fcall_002.phpt
Normal file
|
@ -0,0 +1,31 @@
|
|||
--TEST--
|
||||
JIT INIT_FCALL: 002 incorrect megamorphic call detection
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.file_update_protection=0
|
||||
opcache.jit_buffer_size=1M
|
||||
opcache.jit=tracing
|
||||
opcache.jit_max_polymorphic_calls=0
|
||||
--FILE--
|
||||
<?php
|
||||
class C {
|
||||
function foo($x) {
|
||||
return $x;
|
||||
}
|
||||
}
|
||||
function foo($x) {
|
||||
return $x;
|
||||
}
|
||||
function test2($x) {
|
||||
return foo(foo($x));
|
||||
}
|
||||
function test1() {
|
||||
$x = new C;
|
||||
foo(foo($x->foo(foo(test2($x)))));
|
||||
}
|
||||
test1();
|
||||
?>
|
||||
DONE
|
||||
--EXPECT--
|
||||
DONE
|
Loading…
Add table
Add a link
Reference in a new issue