Merge branch 'PHP-8.1'

* PHP-8.1:
  JIT: Fixed megamorphic call detection
This commit is contained in:
Dmitry Stogov 2021-10-20 22:31:15 +03:00
commit 01c9282d5a
4 changed files with 66 additions and 33 deletions

View file

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

View file

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

View 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

View 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