Optimize observers (#13649)

Inline the lookup whether a function is observed at all.
This strategy is also used for FRAMELESS calls. If the frameless call is observed, we instead allocate a call frame and push the arguments, to call the the function afterwards.
Doing so is still a performance benefit as opposed to executing individual INIT_FCALL+SEND_VAL ops. Thus, even if the frameless call turns out to be observed, the call overhead is slightly lower than before.
If the internal function is not observed at all, the unavoidable overhead is fetching the FLF zend_function pointer and the run-time cache needs to be inspected.

As part of this work, it turned out to be most viable to put the result operand on the ZEND_OP_DATA instead of ZEND_FRAMELESS_ICALL_3, allowing seamless interoperability with the DO_ICALL opcode.
This is a bit unusual in comparison to all other ZEND_OP_DATA usages, but seems to not pose problems overall.

There is also a small issue resolved: trampolines would always use the ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER function due to zend_observer_fcall_op_array_extension being set to -1 too late.
This commit is contained in:
Bob Weinand 2024-06-15 14:42:27 +02:00 committed by GitHub
parent 6e825dfe43
commit 6a2c5318f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 1233 additions and 709 deletions

View File

@ -1776,7 +1776,7 @@ ZEND_FUNCTION(debug_print_backtrace)
ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int options, int limit) /* {{{ */
{
zend_execute_data *call;
zend_execute_data *call, *last_call = NULL;
zend_object *object;
bool fake_frame = 0;
int lineno, frameno = 0;
@ -1821,6 +1821,7 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
if (skip_last) {
/* skip debug_backtrace() */
last_call = call;
call = call->prev_execute_data;
}
@ -1857,9 +1858,15 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
zval *arg = zend_get_zval_ptr(op_data, op_data->op1_type, &op_data->op1, call);
if (Z_TYPE_P(arg) == IS_UNDEF) goto not_frameless_call;
}
zend_function *func = ZEND_FLF_FUNC(opline);
/* Assume frameless functions are not recursive with themselves.
* This condition may be true when observers are enabled:
* Observers will put a call frame on top of the frameless opcode. */
if (last_call && last_call->func == func) {
goto not_frameless_call;
}
stack_frame = zend_new_array(8);
zend_hash_real_init_mixed(stack_frame);
zend_function *func = ZEND_FLF_FUNC(opline);
zend_string *name = func->common.function_name;
ZVAL_STRINGL(&tmp, ZSTR_VAL(name), ZSTR_LEN(name));
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1);
@ -2068,6 +2075,7 @@ skip_frame:
} else {
fake_frame = 0;
include_filename = filename;
last_call = call;
call = prev;
}
}

View File

@ -4575,7 +4575,7 @@ static uint32_t find_frameless_function_offset(uint32_t arity, void *handler)
static const zend_frameless_function_info *find_frameless_function_info(zend_ast_list *args, zend_function *fbc, uint32_t type)
{
if (ZEND_OBSERVER_ENABLED || zend_execute_internal) {
if (zend_execute_internal) {
return NULL;
}

View File

@ -1536,6 +1536,54 @@ static zend_never_inline void zend_assign_to_object_dim(zend_object *obj, zval *
}
}
static void frameless_observed_call_copy(zend_execute_data *call, uint32_t arg, zval *zv)
{
if (Z_ISUNDEF_P(zv)) {
ZVAL_NULL(ZEND_CALL_VAR_NUM(call, arg));
} else {
ZVAL_COPY_DEREF(ZEND_CALL_VAR_NUM(call, arg), zv);
}
}
ZEND_API void zend_frameless_observed_call(zend_execute_data *execute_data)
{
const zend_op *opline = EX(opline);
uint8_t num_args = ZEND_FLF_NUM_ARGS(opline->opcode);
zend_function *fbc = ZEND_FLF_FUNC(opline);
zval *result = EX_VAR(opline->result.var);
zend_execute_data *call = zend_vm_stack_push_call_frame_ex(zend_vm_calc_used_stack(num_args, fbc), ZEND_CALL_NESTED_FUNCTION, fbc, num_args, NULL);
call->prev_execute_data = execute_data;
switch (num_args) {
case 3: frameless_observed_call_copy(call, 2, zend_get_zval_ptr(opline+1, (opline+1)->op1_type, &(opline+1)->op1, execute_data)); ZEND_FALLTHROUGH;
case 2: frameless_observed_call_copy(call, 1, zend_get_zval_ptr(opline, opline->op2_type, &opline->op2, execute_data)); ZEND_FALLTHROUGH;
case 1: frameless_observed_call_copy(call, 0, zend_get_zval_ptr(opline, opline->op1_type, &opline->op1, execute_data));
}
EG(current_execute_data) = call;
zend_observer_fcall_begin_prechecked(call, ZEND_OBSERVER_DATA(fbc));
fbc->internal_function.handler(call, result);
zend_observer_fcall_end(call, result);
EG(current_execute_data) = execute_data;
if (UNEXPECTED(EG(exception) != NULL)) {
zend_rethrow_exception(execute_data);
}
zend_vm_stack_free_args(call);
uint32_t call_info = ZEND_CALL_INFO(call);
if (UNEXPECTED(call_info & ZEND_CALL_ALLOCATED)) {
zend_vm_stack_free_call_frame_ex(call_info, call);
} else {
EG(vm_stack_top) = (zval*)call;
}
}
static zend_always_inline int zend_binary_op(zval *ret, zval *op1, zval *op2 OPLINE_DC)
{
static const binary_op_type zend_binary_ops[] = {

View File

@ -430,6 +430,8 @@ ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data,
ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield);
ZEND_API void zend_frameless_observed_call(zend_execute_data *execute_data);
zval * ZEND_FASTCALL zend_handle_named_arg(
zend_execute_data **call_ptr, zend_string *arg_name,
uint32_t *arg_num_ptr, void **cache_slot);

View File

@ -115,7 +115,7 @@ typedef void (*zend_frameless_function_3)(zval *return_value, zval *op1, zval *o
extern size_t zend_flf_count;
extern size_t zend_flf_capacity;
ZEND_API extern void **zend_flf_handlers;
extern zend_function **zend_flf_functions;
ZEND_API extern zend_function **zend_flf_functions;
typedef struct {
void *handler;

View File

@ -194,6 +194,8 @@ struct _zend_executor_globals {
uint32_t jit_trace_num; /* Used by tracing JIT to reference the currently running trace */
zend_execute_data *current_observed_frame;
int ticks_count;
zend_long precision;

View File

@ -14,6 +14,7 @@
+----------------------------------------------------------------------+
| Authors: Levi Morrison <levim@php.net> |
| Sammy Kaye Powers <sammyk@php.net> |
| Bob Weinand <bobwei9@hotmail.com> |
+----------------------------------------------------------------------+
*/
@ -23,15 +24,8 @@
#include "zend_llist.h"
#include "zend_vm.h"
#define ZEND_OBSERVER_DATA(function) \
ZEND_OP_ARRAY_EXTENSION((&(function)->common), ZEND_USER_CODE((function)->type) \
? zend_observer_fcall_op_array_extension : zend_observer_fcall_internal_function_extension)
#define ZEND_OBSERVER_NOT_OBSERVED ((void *) 2)
#define ZEND_OBSERVABLE_FN(function) \
(ZEND_MAP_PTR(function->common.run_time_cache) && !(function->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
static zend_llist zend_observers_fcall_list;
static zend_llist zend_observer_function_declared_callbacks;
static zend_llist zend_observer_class_linked_callbacks;
@ -46,8 +40,6 @@ bool zend_observer_errors_observed;
bool zend_observer_function_declared_observed;
bool zend_observer_class_linked_observed;
ZEND_TLS zend_execute_data *current_observed_frame;
// Call during minit/startup ONLY
ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init)
{
@ -107,7 +99,7 @@ ZEND_API void zend_observer_post_startup(void)
ZEND_API void zend_observer_activate(void)
{
current_observed_frame = NULL;
EG(current_observed_frame) = NULL;
}
ZEND_API void zend_observer_shutdown(void)
@ -127,11 +119,12 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
zend_function *function = execute_data->func;
ZEND_ASSERT(RUN_TIME_CACHE(&function->common));
zend_observer_fcall_begin_handler *begin_handlers = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(function);
zend_observer_fcall_begin_handler *begin_handlers = ZEND_OBSERVER_DATA(function), *begin_handlers_start = begin_handlers;
zend_observer_fcall_end_handler *end_handlers = (zend_observer_fcall_end_handler *)begin_handlers + list->count, *end_handlers_start = end_handlers;
*begin_handlers = ZEND_OBSERVER_NOT_OBSERVED;
*end_handlers = ZEND_OBSERVER_NOT_OBSERVED;
bool has_handlers = false;
for (zend_llist_element *element = list->head; element; element = element->next) {
zend_observer_fcall_init init;
@ -139,9 +132,11 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
zend_observer_fcall_handlers handlers = init(execute_data);
if (handlers.begin) {
*(begin_handlers++) = handlers.begin;
has_handlers = true;
}
if (handlers.end) {
*(end_handlers++) = handlers.end;
has_handlers = true;
}
}
@ -151,6 +146,10 @@ static void zend_observer_fcall_install(zend_execute_data *execute_data)
*end_handlers = *end_handlers_start;
*end_handlers_start = tmp;
}
if (!has_handlers) {
*begin_handlers_start = ZEND_OBSERVER_NONE_OBSERVED;
}
}
/* We need to provide the ability to retrieve the handler which will move onto the position the current handler was.
@ -182,8 +181,8 @@ static bool zend_observer_remove_handler(void **first_handler, void *old_handler
ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin) {
size_t registered_observers = zend_observers_fcall_list.count;
zend_observer_fcall_begin_handler *first_handler = (void *)&ZEND_OBSERVER_DATA(function), *last_handler = first_handler + registered_observers - 1;
if (*first_handler == ZEND_OBSERVER_NOT_OBSERVED) {
zend_observer_fcall_begin_handler *first_handler = ZEND_OBSERVER_DATA(function), *last_handler = first_handler + registered_observers - 1;
if (*first_handler == ZEND_OBSERVER_NOT_OBSERVED || *first_handler == ZEND_OBSERVER_NONE_OBSERVED) {
*first_handler = begin;
} else {
for (zend_observer_fcall_begin_handler *cur_handler = first_handler + 1; cur_handler <= last_handler; ++cur_handler) {
@ -198,24 +197,47 @@ ZEND_API void zend_observer_add_begin_handler(zend_function *function, zend_obse
}
ZEND_API bool zend_observer_remove_begin_handler(zend_function *function, zend_observer_fcall_begin_handler begin, zend_observer_fcall_begin_handler *next) {
return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(function), begin, (void **)next);
void **begin_handlers = (void **)ZEND_OBSERVER_DATA(function);
if (zend_observer_remove_handler(begin_handlers, begin, (void**)next)) {
// Ensure invariant: ZEND_OBSERVER_NONE_OBSERVED in begin_handlers if both are not observed
if (*begin_handlers == ZEND_OBSERVER_NOT_OBSERVED) {
size_t registered_observers = zend_observers_fcall_list.count;
if (begin_handlers[registered_observers] /* first end handler */ == ZEND_OBSERVER_NOT_OBSERVED) {
*begin_handlers = ZEND_OBSERVER_NONE_OBSERVED;
}
}
return true;
}
return false;
}
ZEND_API void zend_observer_add_end_handler(zend_function *function, zend_observer_fcall_end_handler end) {
size_t registered_observers = zend_observers_fcall_list.count;
zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(function) + registered_observers;
void **begin_handler = (void **)ZEND_OBSERVER_DATA(function);
zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)begin_handler + registered_observers;
// to allow to preserve the invariant that end handlers are in reverse order of begin handlers, push the new end handler in front
if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
// there's no space for new handlers, then it's forbidden to call this function
ZEND_ASSERT(end_handler[registered_observers - 1] == NULL);
memmove(end_handler + 1, end_handler, sizeof(end_handler) * (registered_observers - 1));
} else if (*begin_handler == ZEND_OBSERVER_NONE_OBSERVED) {
*begin_handler = ZEND_OBSERVER_NOT_OBSERVED;
}
*end_handler = end;
}
ZEND_API bool zend_observer_remove_end_handler(zend_function *function, zend_observer_fcall_end_handler end, zend_observer_fcall_end_handler *next) {
size_t registered_observers = zend_observers_fcall_list.count;
return zend_observer_remove_handler((void **)&ZEND_OBSERVER_DATA(function) + registered_observers, end, (void **)next);
void **begin_handlers = (void **)ZEND_OBSERVER_DATA(function);
void **end_handlers = begin_handlers + registered_observers;
if (zend_observer_remove_handler(end_handlers, end, (void**)next)) {
// Ensure invariant: ZEND_OBSERVER_NONE_OBSERVED in begin_handlers if both are not observed
if (*begin_handlers == ZEND_OBSERVER_NOT_OBSERVED && *end_handlers == ZEND_OBSERVER_NOT_OBSERVED) {
*begin_handlers = ZEND_OBSERVER_NONE_OBSERVED;
}
return true;
}
return false;
}
static inline zend_execute_data **prev_observed_frame(zend_execute_data *execute_data) {
@ -224,33 +246,33 @@ static inline zend_execute_data **prev_observed_frame(zend_execute_data *execute
return (zend_execute_data **)&Z_PTR_P(EX_VAR_NUM((ZEND_USER_CODE(func->type) ? func->op_array.last_var : ZEND_CALL_NUM_ARGS(execute_data)) + func->common.T - 1));
}
static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data)
{
static void ZEND_FASTCALL _zend_observe_fcall_begin(zend_execute_data *execute_data) {
if (!ZEND_OBSERVER_ENABLED) {
return;
}
zend_function *function = execute_data->func;
zend_observer_fcall_begin_specialized(execute_data, true);
}
if (!ZEND_OBSERVABLE_FN(function)) {
return;
}
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin_prechecked(zend_execute_data *execute_data, zend_observer_fcall_begin_handler *handler)
{
zend_observer_fcall_begin_handler *possible_handlers_end = handler + zend_observers_fcall_list.count;
zend_observer_fcall_begin_handler *handler = (zend_observer_fcall_begin_handler *)&ZEND_OBSERVER_DATA(function);
if (!*handler) {
zend_observer_fcall_install(execute_data);
if (zend_observer_handler_is_unobserved(handler)) {
return;
}
}
zend_observer_fcall_begin_handler *possible_handlers_end = handler + zend_observers_fcall_list.count;
zend_observer_fcall_end_handler *end_handler = (zend_observer_fcall_end_handler *)possible_handlers_end;
if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) {
*prev_observed_frame(execute_data) = current_observed_frame;
current_observed_frame = execute_data;
}
*prev_observed_frame(execute_data) = EG(current_observed_frame);
EG(current_observed_frame) = execute_data;
if (*handler == ZEND_OBSERVER_NOT_OBSERVED) {
return;
if (*handler == ZEND_OBSERVER_NOT_OBSERVED) { // this function must not be called if ZEND_OBSERVER_NONE_OBSERVED, hence sufficient to check
return;
}
}
do {
@ -265,17 +287,17 @@ ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *ex
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute_data)
{
ZEND_ASSUME(execute_data->func);
if (!(execute_data->func->common.fn_flags & ZEND_ACC_GENERATOR)) {
ZEND_ASSUME(EX(func));
if (!(EX(func)->common.fn_flags & ZEND_ACC_GENERATOR)) {
_zend_observe_fcall_begin(execute_data);
}
}
static inline void call_end_observers(zend_execute_data *execute_data, zval *return_value) {
zend_function *func = execute_data->func;
zend_function *func = EX(func);
ZEND_ASSERT(func);
zend_observer_fcall_end_handler *handler = (zend_observer_fcall_end_handler *)&ZEND_OBSERVER_DATA(func) + zend_observers_fcall_list.count;
zend_observer_fcall_end_handler *handler = (zend_observer_fcall_end_handler *)ZEND_OBSERVER_DATA(func) + zend_observers_fcall_list.count;
// TODO: Fix exceptions from generators
// ZEND_ASSERT(fcall_data);
if (!*handler || *handler == ZEND_OBSERVER_NOT_OBSERVED) {
@ -288,19 +310,16 @@ static inline void call_end_observers(zend_execute_data *execute_data, zval *ret
} while (++handler != possible_handlers_end && *handler != NULL);
}
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(zend_execute_data *execute_data, zval *return_value)
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end_prechecked(zend_execute_data *execute_data, zval *return_value)
{
if (execute_data != current_observed_frame) {
return;
}
call_end_observers(execute_data, return_value);
current_observed_frame = *prev_observed_frame(execute_data);
EG(current_observed_frame) = *prev_observed_frame(execute_data);
}
ZEND_API void zend_observer_fcall_end_all(void)
{
zend_execute_data *execute_data = current_observed_frame, *original_execute_data = EG(current_execute_data);
current_observed_frame = NULL;
zend_execute_data *execute_data = EG(current_observed_frame), *original_execute_data = EG(current_execute_data);
EG(current_observed_frame) = NULL;
while (execute_data) {
EG(current_execute_data) = execute_data;
call_end_observers(execute_data, NULL);
@ -401,8 +420,8 @@ ZEND_API void ZEND_FASTCALL zend_observer_fiber_switch_notify(zend_fiber_context
callback(from, to);
}
from->top_observed_frame = current_observed_frame;
current_observed_frame = to->top_observed_frame;
from->top_observed_frame = EG(current_observed_frame);
EG(current_observed_frame) = to->top_observed_frame;
}
ZEND_API void ZEND_FASTCALL zend_observer_fiber_destroy_notify(zend_fiber_context *destroying)

View File

@ -14,6 +14,7 @@
+----------------------------------------------------------------------+
| Authors: Levi Morrison <levim@php.net> |
| Sammy Kaye Powers <sammyk@php.net> |
| Bob Weinand <bobwei9@hotmail.com> |
+----------------------------------------------------------------------+
*/
@ -32,6 +33,16 @@ extern ZEND_API bool zend_observer_errors_observed;
extern ZEND_API bool zend_observer_function_declared_observed;
extern ZEND_API bool zend_observer_class_linked_observed;
#define ZEND_OBSERVER_HANDLE(function) (ZEND_USER_CODE((function)->type) \
? zend_observer_fcall_op_array_extension : zend_observer_fcall_internal_function_extension)
#define ZEND_OBSERVER_DATA(function) \
((zend_observer_fcall_begin_handler *)&ZEND_OP_ARRAY_EXTENSION((&(function)->common), ZEND_OBSERVER_HANDLE(function)))
/* Neither begin nor end handler present. Needs to be set in the slot of the begin handler.
* Optimization reducing runtime checks. */
#define ZEND_OBSERVER_NONE_OBSERVED ((void *) 3)
/* Omit zend_observer_fcall_internal_function_extension check, they are set at the same time. */
#define ZEND_OBSERVER_ENABLED (zend_observer_fcall_op_array_extension != -1)
@ -73,15 +84,48 @@ ZEND_API void zend_observer_post_startup(void); // Called by engine after MINITs
ZEND_API void zend_observer_activate(void);
ZEND_API void zend_observer_shutdown(void);
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(
zend_execute_data *execute_data);
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin(zend_execute_data *execute_data);
/* prechecked: the call is actually observed. */
ZEND_API void ZEND_FASTCALL zend_observer_fcall_begin_prechecked(zend_execute_data *execute_data, zend_observer_fcall_begin_handler *observer_data);
ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(
zend_execute_data *execute_data);
static zend_always_inline bool zend_observer_handler_is_unobserved(zend_observer_fcall_begin_handler *handler) {
return *handler == ZEND_OBSERVER_NONE_OBSERVED;
}
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(
zend_execute_data *execute_data,
zval *return_value);
/* Initial check for observers has not happened yet or no observers are installed. */
static zend_always_inline bool zend_observer_fcall_has_no_observers(zend_execute_data *execute_data, bool allow_generator, zend_observer_fcall_begin_handler **handler) {
zend_function *function = EX(func);
void *ZEND_MAP_PTR(runtime_cache) = ZEND_MAP_PTR(function->common.run_time_cache);
if (function->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | (allow_generator ? 0 : ZEND_ACC_GENERATOR))) {
return true;
}
if (!ZEND_MAP_PTR(runtime_cache)) {
return true;
}
*handler = (zend_observer_fcall_begin_handler *)ZEND_MAP_PTR_GET(runtime_cache) + ZEND_OBSERVER_HANDLE(function);
return zend_observer_handler_is_unobserved(*handler);
}
/* zend_observer_fcall_begin(), but with generator check inlined and optimized away. */
static zend_always_inline void zend_observer_fcall_begin_specialized(zend_execute_data *execute_data, bool allow_generator) {
zend_observer_fcall_begin_handler *handler;
if (!zend_observer_fcall_has_no_observers(execute_data, allow_generator, &handler)) {
zend_observer_fcall_begin_prechecked(execute_data, handler);
}
}
ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *execute_data);
/* prechecked: the call is actually observed. */
ZEND_API void ZEND_FASTCALL zend_observer_fcall_end_prechecked(zend_execute_data *execute_data, zval *return_value);
static zend_always_inline void zend_observer_fcall_end(zend_execute_data *execute_data, zval *return_value) {
if (execute_data == EG(current_observed_frame)) {
zend_observer_fcall_end_prechecked(execute_data, return_value);
}
}
ZEND_API void zend_observer_fcall_end_all(void);

View File

@ -9599,38 +9599,59 @@ ZEND_VM_C_LABEL(try_again):
}
}
ZEND_VM_HANDLER(204, ZEND_FRAMELESS_ICALL_0, UNUSED, UNUSED)
ZEND_VM_HANDLER(204, ZEND_FRAMELESS_ICALL_0, UNUSED, UNUSED, SPEC(OBSERVER))
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
function(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
#if !ZEND_VM_SPEC || ZEND_OBSERVER_ENABLED
if (ZEND_OBSERVER_ENABLED && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline);
function(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(205, ZEND_FRAMELESS_ICALL_1, ANY, UNUSED)
ZEND_VM_HANDLER(205, ZEND_FRAMELESS_ICALL_1, ANY, UNUSED, SPEC(OBSERVER))
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_1 function = (zend_frameless_function_1)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
if (EG(exception)) {
FREE_OP1();
HANDLE_EXCEPTION();
}
function(EX_VAR(opline->result.var), arg1);
#if !ZEND_VM_SPEC || ZEND_OBSERVER_ENABLED
if (ZEND_OBSERVER_ENABLED && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_1 function = (zend_frameless_function_1)ZEND_FLF_HANDLER(opline);
function(result, arg1);
}
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(206, ZEND_FRAMELESS_ICALL_2, ANY, ANY)
ZEND_VM_HANDLER(206, ZEND_FRAMELESS_ICALL_2, ANY, ANY, SPEC(OBSERVER))
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_2 function = (zend_frameless_function_2)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
zval *arg2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
if (EG(exception)) {
@ -9638,7 +9659,17 @@ ZEND_VM_HANDLER(206, ZEND_FRAMELESS_ICALL_2, ANY, ANY)
FREE_OP2();
HANDLE_EXCEPTION();
}
function(EX_VAR(opline->result.var), arg1, arg2);
#if !ZEND_VM_SPEC || ZEND_OBSERVER_ENABLED
if (ZEND_OBSERVER_ENABLED && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_2 function = (zend_frameless_function_2)ZEND_FLF_HANDLER(opline);
function(result, arg1, arg2);
}
FREE_OP1();
/* Set OP1 to UNDEF in case FREE_OP2() throws. */
if (OP1_TYPE & (IS_VAR|IS_TMP_VAR)) {
@ -9648,12 +9679,13 @@ ZEND_VM_HANDLER(206, ZEND_FRAMELESS_ICALL_2, ANY, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HANDLER(207, ZEND_FRAMELESS_ICALL_3, ANY, ANY)
ZEND_VM_HANDLER(207, ZEND_FRAMELESS_ICALL_3, ANY, ANY, SPEC(OBSERVER))
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_3 function = (zend_frameless_function_3)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
zval *arg2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
zval *arg3 = GET_OP_DATA_ZVAL_PTR_DEREF(BP_VAR_R);
@ -9663,7 +9695,17 @@ ZEND_VM_HANDLER(207, ZEND_FRAMELESS_ICALL_3, ANY, ANY)
FREE_OP_DATA();
HANDLE_EXCEPTION();
}
function(EX_VAR(opline->result.var), arg1, arg2, arg3);
#if !ZEND_VM_SPEC || ZEND_OBSERVER_ENABLED
if (ZEND_OBSERVER_ENABLED && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_3 function = (zend_frameless_function_3)ZEND_FLF_HANDLER(opline);
function(result, arg1, arg2, arg3);
}
FREE_OP1();
/* Set to UNDEF in case FREE_OP2() throws. */
if (OP1_TYPE & (IS_VAR|IS_TMP_VAR)) {

436
Zend/zend_vm_execute.h generated
View File

@ -1408,7 +1408,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_OBS
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
zend_observer_fcall_begin(call);
zend_observer_fcall_begin_specialized(call, false);
fbc->internal_function.handler(call, ret);
#if ZEND_DEBUG
@ -1519,7 +1519,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_UCALL_SPEC_OBS
i_init_func_execute_data(&fbc->op_array, ret, 0 EXECUTE_DATA_CC);
LOAD_OPLINE_EX();
SAVE_OPLINE();
zend_observer_fcall_begin(execute_data);
zend_observer_fcall_begin_specialized(execute_data, false);
ZEND_VM_ENTER_EX();
}
@ -1735,7 +1735,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_
i_init_func_execute_data(&fbc->op_array, ret, 0 EXECUTE_DATA_CC);
LOAD_OPLINE_EX();
SAVE_OPLINE();
zend_observer_fcall_begin(execute_data);
zend_observer_fcall_begin_specialized(execute_data, false);
ZEND_VM_ENTER_EX();
} else {
@ -1767,7 +1767,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
zend_observer_fcall_begin(call);
zend_observer_fcall_begin_specialized(call, false);
fbc->internal_function.handler(call, ret);
#if ZEND_DEBUG
@ -2053,11 +2053,11 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS
if (EXPECTED(zend_execute_ex == execute_ex)) {
LOAD_OPLINE_EX();
SAVE_OPLINE();
zend_observer_fcall_begin(execute_data);
zend_observer_fcall_begin_specialized(execute_data, false);
ZEND_VM_ENTER_EX();
} else {
SAVE_OPLINE_EX();
zend_observer_fcall_begin(execute_data);
zend_observer_fcall_begin_specialized(execute_data, false);
execute_data = EX(prev_execute_data);
LOAD_OPLINE();
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
@ -2092,7 +2092,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
zend_observer_fcall_begin(call);
zend_observer_fcall_begin_specialized(call, false);
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
fbc->internal_function.handler(call, ret);
@ -3610,11 +3610,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
if (EXPECTED(zend_execute_ex == execute_ex)) {
LOAD_OPLINE_EX();
SAVE_OPLINE();
zend_observer_fcall_begin(execute_data);
zend_observer_fcall_begin_specialized(execute_data, false);
ZEND_VM_ENTER_EX();
} else {
SAVE_OPLINE_EX();
zend_observer_fcall_begin(execute_data);
zend_observer_fcall_begin_specialized(execute_data, false);
execute_data = EX(prev_execute_data);
if (execute_data) {
LOAD_OPLINE();
@ -3638,7 +3638,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_
}
ZVAL_NULL(ret);
zend_observer_fcall_begin(call);
zend_observer_fcall_begin_specialized(call, false);
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
fbc->internal_function.handler(call, ret);
@ -3697,8 +3697,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_2_SPEC_HANDLER
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_2 function = (zend_frameless_function_2)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = get_zval_ptr_deref(opline->op1_type, opline->op1, BP_VAR_R);
zval *arg2 = get_zval_ptr_deref(opline->op2_type, opline->op2, BP_VAR_R);
if (EG(exception)) {
@ -3706,7 +3707,51 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_2_SPEC_HANDLER
FREE_OP(opline->op2_type, opline->op2.var);
HANDLE_EXCEPTION();
}
function(EX_VAR(opline->result.var), arg1, arg2);
#if 0 || 0
if (0 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_2 function = (zend_frameless_function_2)ZEND_FLF_HANDLER(opline);
function(result, arg1, arg2);
}
FREE_OP(opline->op1_type, opline->op1.var);
/* Set OP1 to UNDEF in case FREE_OP(opline->op2_type, opline->op2.var) throws. */
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
ZVAL_UNDEF(EX_VAR(opline->op1.var));
}
FREE_OP(opline->op2_type, opline->op2.var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = get_zval_ptr_deref(opline->op1_type, opline->op1, BP_VAR_R);
zval *arg2 = get_zval_ptr_deref(opline->op2_type, opline->op2, BP_VAR_R);
if (EG(exception)) {
FREE_OP(opline->op1_type, opline->op1.var);
FREE_OP(opline->op2_type, opline->op2.var);
HANDLE_EXCEPTION();
}
#if 0 || 1
if (1 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_2 function = (zend_frameless_function_2)ZEND_FLF_HANDLER(opline);
function(result, arg1, arg2);
}
FREE_OP(opline->op1_type, opline->op1.var);
/* Set OP1 to UNDEF in case FREE_OP(opline->op2_type, opline->op2.var) throws. */
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
@ -3720,8 +3765,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_3_SPEC_HANDLER
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_3 function = (zend_frameless_function_3)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = get_zval_ptr_deref(opline->op1_type, opline->op1, BP_VAR_R);
zval *arg2 = get_zval_ptr_deref(opline->op2_type, opline->op2, BP_VAR_R);
zval *arg3 = get_op_data_zval_ptr_deref_r((opline+1)->op1_type, (opline+1)->op1);
@ -3731,7 +3777,57 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_3_SPEC_HANDLER
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
HANDLE_EXCEPTION();
}
function(EX_VAR(opline->result.var), arg1, arg2, arg3);
#if 0 || 0
if (0 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_3 function = (zend_frameless_function_3)ZEND_FLF_HANDLER(opline);
function(result, arg1, arg2, arg3);
}
FREE_OP(opline->op1_type, opline->op1.var);
/* Set to UNDEF in case FREE_OP(opline->op2_type, opline->op2.var) throws. */
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
ZVAL_UNDEF(EX_VAR(opline->op1.var));
}
FREE_OP(opline->op2_type, opline->op2.var);
if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
ZVAL_UNDEF(EX_VAR(opline->op2.var));
}
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = get_zval_ptr_deref(opline->op1_type, opline->op1, BP_VAR_R);
zval *arg2 = get_zval_ptr_deref(opline->op2_type, opline->op2, BP_VAR_R);
zval *arg3 = get_op_data_zval_ptr_deref_r((opline+1)->op1_type, (opline+1)->op1);
if (EG(exception)) {
FREE_OP(opline->op1_type, opline->op1.var);
FREE_OP(opline->op2_type, opline->op2.var);
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
HANDLE_EXCEPTION();
}
#if 0 || 1
if (1 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_3 function = (zend_frameless_function_3)ZEND_FLF_HANDLER(opline);
function(result, arg1, arg2, arg3);
}
FREE_OP(opline->op1_type, opline->op1.var);
/* Set to UNDEF in case FREE_OP(opline->op2_type, opline->op2.var) throws. */
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
@ -4138,14 +4234,50 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_1 function = (zend_frameless_function_1)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = get_zval_ptr_deref(opline->op1_type, opline->op1, BP_VAR_R);
if (EG(exception)) {
FREE_OP(opline->op1_type, opline->op1.var);
HANDLE_EXCEPTION();
}
function(EX_VAR(opline->result.var), arg1);
#if 0 || 0
if (0 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_1 function = (zend_frameless_function_1)ZEND_FLF_HANDLER(opline);
function(result, arg1);
}
FREE_OP(opline->op1_type, opline->op1.var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
zval *arg1 = get_zval_ptr_deref(opline->op1_type, opline->op1, BP_VAR_R);
if (EG(exception)) {
FREE_OP(opline->op1_type, opline->op1.var);
HANDLE_EXCEPTION();
}
#if 0 || 1
if (1 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_1 function = (zend_frameless_function_1)ZEND_FLF_HANDLER(opline);
function(result, arg1);
}
FREE_OP(opline->op1_type, opline->op1.var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@ -5217,7 +5349,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_
call->prev_execute_data = execute_data;
i_init_code_execute_data(call, new_op_array, return_value);
zend_observer_fcall_begin(call);
zend_observer_fcall_begin_specialized(call, false);
if (EXPECTED(zend_execute_ex == execute_ex)) {
FREE_OP(opline->op1_type, opline->op1.var);
ZEND_VM_ENTER();
@ -37351,9 +37483,39 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_0_SPEC_UNUSED_
{
USE_OPLINE
SAVE_OPLINE();
zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline);
ZVAL_NULL(EX_VAR(opline->result.var));
function(EX_VAR(opline->result.var));
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
#if 0 || 0
if (0 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline);
function(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zval *result = EX_VAR(opline->result.var);
ZVAL_NULL(result);
#if 0 || 1
if (1 && UNEXPECTED(zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(ZEND_FLF_FUNC(opline))) == false)) {
zend_frameless_observed_call(execute_data);
} else
#endif
{
zend_frameless_function_0 function = (zend_frameless_function_0)ZEND_FLF_HANDLER(opline);
function(EX_VAR(opline->result.var));
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@ -56188,9 +56350,13 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_BIND_INIT_STATIC_OR_JMP_SPEC_CV_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_0_SPEC_UNUSED_UNUSED_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_2_SPEC_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_3_SPEC_LABEL,
(void*)&&ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER_LABEL,
(void*)&&ZEND_JMP_FRAMELESS_SPEC_CONST_LABEL,
(void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL,
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
@ -57549,11 +57715,21 @@ zend_leave_helper_SPEC_LABEL:
ZEND_FRAMELESS_ICALL_2_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_2_SPEC)
HYBRID_BREAK();
HYBRID_CASE(ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER):
VM_TRACE(ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER)
ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER)
HYBRID_BREAK();
HYBRID_CASE(ZEND_FRAMELESS_ICALL_3_SPEC):
VM_TRACE(ZEND_FRAMELESS_ICALL_3_SPEC)
ZEND_FRAMELESS_ICALL_3_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_3_SPEC)
HYBRID_BREAK();
HYBRID_CASE(ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER):
VM_TRACE(ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER)
ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER)
HYBRID_BREAK();
HYBRID_CASE(ZEND_JMP_FORWARD_SPEC):
VM_TRACE(ZEND_JMP_FORWARD_SPEC)
ZEND_JMP_FORWARD_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -57609,6 +57785,11 @@ zend_leave_helper_SPEC_LABEL:
ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED)
HYBRID_BREAK();
HYBRID_CASE(ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER):
VM_TRACE(ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER)
ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER)
HYBRID_BREAK();
HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_CV):
VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_CV)
ZEND_INIT_DYNAMIC_CALL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -61236,6 +61417,11 @@ zend_leave_helper_SPEC_LABEL:
ZEND_FRAMELESS_ICALL_0_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_0_SPEC_UNUSED_UNUSED)
HYBRID_BREAK();
HYBRID_CASE(ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER):
VM_TRACE(ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER)
ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER)
HYBRID_BREAK();
HYBRID_CASE(ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV):
VM_TRACE(ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV)
ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -65351,9 +65537,13 @@ void zend_vm_init(void)
ZEND_CALLABLE_CONVERT_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_BIND_INIT_STATIC_OR_JMP_SPEC_CV_HANDLER,
ZEND_FRAMELESS_ICALL_0_SPEC_UNUSED_UNUSED_HANDLER,
ZEND_FRAMELESS_ICALL_0_SPEC_OBSERVER_HANDLER,
ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER,
ZEND_FRAMELESS_ICALL_1_SPEC_OBSERVER_HANDLER,
ZEND_FRAMELESS_ICALL_2_SPEC_HANDLER,
ZEND_FRAMELESS_ICALL_2_SPEC_OBSERVER_HANDLER,
ZEND_FRAMELESS_ICALL_3_SPEC_HANDLER,
ZEND_FRAMELESS_ICALL_3_SPEC_OBSERVER_HANDLER,
ZEND_JMP_FRAMELESS_SPEC_CONST_HANDLER,
ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER,
ZEND_RECV_NOTYPE_SPEC_HANDLER,
@ -66312,7 +66502,7 @@ void zend_vm_init(void)
1255,
1256 | SPEC_RULE_OP1,
1261 | SPEC_RULE_OP1,
3482,
3486,
1266 | SPEC_RULE_OP1,
1271 | SPEC_RULE_OP1,
1276 | SPEC_RULE_OP2,
@ -66471,58 +66661,58 @@ void zend_vm_init(void)
2565,
2566,
2567,
2568,
2569,
2570,
2571,
2572,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
3482,
2568 | SPEC_RULE_OBSERVER,
2570 | SPEC_RULE_OBSERVER,
2572 | SPEC_RULE_OBSERVER,
2574 | SPEC_RULE_OBSERVER,
2576,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
3486,
};
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
zend_opcode_handler_funcs = labels;
@ -66695,7 +66885,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2581 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2585 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -66703,7 +66893,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2606 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2610 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -66711,7 +66901,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2631 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2635 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -66722,17 +66912,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2656 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2660 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2681 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2685 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2706 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2710 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
@ -66743,17 +66933,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2731 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2735 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2756 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2760 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2781 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2785 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_IDENTICAL:
@ -66764,14 +66954,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2806 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2810 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2881 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2885 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
spec = 3106 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3110 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_IDENTICAL:
@ -66782,14 +66972,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2956 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2960 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3031 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 3035 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
spec = 3111 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 3115 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_EQUAL:
@ -66800,12 +66990,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2806 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2810 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2881 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2885 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_EQUAL:
@ -66816,12 +67006,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2956 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2960 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3031 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 3035 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_SMALLER:
@ -66829,12 +67019,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3116 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3120 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3191 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3195 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
@ -66842,79 +67032,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3266 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3270 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3341 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3345 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_LONG) {
spec = 3428 | SPEC_RULE_OP1;
spec = 3432 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_DOUBLE) {
spec = 3433 | SPEC_RULE_OP1;
spec = 3437 | SPEC_RULE_OP1;
} else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
spec = 3438 | SPEC_RULE_OP1;
spec = 3442 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3416 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3418 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3420 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3422 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3424 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3426 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3424;
spec = 3428;
} else if (op1_info == MAY_BE_LONG) {
spec = 3425;
spec = 3429;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3426;
spec = 3430;
} else if (op1_info == MAY_BE_LONG) {
spec = 3427;
spec = 3431;
}
break;
case ZEND_JMP:
if (OP_JMP_ADDR(op, op->op1) > op) {
spec = 2580;
spec = 2584;
}
break;
case ZEND_INIT_FCALL:
if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) {
spec = 2573;
spec = 2577;
}
break;
case ZEND_RECV:
if (op->op2.num == MAY_BE_ANY) {
spec = 2574;
spec = 2578;
}
break;
case ZEND_SEND_VAL:
if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 3478;
spec = 3482;
}
break;
case ZEND_SEND_VAR_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 3473 | SPEC_RULE_OP1;
spec = 3477 | SPEC_RULE_OP1;
}
break;
case ZEND_FE_FETCH_R:
if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) {
spec = 3480 | SPEC_RULE_RETVAL;
spec = 3484 | SPEC_RULE_RETVAL;
}
break;
case ZEND_FETCH_DIM_R:
@ -66922,22 +67112,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3443 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3447 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAL_EX:
if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 3479;
spec = 3483;
}
break;
case ZEND_SEND_VAR:
if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 3468 | SPEC_RULE_OP1;
spec = 3472 | SPEC_RULE_OP1;
}
break;
case ZEND_COUNT:
if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) {
spec = 2575 | SPEC_RULE_OP1;
spec = 2579 | SPEC_RULE_OP1;
}
break;
case ZEND_BW_OR:

View File

@ -730,8 +730,8 @@ function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null)
"/FREE_OP2\(\)/" => $op2_free_op[$op2],
"/FREE_OP1_IF_VAR\(\)/" => $op1_free_op_if_var[$op1],
"/FREE_OP2_IF_VAR\(\)/" => $op2_free_op_if_var[$op2],
"/\!ZEND_VM_SPEC/m" => ($op1!="ANY"||$op2!="ANY")?"0":"1",
"/ZEND_VM_SPEC/m" => ($op1!="ANY"||$op2!="ANY")?"1":"0",
"/\!ZEND_VM_SPEC/m" => ($op1!="ANY"||$op2!="ANY"||$extra_spec)?"0":"1",
"/ZEND_VM_SPEC/m" => ($op1!="ANY"||$op2!="ANY"||$extra_spec)?"1":"0",
"/ZEND_VM_C_LABEL\(\s*([A-Za-z_]*)\s*\)/m" => "\\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec)):""),
"/ZEND_VM_C_GOTO\(\s*([A-Za-z_]*)\s*\)/m" => "goto \\1".(($spec && $kind != ZEND_VM_KIND_CALL)?("_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec)):""),
"/^#(\s*)if\s+1\s*\\|\\|.*[^\\\\]$/m" => "#\\1if 1",
@ -776,7 +776,7 @@ function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null)
"/ZEND_OBSERVER_FREE_RETVAL\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "if (return_value == &observer_retval) { zval_ptr_dtor_nogc(&observer_retval); }" : "",
"/ZEND_OBSERVER_SAVE_OPLINE\(\)/" => isset($extra_spec['OBSERVER']) && $extra_spec['OBSERVER'] == 1 ? "SAVE_OPLINE()" : "",
"/ZEND_OBSERVER_FCALL_BEGIN\(\s*(.*)\s*\)/" => isset($extra_spec['OBSERVER']) ?
($extra_spec['OBSERVER'] == 0 ? "" : "zend_observer_fcall_begin(\\1)")
($extra_spec['OBSERVER'] == 0 ? "" : "zend_observer_fcall_begin_specialized(\\1, false)")
: "",
"/ZEND_OBSERVER_FCALL_END\(\s*([^,]*)\s*,\s*(.*)\s*\)/" => isset($extra_spec['OBSERVER']) ?
($extra_spec['OBSERVER'] == 0 ? "" : "zend_observer_fcall_end(\\1, \\2)")

990
Zend/zend_vm_handlers.h generated

File diff suppressed because it is too large Load Diff

View File

@ -4513,6 +4513,102 @@ static int zend_jit_update_regs(zend_jit_ctx *jit, uint32_t var, zend_jit_addr s
return 1;
}
struct jit_observer_fcall_is_unobserved_data {
ir_ref if_unobserved;
ir_ref ir_end_inputs;
};
static struct jit_observer_fcall_is_unobserved_data jit_observer_fcall_is_unobserved_start(zend_jit_ctx *jit, const zend_function *func, ir_ref *observer_handler, ir_ref rx, ir_ref func_ref) {
ir_ref run_time_cache;
struct jit_observer_fcall_is_unobserved_data data = { .ir_end_inputs = IR_UNUSED };
if (func) {
ZEND_ASSERT((func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0);
} else {
// JIT: if (function->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) {
ZEND_ASSERT(rx != IR_UNUSED);
ir_ref if_trampoline_or_generator = ir_IF(ir_AND_U32(
ir_LOAD_U32(ir_ADD_OFFSET(func_ref, offsetof(zend_function, common.fn_flags))),
ir_CONST_U32(ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)));
ir_IF_TRUE(if_trampoline_or_generator);
ir_END_list(data.ir_end_inputs);
ir_IF_FALSE(if_trampoline_or_generator);
}
if (func && (func->common.fn_flags & ZEND_ACC_CLOSURE) == 0 && ZEND_MAP_PTR_IS_OFFSET(func->common.run_time_cache)) {
// JIT: ZEND_MAP_PTR_GET_IMM(func->common.runtime_cache)
run_time_cache = ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_CG(map_ptr_base)), (uintptr_t)ZEND_MAP_PTR(func->common.run_time_cache)));
} else {
ZEND_ASSERT(rx != IR_UNUSED);
// Closures may be duplicated and have a different runtime cache. Use the regular run_time_cache access pattern for these
if (func && ZEND_USER_CODE(func->type)) { // not a closure and definitely not an internal function
run_time_cache = ir_LOAD_A(jit_CALL(rx, run_time_cache));
} else {
// JIT: ZEND_MAP_PTR_GET(func->common.runtime_cache)
run_time_cache = ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_CALL(rx, func)), offsetof(zend_op_array, run_time_cache__ptr)));
ir_ref if_odd = ir_IF(ir_AND_A(run_time_cache, ir_CONST_ADDR(1)));
ir_IF_TRUE(if_odd);
ir_ref run_time_cache2 = ir_LOAD_A(ir_ADD_A(run_time_cache, ir_LOAD_A(jit_CG(map_ptr_base))));
ir_ref if_odd_end = ir_END();
ir_IF_FALSE(if_odd);
// JIT: if (func->common.runtime_cache != NULL) {
ir_ref if_rt_cache = ir_IF(ir_EQ(run_time_cache, IR_NULL));
ir_IF_TRUE(if_rt_cache);
ir_END_list(data.ir_end_inputs);
ir_IF_FALSE(if_rt_cache);
ir_MERGE_WITH(if_odd_end);
run_time_cache = ir_PHI_2(IR_ADDR, run_time_cache, run_time_cache2);
}
}
// JIT: observer_handler = runtime_cache + ZEND_OBSERVER_HANDLE(function)
if (func) {
*observer_handler = ir_ADD_OFFSET(run_time_cache, ZEND_OBSERVER_HANDLE(func) * sizeof(void *));
} else {
// JIT: (func->type == ZEND_INTERNAL_FUNCTION ? zend_observer_fcall_internal_function_extension : zend_observer_fcall_op_array_extension) * sizeof(void *)
ir_ref tmp = ir_LOAD_U8(ir_ADD_OFFSET(func_ref, offsetof(zend_function, type)));
ir_ref if_internal_func = ir_IF(ir_AND_U8(tmp, ir_CONST_U8(ZEND_INTERNAL_FUNCTION)));
ir_IF_TRUE(if_internal_func);
ir_ref observer_handler_internal = ir_ADD_OFFSET(run_time_cache, zend_observer_fcall_internal_function_extension * sizeof(void *));
ir_ref if_internal_func_end = ir_END();
ir_IF_FALSE(if_internal_func);
ir_ref observer_handler_user = ir_ADD_OFFSET(run_time_cache, zend_observer_fcall_op_array_extension * sizeof(void *));
ir_MERGE_WITH(if_internal_func_end);
*observer_handler = ir_PHI_2(IR_ADDR, observer_handler_internal, observer_handler_user);
}
// JIT: if (*observer_handler == ZEND_OBSERVER_NONE_OBSERVED) {
data.if_unobserved = ir_IF(ir_EQ(ir_LOAD_A(*observer_handler), ir_CONST_ADDR(ZEND_OBSERVER_NONE_OBSERVED)));
ir_IF_FALSE(data.if_unobserved);
return data;
}
/* For frameless the true branch of if_unobserved is used and this function not called. */
static void jit_observer_fcall_is_unobserved_end(zend_jit_ctx *jit, struct jit_observer_fcall_is_unobserved_data *data) {
ir_END_list(data->ir_end_inputs);
ir_IF_TRUE(data->if_unobserved);
ir_END_list(data->ir_end_inputs);
ir_MERGE_list(data->ir_end_inputs);
}
static void jit_observer_fcall_begin(zend_jit_ctx *jit, ir_ref rx, ir_ref observer_handler) {
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_begin_prechecked), rx, observer_handler);
}
static void jit_observer_fcall_end(zend_jit_ctx *jit, ir_ref rx, ir_ref res_ref) {
// JIT: if (execute_data == EG(current_observed_frame)) {
ir_ref has_end_observer = ir_IF(ir_EQ(rx, ir_LOAD_A(jit_EG(current_observed_frame))));
ir_IF_TRUE(has_end_observer);
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_end_prechecked),
rx, res_ref);
ir_MERGE_WITH_EMPTY_FALSE(has_end_observer);
}
static int zend_jit_inc_dec(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op1_def_info, zend_jit_addr op1_def_addr, uint32_t res_use_info, uint32_t res_info, zend_jit_addr res_addr, int may_overflow, int may_throw)
{
ir_ref if_long = IR_UNUSED;
@ -9951,7 +10047,10 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
ir_MERGE_WITH_EMPTY_FALSE(if_need);
}
if (ZEND_OBSERVER_ENABLED) {
if (ZEND_OBSERVER_ENABLED && (!func || (func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0)) {
ir_ref observer_handler;
ir_ref rx = jit_FP(jit);
struct jit_observer_fcall_is_unobserved_data unobserved_data = jit_observer_fcall_is_unobserved_start(jit, func, &observer_handler, rx, func_ref);
if (trace && (trace->op != ZEND_JIT_TRACE_END || trace->stop != ZEND_JIT_TRACE_STOP_INTERPRETER)) {
ZEND_ASSERT(trace[1].op == ZEND_JIT_TRACE_VM || trace[1].op == ZEND_JIT_TRACE_END);
jit_SET_EX_OPLINE(jit, trace[1].opline);
@ -9959,7 +10058,8 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
// EX(opline) = opline
ir_STORE(jit_EX(opline), jit_IP(jit));
}
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_begin), jit_FP(jit));
jit_observer_fcall_begin(jit, rx, observer_handler);
jit_observer_fcall_is_unobserved_end(jit, &unobserved_data);
}
if (trace) {
@ -10086,8 +10186,12 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
// JIT: EG(current_execute_data) = execute_data;
ir_STORE(jit_EG(current_execute_data), rx);
if (ZEND_OBSERVER_ENABLED) {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_begin), rx);
bool may_have_observer = ZEND_OBSERVER_ENABLED && (!func || (func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0);
if (may_have_observer) {
ir_ref observer_handler;
struct jit_observer_fcall_is_unobserved_data unobserved_data = jit_observer_fcall_is_unobserved_start(jit, func, &observer_handler, rx, func_ref ? func_ref : ir_LOAD_A(jit_CALL(rx, func)));
jit_observer_fcall_begin(jit, rx, observer_handler);
jit_observer_fcall_is_unobserved_end(jit, &unobserved_data);
}
// JIT: ZVAL_NULL(EX_VAR(opline->result.var));
@ -10113,8 +10217,9 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
zend_jit_reset_last_valid_opline(jit);
// JIT: (zend_execute_internal ? zend_execute_internal : fbc->internal_function.handler)(call, ret);
ir_ref res_ref = jit_ZVAL_ADDR(jit, res_addr);
if (zend_execute_internal) {
ir_CALL_2(IR_VOID, ir_CONST_FUNC(zend_execute_internal), rx, jit_ZVAL_ADDR(jit, res_addr));
ir_CALL_2(IR_VOID, ir_CONST_FUNC(zend_execute_internal), rx, res_ref);
} else {
if (func) {
func_ptr = ir_CONST_FC_FUNC(func->internal_function.handler);
@ -10124,12 +10229,11 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen
func_ptr = ir_CAST_FC_FUNC(func_ptr);
#endif
}
ir_CALL_2(IR_VOID, func_ptr, rx, jit_ZVAL_ADDR(jit, res_addr));
ir_CALL_2(IR_VOID, func_ptr, rx, res_ref);
}
if (ZEND_OBSERVER_ENABLED) {
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_end),
rx, jit_ZVAL_ADDR(jit, res_addr));
if (may_have_observer) {
jit_observer_fcall_end(jit, rx, res_ref);
}
// JIT: EG(current_execute_data) = execute_data;
@ -10841,9 +10945,7 @@ static int zend_jit_return(zend_jit_ctx *jit, const zend_op *opline, const zend_
}
op1_addr = dst;
}
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_observer_fcall_end),
jit_FP(jit),
jit_ZVAL_ADDR(jit, op1_addr));
jit_observer_fcall_end(jit, jit_FP(jit), jit_ZVAL_ADDR(jit, op1_addr));
}
// JIT: if (!EX(return_value))
@ -16994,6 +17096,21 @@ static bool zend_jit_may_be_in_reg(const zend_op_array *op_array, zend_ssa *ssa,
return 1;
}
static ir_ref jit_frameless_observer(zend_jit_ctx *jit, const zend_op *opline) {
// JIT: zend_observer_handler_is_unobserved(ZEND_OBSERVER_DATA(fbc))
ir_ref observer_handler;
zend_function *fbc = ZEND_FLF_FUNC(opline);
// Not need for runtime cache or generator checks here, we just need if_unobserved
ir_ref if_unobserved = jit_observer_fcall_is_unobserved_start(jit, fbc, &observer_handler, IR_UNUSED, IR_UNUSED).if_unobserved;
// Call zend_frameless_observed_call for the main logic.
ir_CALL_1(IR_VOID, ir_CONST_ADDR((size_t)zend_frameless_observed_call), jit_FP(jit));
ir_ref skip = ir_END();
ir_IF_TRUE(if_unobserved);
return skip;
}
static void jit_frameless_icall0(zend_jit_ctx *jit, const zend_op *opline)
{
jit_SET_EX_OPLINE(jit, opline);
@ -17002,7 +17119,18 @@ static void jit_frameless_icall0(zend_jit_ctx *jit, const zend_op *opline)
zend_jit_addr res_addr = RES_ADDR();
ir_ref res_ref = jit_ZVAL_ADDR(jit, res_addr);
jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL);
ir_ref skip_observer = IR_UNUSED;
if (ZEND_OBSERVER_ENABLED) {
skip_observer = jit_frameless_observer(jit, opline);
}
ir_CALL_1(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref);
if (skip_observer != IR_UNUSED) {
ir_MERGE_WITH(skip_observer);
}
zend_jit_check_exception(jit);
}
@ -17027,7 +17155,18 @@ static void jit_frameless_icall1(zend_jit_ctx *jit, const zend_op *opline, uint3
if (op1_info & MAY_BE_REF) {
op1_ref = jit_ZVAL_DEREF_ref(jit, op1_ref);
}
ir_ref skip_observer = IR_UNUSED;
if (ZEND_OBSERVER_ENABLED) {
skip_observer = jit_frameless_observer(jit, opline);
}
ir_CALL_2(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref);
if (skip_observer != IR_UNUSED) {
ir_MERGE_WITH(skip_observer);
}
jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL);
zend_jit_check_exception(jit);
}
@ -17064,7 +17203,18 @@ static void jit_frameless_icall2(zend_jit_ctx *jit, const zend_op *opline, uint3
if (op2_info & MAY_BE_REF) {
op2_ref = jit_ZVAL_DEREF_ref(jit, op2_ref);
}
ir_ref skip_observer = IR_UNUSED;
if (ZEND_OBSERVER_ENABLED) {
skip_observer = jit_frameless_observer(jit, opline);
}
ir_CALL_3(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref, op2_ref);
if (skip_observer != IR_UNUSED) {
ir_MERGE_WITH(skip_observer);
}
jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL);
/* Set OP1 to UNDEF in case FREE_OP2() throws. */
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) != 0 && (opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0) {
@ -17118,7 +17268,18 @@ static void jit_frameless_icall3(zend_jit_ctx *jit, const zend_op *opline, uint3
if (op1_data_info & MAY_BE_REF) {
op3_ref = jit_ZVAL_DEREF_ref(jit, op3_ref);
}
ir_ref skip_observer = IR_UNUSED;
if (ZEND_OBSERVER_ENABLED) {
skip_observer = jit_frameless_observer(jit, opline);
}
ir_CALL_4(IR_VOID, ir_CONST_ADDR((size_t)function), res_ref, op1_ref, op2_ref, op3_ref);
if (skip_observer != IR_UNUSED) {
ir_MERGE_WITH(skip_observer);
}
jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL);
/* Set OP1 to UNDEF in case FREE_OP2() throws. */
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))

View File

@ -1,5 +1,5 @@
--TEST--
Bug #81435 (Observer current_observed_frame may point to an old (overwritten) frame)
Bug #81435 (Observer EG(current_observed_frame) may point to an old (overwritten) frame)
--EXTENSIONS--
zend_test
--INI--

View File

@ -1,5 +1,5 @@
--TEST--
Observer: Frameless calls are disabled when there are observers
Observer: Frameless calls are properly observable
--EXTENSIONS--
zend_test
--INI--
@ -10,9 +10,13 @@ zend_test.observer.show_output=1
function _strpos(string $str) {
return \strpos($str, 'o', 1);
}
_strpos('foo');
var_dump(_strpos('foo'));
var_dump(_strpos('foo')); // second time, with no observers detected
?>
--EXPECTF--
<!-- init '%s' -->
<!-- init _strpos() -->
<!-- init strpos() -->
<!-- init var_dump() -->
int(1)
int(1)

View File

@ -2108,6 +2108,11 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
#endif
gc_globals_ctor();
zend_observer_startup();
#if ZEND_DEBUG
zend_observer_error_register(report_zend_debug_error_notify_cb);
#endif
zuf.error_function = php_error_cb;
zuf.printf_function = php_printf;
zuf.write_function = php_output_write;
@ -2127,11 +2132,6 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
zend_reset_lc_ctype_locale();
zend_update_current_locale();
zend_observer_startup();
#if ZEND_DEBUG
zend_observer_error_register(report_zend_debug_error_notify_cb);
#endif
#if HAVE_TZSET
tzset();
#endif