diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index f1aebddea93..d396f3530a5 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -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) diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index d48e2782566..bc3ecb551ed 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -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); } diff --git a/ext/opcache/tests/jit/init_fcall_001.phpt b/ext/opcache/tests/jit/init_fcall_001.phpt new file mode 100644 index 00000000000..32f40795150 --- /dev/null +++ b/ext/opcache/tests/jit/init_fcall_001.phpt @@ -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-- + +--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 + diff --git a/ext/opcache/tests/jit/init_fcall_002.phpt b/ext/opcache/tests/jit/init_fcall_002.phpt new file mode 100644 index 00000000000..29569f93c7c --- /dev/null +++ b/ext/opcache/tests/jit/init_fcall_002.phpt @@ -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-- +foo(foo(test2($x))))); +} +test1(); +?> +DONE +--EXPECT-- +DONE