Skip to content

Introduce zend_vm_opcode_handler_t / zend_vm_opcode_handler_func_t #19006

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Introduce zend_vm_opcode_handler_t / zend_vm_opcode_handler_func_t
This reduces confusion between opcode handlers used by the VM, and opcode
handler functions used for tracing. Depending on the VM,
zend_vm_opcode_handler_t may not be a real function. For instance in the HYBRID
VM this is a label pointer.
  • Loading branch information
arnaud-lb committed Jul 2, 2025
commit ed49acd79e0c36441b15b52b4d0b5700e1711a61
3 changes: 2 additions & 1 deletion Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "zend_types.h"
#include "zend_map_ptr.h"
#include "zend_alloc.h"
#include "zend_vm_opcodes.h"

#include <stdarg.h>
#include <stdint.h>
Expand Down Expand Up @@ -135,7 +136,7 @@ void zend_const_expr_to_zval(zval *result, zend_ast **ast_ptr, bool allow_dynami
typedef int (*user_opcode_handler_t) (zend_execute_data *execute_data);

struct _zend_op {
const void *handler;
zend_vm_opcode_handler_t handler;
znode_op op1;
znode_op op2;
znode_op result;
Expand Down
14 changes: 9 additions & 5 deletions Zend/zend_vm_execute.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Zend/zend_vm_execute.skl
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ ZEND_API void ZEND_FASTCALL zend_serialize_opcode_handler(zend_op *op)
}
zv = zend_hash_index_find(zend_handlers_table, (zend_long)(uintptr_t)op->handler);
ZEND_ASSERT(zv != NULL);
op->handler = (const void *)(uintptr_t)Z_LVAL_P(zv);
op->handler = (zend_vm_opcode_handler_t)(uintptr_t)Z_LVAL_P(zv);
}

ZEND_API void ZEND_FASTCALL zend_deserialize_opcode_handler(zend_op *op)
Expand Down
23 changes: 19 additions & 4 deletions Zend/zend_vm_gen.php
Original file line number Diff line number Diff line change
Expand Up @@ -1813,11 +1813,11 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#define SPEC_RULE_OBSERVER 0x02000000\n");
out($f,"\n");
out($f,"static const uint32_t *zend_spec_handlers;\n");
out($f,"static const void * const *zend_opcode_handlers;\n");
out($f,"static zend_vm_opcode_handler_t const *zend_opcode_handlers;\n");
out($f,"static int zend_handlers_count;\n");
if ($kind == ZEND_VM_KIND_HYBRID) {
out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
out($f,"static const void * const * zend_opcode_handler_funcs;\n");
out($f,"static zend_vm_opcode_handler_func_t const * zend_opcode_handler_funcs;\n");
out($f,"static zend_op hybrid_halt_op;\n");
out($f,"#endif\n");
}
Expand Down Expand Up @@ -2097,7 +2097,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,$prolog."\tstatic const void * const labels[] = {\n");
gen_labels($f, $spec, ($kind == ZEND_VM_KIND_HYBRID) ? ZEND_VM_KIND_GOTO : $kind, $prolog."\t\t", $specs);
out($f,$prolog."\t};\n");
out($f,$prolog."\tzend_opcode_handlers = (const void **) labels;\n");
out($f,$prolog."\tzend_opcode_handlers = (zend_vm_opcode_handler_t*) labels;\n");
out($f,$prolog."\tzend_handlers_count = sizeof(labels) / sizeof(void*);\n");
if ($kind == ZEND_VM_KIND_HYBRID) {
out($f,$prolog."\tmemset(&hybrid_halt_op, 0, sizeof(hybrid_halt_op));\n");
Expand Down Expand Up @@ -2212,7 +2212,11 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,$prolog."zend_spec_handlers = specs;\n");
out($f,$prolog.$executor_name."_ex(NULL);\n");
} else {
out($f,$prolog."static const void * const labels[] = {\n");
out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
out($f,$prolog."static zend_vm_opcode_handler_func_t const labels[] = {\n");
out($f,"#else\n");
out($f,$prolog."static zend_vm_opcode_handler_t const labels[] = {\n");
out($f,"#endif\n");
gen_labels($f, $spec, ($kind == ZEND_VM_KIND_HYBRID) ? ZEND_VM_KIND_CALL : $kind, $prolog."\t", $specs, $switch_labels);
out($f,$prolog."};\n");
out($f,$prolog."static const uint32_t specs[] = {\n");
Expand Down Expand Up @@ -2359,6 +2363,17 @@ function gen_vm_opcodes_header(
$str .= "# endif\n";
$str .= "#endif\n";
$str .= "\n";
$str .= "#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID\n";
$str .= "typedef const void* zend_vm_opcode_handler_t;\n";
$str .= "typedef void (ZEND_FASTCALL *zend_vm_opcode_handler_func_t)(void);\n";
$str .= "#elif ZEND_VM_KIND == ZEND_VM_KIND_CALL\n";
$str .= "typedef const struct _zend_op *(ZEND_FASTCALL *zend_vm_opcode_handler_t)(struct _zend_execute_data *execute_data, const struct _zend_op *opline);\n";
$str .= "typedef const struct _zend_op *(ZEND_FASTCALL *zend_vm_opcode_handler_func_t)(struct _zend_execute_data *execute_data, const struct _zend_op *opline);\n";
$str .= "#else\n";
$str .= "typedef const void* zend_vm_opcode_handler_t;\n";
$str .= "typedef const void* zend_vm_opcode_handler_func_t;\n";
$str .= "#endif\n";
$str .= "\n";
foreach ($vm_op_flags as $name => $val) {
$str .= sprintf("#define %-24s 0x%08x\n", $name, $val);
}
Expand Down
11 changes: 11 additions & 0 deletions Zend/zend_vm_opcodes.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 31 additions & 31 deletions ext/opcache/jit/zend_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ static size_t dasm_size = 0;

static zend_long jit_bisect_pos = 0;

static const void *zend_jit_runtime_jit_handler = NULL;
static const void *zend_jit_profile_jit_handler = NULL;
static const void *zend_jit_func_hot_counter_handler = NULL;
static const void *zend_jit_loop_hot_counter_handler = NULL;
static const void *zend_jit_func_trace_counter_handler = NULL;
static const void *zend_jit_ret_trace_counter_handler = NULL;
static const void *zend_jit_loop_trace_counter_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_runtime_jit_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_profile_jit_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_func_hot_counter_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_loop_hot_counter_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_func_trace_counter_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_ret_trace_counter_handler = NULL;
static zend_vm_opcode_handler_t zend_jit_loop_trace_counter_handler = NULL;

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS);

Expand Down Expand Up @@ -1417,7 +1417,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
zend_jit_ctx ctx;
zend_jit_ctx *jit = &ctx;
zend_jit_reg_var *ra = NULL;
void *handler;
zend_vm_opcode_handler_t handler;
int call_level = 0;
void *checkpoint = NULL;
bool recv_emitted = 0; /* emitted at least one RECV opcode */
Expand Down Expand Up @@ -3213,7 +3213,7 @@ static void zend_jit_setup_hot_counters_ex(zend_op_array *op_array, zend_cfg *cf
}
}

opline->handler = (const void*)zend_jit_func_hot_counter_handler;
opline->handler = zend_jit_func_hot_counter_handler;
}

if (JIT_G(hot_loop)) {
Expand All @@ -3223,7 +3223,7 @@ static void zend_jit_setup_hot_counters_ex(zend_op_array *op_array, zend_cfg *cf
if ((cfg->blocks[i].flags & ZEND_BB_REACHABLE) &&
(cfg->blocks[i].flags & ZEND_BB_LOOP_HEADER)) {
op_array->opcodes[cfg->blocks[i].start].handler =
(const void*)zend_jit_loop_hot_counter_handler;
zend_jit_loop_hot_counter_handler;
}
}
}
Expand Down Expand Up @@ -3316,7 +3316,7 @@ int zend_jit_op_array(zend_op_array *op_array, zend_script *script)
jit_extension->op_array = op_array;
jit_extension->orig_handler = (void*)opline->handler;
ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
opline->handler = (const void*)zend_jit_runtime_jit_handler;
opline->handler = zend_jit_runtime_jit_handler;
zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);

return SUCCESS;
Expand Down Expand Up @@ -3346,7 +3346,7 @@ int zend_jit_op_array(zend_op_array *op_array, zend_script *script)
jit_extension->op_array = op_array;
jit_extension->orig_handler = (void*)opline->handler;
ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
opline->handler = (const void*)zend_jit_profile_jit_handler;
opline->handler = zend_jit_profile_jit_handler;
zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
}

Expand Down Expand Up @@ -3566,23 +3566,23 @@ void zend_jit_protect(void)

static void zend_jit_init_handlers(void)
{
if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
zend_jit_runtime_jit_handler = zend_jit_stub_handlers[jit_stub_hybrid_runtime_jit];
zend_jit_profile_jit_handler = zend_jit_stub_handlers[jit_stub_hybrid_profile_jit];
zend_jit_func_hot_counter_handler = zend_jit_stub_handlers[jit_stub_hybrid_func_hot_counter];
zend_jit_loop_hot_counter_handler = zend_jit_stub_handlers[jit_stub_hybrid_loop_hot_counter];
zend_jit_func_trace_counter_handler = zend_jit_stub_handlers[jit_stub_hybrid_func_trace_counter];
zend_jit_ret_trace_counter_handler = zend_jit_stub_handlers[jit_stub_hybrid_ret_trace_counter];
zend_jit_loop_trace_counter_handler = zend_jit_stub_handlers[jit_stub_hybrid_loop_trace_counter];
} else {
zend_jit_runtime_jit_handler = (const void*)zend_runtime_jit;
zend_jit_profile_jit_handler = (const void*)zend_jit_profile_helper;
zend_jit_func_hot_counter_handler = (const void*)zend_jit_func_counter_helper;
zend_jit_loop_hot_counter_handler = (const void*)zend_jit_loop_counter_helper;
zend_jit_func_trace_counter_handler = (const void*)zend_jit_func_trace_helper;
zend_jit_ret_trace_counter_handler = (const void*)zend_jit_ret_trace_helper;
zend_jit_loop_trace_counter_handler = (const void*)zend_jit_loop_trace_helper;
}
#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID
zend_jit_runtime_jit_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_runtime_jit];
zend_jit_profile_jit_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_profile_jit];
zend_jit_func_hot_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_func_hot_counter];
zend_jit_loop_hot_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_loop_hot_counter];
zend_jit_func_trace_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_func_trace_counter];
zend_jit_ret_trace_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_ret_trace_counter];
zend_jit_loop_trace_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_loop_trace_counter];
#else
zend_jit_runtime_jit_handler = zend_runtime_jit;
zend_jit_profile_jit_handler = zend_jit_profile_helper;
zend_jit_func_hot_counter_handler = zend_jit_func_counter_helper;
zend_jit_loop_hot_counter_handler = zend_jit_loop_counter_helper;
zend_jit_func_trace_counter_handler = zend_jit_func_trace_helper;
zend_jit_ret_trace_counter_handler = zend_jit_ret_trace_helper;
zend_jit_loop_trace_counter_handler = zend_jit_loop_trace_helper;
#endif
}

static void zend_jit_globals_ctor(zend_jit_globals *jit_globals)
Expand Down Expand Up @@ -3945,9 +3945,9 @@ static void zend_jit_restart_preloaded_op_array(zend_op_array *op_array)
}
}
if (func_info->flags & ZEND_FUNC_JIT_ON_FIRST_EXEC) {
opline->handler = (const void*)zend_jit_runtime_jit_handler;
opline->handler = zend_jit_runtime_jit_handler;
} else {
opline->handler = (const void*)zend_jit_profile_jit_handler;
opline->handler = zend_jit_profile_jit_handler;
}
#endif
}
Expand Down
12 changes: 5 additions & 7 deletions ext/opcache/jit/zend_jit_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "Zend/zend_constants.h"
#include "Zend/Optimizer/zend_func_info.h"
#include "Zend/Optimizer/zend_call_graph.h"
#include "zend_vm_opcodes.h"

/* Address Encoding */
typedef uintptr_t zend_jit_addr;
Expand Down Expand Up @@ -130,7 +131,7 @@ static zend_always_inline bool zend_jit_same_addr(zend_jit_addr addr1, zend_jit_
typedef struct _zend_jit_op_array_extension {
zend_func_info func_info;
const zend_op_array *op_array;
const void *orig_handler;
zend_vm_opcode_handler_t orig_handler;
} zend_jit_op_array_extension;

/* Profiler */
Expand Down Expand Up @@ -169,7 +170,7 @@ typedef struct _zend_jit_op_array_hot_extension {
zend_func_info func_info;
const zend_op_array *op_array;
int16_t *counter;
const void *orig_handlers[1];
zend_vm_opcode_handler_t orig_handlers[1];
} zend_jit_op_array_hot_extension;

#define zend_jit_op_array_hash(op_array) \
Expand Down Expand Up @@ -225,9 +226,6 @@ extern const zend_op *zend_jit_halt_op;
# define ZEND_VM_ENTER_BIT 1ULL
#endif

/* VM handlers */
typedef ZEND_OPCODE_HANDLER_RET (ZEND_FASTCALL *zend_vm_opcode_handler_t)(ZEND_OPCODE_HANDLER_ARGS);

/* VM helpers */
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPCODE_HANDLER_ARGS_EX uint32_t call_info);
ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(ZEND_OPCODE_HANDLER_ARGS_EX uint32_t call_info);
Expand Down Expand Up @@ -339,8 +337,8 @@ typedef enum _zend_jit_trace_stop {
typedef union _zend_op_trace_info {
zend_op dummy; /* the size of this structure must be the same as zend_op */
struct {
const void *orig_handler;
const void *call_handler;
zend_vm_opcode_handler_t orig_handler;
zend_vm_opcode_handler_func_t call_handler;
int16_t *counter;
uint8_t trace_flags;
};
Expand Down
Loading