From c3186c4ec11b5c5ac33eb4d00f16075ee5bee5c5 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 26 Sep 2024 20:26:31 +0300 Subject: [PATCH 001/101] Initial JIT/FFI PoC --- ext/ffi/ffi.c | 92 +------ ext/ffi/ffi_arginfo.h | 2 +- ext/ffi/php_ffi.h | 94 +++++++ ext/opcache/jit/zend_jit.c | 4 + ext/opcache/jit/zend_jit_internal.h | 2 + ext/opcache/jit/zend_jit_ir.c | 340 +++++++++++++++++++++++++- ext/opcache/jit/zend_jit_trace.c | 76 ++++++ ext/opcache/jit/zend_jit_vm_helpers.c | 42 ++++ 8 files changed, 555 insertions(+), 97 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 4e7253b9010e4..c51c005c8c49a 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -74,76 +74,8 @@ typedef struct _zend_ffi_tag { zend_ffi_type *type; } zend_ffi_tag; -typedef enum _zend_ffi_type_kind { - ZEND_FFI_TYPE_VOID, - ZEND_FFI_TYPE_FLOAT, - ZEND_FFI_TYPE_DOUBLE, -#ifdef HAVE_LONG_DOUBLE - ZEND_FFI_TYPE_LONGDOUBLE, -#endif - ZEND_FFI_TYPE_UINT8, - ZEND_FFI_TYPE_SINT8, - ZEND_FFI_TYPE_UINT16, - ZEND_FFI_TYPE_SINT16, - ZEND_FFI_TYPE_UINT32, - ZEND_FFI_TYPE_SINT32, - ZEND_FFI_TYPE_UINT64, - ZEND_FFI_TYPE_SINT64, - ZEND_FFI_TYPE_ENUM, - ZEND_FFI_TYPE_BOOL, - ZEND_FFI_TYPE_CHAR, - ZEND_FFI_TYPE_POINTER, - ZEND_FFI_TYPE_FUNC, - ZEND_FFI_TYPE_ARRAY, - ZEND_FFI_TYPE_STRUCT, -} zend_ffi_type_kind; - #include "ffi_arginfo.h" -typedef enum _zend_ffi_flags { - ZEND_FFI_FLAG_CONST = (1 << 0), - ZEND_FFI_FLAG_OWNED = (1 << 1), - ZEND_FFI_FLAG_PERSISTENT = (1 << 2), -} zend_ffi_flags; - -struct _zend_ffi_type { - zend_ffi_type_kind kind; - size_t size; - uint32_t align; - uint32_t attr; - union { - struct { - zend_string *tag_name; - zend_ffi_type_kind kind; - } enumeration; - struct { - zend_ffi_type *type; - zend_long length; - } array; - struct { - zend_ffi_type *type; - } pointer; - struct { - zend_string *tag_name; - HashTable fields; - } record; - struct { - zend_ffi_type *ret_type; - HashTable *args; - ffi_abi abi; - } func; - }; -}; - -typedef struct _zend_ffi_field { - size_t offset; - bool is_const; - bool is_nested; /* part of nested anonymous struct */ - uint8_t first_bit; - uint8_t bits; - zend_ffi_type *type; -} zend_ffi_field; - typedef enum _zend_ffi_symbol_kind { ZEND_FFI_SYM_TYPE, ZEND_FFI_SYM_CONST, @@ -174,39 +106,19 @@ typedef struct _zend_ffi { bool persistent; } zend_ffi; -#define ZEND_FFI_TYPE_OWNED (1<<0) - -#define ZEND_FFI_TYPE(t) \ - ((zend_ffi_type*)(((uintptr_t)(t)) & ~ZEND_FFI_TYPE_OWNED)) - -#define ZEND_FFI_TYPE_IS_OWNED(t) \ - (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED) - #define ZEND_FFI_TYPE_MAKE_OWNED(t) \ ((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED)) #define ZEND_FFI_SIZEOF_ARG \ MAX(FFI_SIZEOF_ARG, sizeof(double)) -typedef struct _zend_ffi_cdata { - zend_object std; - zend_ffi_type *type; - void *ptr; - void *ptr_holder; - zend_ffi_flags flags; -} zend_ffi_cdata; - -typedef struct _zend_ffi_ctype { - zend_object std; - zend_ffi_type *type; -} zend_ffi_ctype; - static zend_class_entry *zend_ffi_exception_ce; static zend_class_entry *zend_ffi_parser_exception_ce; static zend_class_entry *zend_ffi_ce; -static zend_class_entry *zend_ffi_cdata_ce; static zend_class_entry *zend_ffi_ctype_ce; +ZEND_API zend_class_entry *zend_ffi_cdata_ce; + static zend_object_handlers zend_ffi_handlers; static zend_object_handlers zend_ffi_cdata_handlers; static zend_object_handlers zend_ffi_cdata_value_handlers; diff --git a/ext/ffi/ffi_arginfo.h b/ext/ffi/ffi_arginfo.h index 563c9f8b8e8b1..1e50ce70da792 100644 --- a/ext/ffi/ffi_arginfo.h +++ b/ext/ffi/ffi_arginfo.h @@ -6,7 +6,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_cdef, 0, 0, FFI, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, lib, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_load, 0, 1, FFI, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_load, 0, 1, FFI, 0) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 430b8a2e568f8..819bb4df69914 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -277,4 +277,98 @@ void zend_ffi_val_float_number(zend_ffi_val *val, const char *str, size_t str_le void zend_ffi_val_string(zend_ffi_val *val, const char *str, size_t str_len); void zend_ffi_val_character(zend_ffi_val *val, const char *str, size_t str_len); + +/* FFI internals exported for JIT */ + +typedef enum _zend_ffi_type_kind { + ZEND_FFI_TYPE_VOID, + ZEND_FFI_TYPE_FLOAT, + ZEND_FFI_TYPE_DOUBLE, +#ifdef HAVE_LONG_DOUBLE + ZEND_FFI_TYPE_LONGDOUBLE, +#endif + ZEND_FFI_TYPE_UINT8, + ZEND_FFI_TYPE_SINT8, + ZEND_FFI_TYPE_UINT16, + ZEND_FFI_TYPE_SINT16, + ZEND_FFI_TYPE_UINT32, + ZEND_FFI_TYPE_SINT32, + ZEND_FFI_TYPE_UINT64, + ZEND_FFI_TYPE_SINT64, + ZEND_FFI_TYPE_ENUM, + ZEND_FFI_TYPE_BOOL, + ZEND_FFI_TYPE_CHAR, + ZEND_FFI_TYPE_POINTER, + ZEND_FFI_TYPE_FUNC, + ZEND_FFI_TYPE_ARRAY, + ZEND_FFI_TYPE_STRUCT, +} zend_ffi_type_kind; + +typedef enum _zend_ffi_flags { + ZEND_FFI_FLAG_CONST = (1 << 0), + ZEND_FFI_FLAG_OWNED = (1 << 1), + ZEND_FFI_FLAG_PERSISTENT = (1 << 2), +} zend_ffi_flags; + +struct _zend_ffi_type { + zend_ffi_type_kind kind; + size_t size; + uint32_t align; + uint32_t attr; + union { + struct { + zend_string *tag_name; + zend_ffi_type_kind kind; + } enumeration; + struct { + zend_ffi_type *type; + zend_long length; + } array; + struct { + zend_ffi_type *type; + } pointer; + struct { + zend_string *tag_name; + HashTable fields; + } record; + struct { + zend_ffi_type *ret_type; + HashTable *args; + int abi; /*ffi_abi*/ + } func; + }; +}; + +typedef struct _zend_ffi_field { + size_t offset; + bool is_const; + bool is_nested; /* part of nested anonymous struct */ + uint8_t first_bit; + uint8_t bits; + zend_ffi_type *type; +} zend_ffi_field; + +typedef struct _zend_ffi_cdata { + zend_object std; + zend_ffi_type *type; + void *ptr; + void *ptr_holder; + zend_ffi_flags flags; +} zend_ffi_cdata; + +typedef struct _zend_ffi_ctype { + zend_object std; + zend_ffi_type *type; +} zend_ffi_ctype; + +extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; + +#define ZEND_FFI_TYPE_OWNED (1<<0) + +#define ZEND_FFI_TYPE(t) \ + ((zend_ffi_type*)(((uintptr_t)(t)) & ~ZEND_FFI_TYPE_OWNED)) + +#define ZEND_FFI_TYPE_IS_OWNED(t) \ + (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED) + #endif /* PHP_FFI_H */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index ffcb8daaf568f..b4761c5ea4d74 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -42,6 +42,10 @@ #include "jit/zend_jit_internal.h" +#if HAVE_FFI +# include "ext/ffi/php_ffi.h" +#endif + #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP #include #endif diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 61d8a853fa005..6556ae33f7a3c 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -349,6 +349,8 @@ typedef enum _zend_jit_trace_op { ZEND_JIT_TRACE_VM, ZEND_JIT_TRACE_OP1_TYPE, ZEND_JIT_TRACE_OP2_TYPE, + ZEND_JIT_TRACE_OP1_FFI_TYPE, + ZEND_JIT_TRACE_OP2_FFI_TYPE, ZEND_JIT_TRACE_VAL_INFO, ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_DO_ICALL, diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 9124b8400bcea..096632f6219a6 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12724,6 +12724,90 @@ static int zend_jit_fetch_dim_read(zend_jit_ctx *jit, return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_avoid_refcounting, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t res_info, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type) +{ + uint32_t res_type; + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + + // TODO: ce guard ??? + // TODO: ffi type guard ??? + if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY) { + // TODO: array bounds check ??? + } + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + switch (el_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ir_LOAD_F(ptr))); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ir_LOAD_D(ptr)); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U8(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT8: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I8(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT16: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U16(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT16: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I16(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U32(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I32(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT64: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U32(ptr)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT64: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I32(ptr)); + res_type = IS_LONG; + break; + default: + ZEND_UNREACHABLE(); + } + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + return 1; +} +#endif + static zend_jit_addr zend_jit_prepare_array_update(zend_jit_ctx *jit, const zend_op *opline, uint32_t *op1_info_ptr, @@ -13387,6 +13471,111 @@ static int zend_jit_assign_dim(zend_jit_ctx *jit, return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, + const zend_op *opline, + uint32_t op1_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t val_info, + zend_jit_addr op3_addr, + zend_jit_addr op3_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + + // TODO: ce guard ??? + // TODO: ffi type guard ??? + if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY) { + // TODO: array bounds check ??? + } + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + + ZEND_ASSERT(!res_addr); + + switch (el_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_INT2F(jit_Z_LVAL(jit, op3_addr))); + } else if (val_info == MAY_BE_DOUBLE) { + ir_STORE(ptr, ir_D2F(jit_Z_DVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_DOUBLE: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_INT2D(jit_Z_LVAL(jit, op3_addr))); + } else if (val_info == MAY_BE_DOUBLE) { + ir_STORE(ptr, jit_Z_DVAL(jit, op3_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT8: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_TRUNC_U8(jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT8: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_TRUNC_I8(jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT16: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_TRUNC_U16(jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT16: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_TRUNC_I16(jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT32: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_TRUNC_U32(jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT32: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_TRUNC_I32(jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, jit_Z_LVAL(jit, op3_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + default: + ZEND_UNREACHABLE(); + } + + return 1; +} +#endif + static int zend_jit_assign_dim_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info, @@ -13601,6 +13790,111 @@ static int zend_jit_assign_dim_op(zend_jit_ctx *jit, return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, + const zend_op *opline, + uint32_t op1_info, + uint32_t op1_def_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t op1_data_info, + zend_jit_addr op3_addr, + zend_ssa_range *op1_data_range, + zend_ffi_type *op1_ffi_type) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + + // TODO: ce guard ??? + // TODO: ffi type guard ??? + if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY) { + // TODO: array bounds check ??? + } + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + + ZEND_ASSERT(opline->extended_value == ZEND_ADD); + + switch (el_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_F(ir_LOAD_F(ptr), ir_INT2F(jit_Z_LVAL(jit, op3_addr)))); + } else if (op1_data_info == MAY_BE_DOUBLE) { + ir_STORE(ptr, ir_ADD_F(ir_LOAD_F(ptr), ir_D2F(jit_Z_DVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_DOUBLE: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_D(ir_LOAD_D(ptr), ir_INT2D(jit_Z_LVAL(jit, op3_addr)))); + } else if (op1_data_info == MAY_BE_DOUBLE) { + ir_STORE(ptr, ir_ADD_D(ir_LOAD_D(ptr), jit_Z_DVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT8: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_U8(ir_LOAD_U8(ptr), ir_TRUNC_U8(jit_Z_LVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT8: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_I8(ir_LOAD_I8(ptr), ir_TRUNC_I8(jit_Z_LVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT16: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_U16(ir_LOAD_U16(ptr), ir_TRUNC_U16(jit_Z_LVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT16: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_I16(ir_LOAD_I16(ptr), ir_TRUNC_I16(jit_Z_LVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT32: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_U32(ir_LOAD_U32(ptr), ir_TRUNC_U32(jit_Z_LVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT32: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_I32(ir_LOAD_I32(ptr), ir_TRUNC_I32(jit_Z_LVAL(jit, op3_addr)))); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + if (op1_data_info == MAY_BE_LONG) { + ir_STORE(ptr, ir_ADD_I64(ir_LOAD_I64(ptr), jit_Z_LVAL(jit, op3_addr))); + } else { + ZEND_UNREACHABLE(); + } + break; + default: + ZEND_UNREACHABLE(); + } + + return 1; +} +#endif + static int zend_jit_fe_reset(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info) { zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); @@ -17352,9 +17646,26 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa && (trace->op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)) == IS_ARRAY) { op1_info &= ~((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY); } - return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) && - (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) || - ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING)); + if (((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) + && (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) + || ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING))) { + return 1; + } +#ifdef HAVE_FFI + if (trace + && (trace+1)->op == ZEND_JIT_TRACE_OP1_TYPE + && (trace+2)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; + if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM + && op2_info == MAY_BE_LONG) { + return 1; + } + } +#endif + return 0; case ZEND_ASSIGN_DIM_OP: if (opline->result_type != IS_UNUSED) { return 0; @@ -17394,9 +17705,26 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa return 0; } } - return ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) && - (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) || - ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING)); + if (((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) + && (((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) + || ((op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING))) { + return 1; + } +#ifdef HAVE_FFI + if (trace + && (trace+1)->op == ZEND_JIT_TRACE_OP1_TYPE + && (trace+2)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; + if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM + && op2_info == MAY_BE_LONG) { + return 1; + } + } +#endif + return 0; case ZEND_ASSIGN_OBJ_OP: if (opline->result_type != IS_UNUSED) { return 0; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 3a6e3f0eb9d28..5b35b16adcb00 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4391,6 +4391,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par bool op1_indirect; zend_class_entry *op1_ce = NULL; zend_class_entry *op2_ce = NULL; +#ifdef HAVE_FFI + zend_ffi_type *op1_ffi_type = NULL; + zend_ffi_type *op2_ffi_type = NULL; + (void)op2_ffi_type; +#endif bool gen_handler = false; opline = p->opline; @@ -4411,10 +4416,22 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op1_ce = (zend_class_entry*)(p+1)->ce; p++; } +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + op1_ffi_type = (zend_ffi_type*)(p+1)->ptr; + p++; + } +#endif if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) { op2_ce = (zend_class_entry*)(p+1)->ce; p++; } +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP2_FFI_TYPE) { + op2_ffi_type = (zend_ffi_type*)(p+1)->ptr; + p++; + } +#endif if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) { val_type = (p+1)->op1_type; p++; @@ -4754,6 +4771,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op1_data_info = OP1_DATA_INFO(); CHECK_OP1_DATA_TRACE_TYPE(); op1_def_info = OP1_DEF_INFO(); +#ifdef HAVE_FFI + if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM + && op2_info == MAY_BE_LONG) { + if (!zend_jit_ffi_assign_dim_op(&ctx, opline, + op1_info, op1_def_info, op1_addr, + op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, + (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, + op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), op1_ffi_type)) { + goto jit_failure; + } + } else +#endif if (!zend_jit_assign_dim_op(&ctx, opline, op1_info, op1_def_info, op1_addr, op1_indirect, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, @@ -5074,6 +5106,24 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par CHECK_OP2_TRACE_TYPE(); op1_data_info = OP1_DATA_INFO(); CHECK_OP1_DATA_TRACE_TYPE(); +#ifdef HAVE_FFI + if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM + && op2_info == MAY_BE_LONG) { + if (!zend_jit_ffi_assign_dim(&ctx, opline, + op1_info, op1_addr, + op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, + (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, + op1_data_info, OP1_DATA_REG_ADDR(), + (ctx.ra && (ssa_op+1)->op1_def >= 0) ? OP1_DATA_DEF_REG_ADDR() : 0, + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_type)) { + goto jit_failure; + } + } else +#endif if (!zend_jit_assign_dim(&ctx, opline, op1_info, op1_addr, op1_indirect, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, @@ -5862,6 +5912,20 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } } +#ifdef HAVE_FFI + if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM + && op2_info == MAY_BE_LONG) { + if (!zend_jit_ffi_fetch_dim_read(&ctx, opline, ssa, ssa_op, + op1_info, op1_addr, avoid_refcounting, + op2_info, OP2_REG_ADDR(), OP2_RANGE(), + res_info, RES_REG_ADDR(), op1_ffi_type)) { + goto jit_failure; + } + } else +#endif if (!zend_jit_fetch_dim_read(&ctx, opline, ssa, ssa_op, op1_info, op1_addr, avoid_refcounting, op2_info, OP2_REG_ADDR(), OP2_RANGE(), @@ -7847,6 +7911,12 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa ((op1_type & IS_TRACE_REFERENCE) ? "&" : ""); if ((p+1)->op == ZEND_JIT_TRACE_OP1_TYPE) { p++; +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + fprintf(stderr, " op1(%sobject of class %s: ffi_type)", ref, + ZSTR_VAL(p->ce->name)); + } else +#endif fprintf(stderr, " op1(%sobject of class %s)", ref, ZSTR_VAL(p->ce->name)); } else { @@ -7860,6 +7930,12 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa ((op2_type & IS_TRACE_REFERENCE) ? "&" : ""); if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) { p++; +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP2_FFI_TYPE) { + fprintf(stderr, " op2(%sobject of class %s: ffi_type)", ref, + ZSTR_VAL(p->ce->name)); + } else +#endif fprintf(stderr, " op2(%sobject of class %s)", ref, ZSTR_VAL(p->ce->name)); } else { diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 165896acbdaed..cb322a96d60ea 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -31,6 +31,10 @@ #include "zend_jit_internal.h" +#ifdef HAVE_FFI +# include "ext/ffi/php_ffi.h" +#endif + #ifdef HAVE_GCC_GLOBAL_REGS # pragma GCC diagnostic ignored "-Wvolatile-register-var" # if defined(__x86_64__) @@ -610,6 +614,9 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, int idx, count; uint8_t trace_flags, op1_type, op2_type, op3_type; zend_class_entry *ce1, *ce2; +#ifdef HAVE_FFI + zend_ffi_type *op1_ffi_type, *op2_ffi_type; +#endif const zend_op *link_to_enter_opline = NULL; int backtrack_link_to_enter = -1; int backtrack_recursion = -1; @@ -680,6 +687,9 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, while (1) { ce1 = ce2 = NULL; +#ifdef HAVE_FFI + op1_ffi_type = op2_ffi_type = NULL; +#endif op1_type = op2_type = op3_type = IS_UNKNOWN; if ((opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && opline->opcode != ZEND_ROPE_ADD @@ -703,6 +713,17 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } if (Z_TYPE_P(zv) == IS_OBJECT) { ce1 = Z_OBJCE_P(zv); +#ifdef HAVE_FFI + if (ce1 == zend_ffi_cdata_ce) { + zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); +// if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { + zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); +// if (ffi_type->attr & ZEND_FFI_FLAG_PERSISTENT) { + op1_ffi_type = ffi_type; +// } +// } + } +#endif } else if (Z_TYPE_P(zv) == IS_ARRAY) { if (HT_IS_PACKED(Z_ARRVAL_P(zv))) { flags |= IS_TRACE_PACKED; @@ -752,6 +773,17 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } if (Z_TYPE_P(zv) == IS_OBJECT) { ce2 = Z_OBJCE_P(zv); +#ifdef HAVE_FFI + if (ce2 == zend_ffi_cdata_ce) { + zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); + if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { + zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); + if (ffi_type->attr & ZEND_FFI_FLAG_PERSISTENT) { + op2_ffi_type = ffi_type; + } + } + } +#endif } op2_type |= flags; } @@ -786,10 +818,20 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (ce1) { TRACE_RECORD(ZEND_JIT_TRACE_OP1_TYPE, 0, ce1); +#ifdef HAVE_FFI + if (op1_ffi_type) { + TRACE_RECORD(ZEND_JIT_TRACE_OP1_FFI_TYPE, 0, op1_ffi_type); + } +#endif } if (ce2) { TRACE_RECORD(ZEND_JIT_TRACE_OP2_TYPE, 0, ce2); +#ifdef HAVE_FFI + if (op2_ffi_type) { + TRACE_RECORD(ZEND_JIT_TRACE_OP2_FFI_TYPE, 0, op2_ffi_type); + } +#endif } switch (opline->opcode) { From ca9079e8169d0b642f0f6ade2528812ad21209a8 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 7 Jun 2024 10:00:25 +0300 Subject: [PATCH 002/101] JIT: Implement FFI Array Bounds Checks --- ext/opcache/jit/zend_jit_ir.c | 68 +++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 096632f6219a6..4e5eee7d2e156 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12725,6 +12725,56 @@ static int zend_jit_fetch_dim_read(zend_jit_ctx *jit, } #ifdef HAVE_FFI +static int zend_jit_ffi_abc(zend_jit_ctx *jit, + const zend_op *opline, + zend_ffi_type *ffi_type, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range) +{ + int32_t exit_point; + const void *exit_addr; + + ZEND_ASSERT(op2_info == MAY_BE_LONG); + if (ffi_type->kind == ZEND_FFI_TYPE_ARRAY + && !(ffi_type->attr & (ZEND_FFI_ATTR_VLA|ZEND_FFI_ATTR_INCOMPLETE_ARRAY))) { + if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { + zval *zv = Z_ZV(op2_addr); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_LONG); + if (Z_LVAL_P(zv) < 0 || Z_LVAL_P(zv) >= ffi_type->array.length) { + /* Always out of range */ + exit_point = zend_jit_trace_get_exit_point(opline, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); + } + } else if (!op2_range || op2_range->min < 0 || op2_range->max >= ffi_type->array.length) { + if (op2_range->max < 0 || op2_range->min >= ffi_type->array.length) { + /* Always out of range */ + exit_point = zend_jit_trace_get_exit_point(opline, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); + } else { + /* Array Bounds Check */ + exit_point = zend_jit_trace_get_exit_point(opline, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + ir_GUARD(ir_ULT(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ffi_type->array.length)), + ir_CONST_ADDR(exit_addr)); + } + } + } + + return 1; +} + static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, const zend_op *opline, zend_ssa *ssa, @@ -12744,9 +12794,11 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, // TODO: ce guard ??? // TODO: ffi type guard ??? - if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY) { - // TODO: array bounds check ??? + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; } + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); @@ -13489,9 +13541,11 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, // TODO: ce guard ??? // TODO: ffi type guard ??? - if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY) { - // TODO: array bounds check ??? + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; } + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); @@ -13808,9 +13862,11 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, // TODO: ce guard ??? // TODO: ffi type guard ??? - if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY) { - // TODO: array bounds check ??? + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; } + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); From 95581eed5acc5fdfd2bc27c0ce90d9a7554ffc8d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 7 Jun 2024 11:35:28 +0300 Subject: [PATCH 003/101] JIT: Separate FFI assing_op helper. Add support for all SUB, AND, etc --- ext/opcache/jit/zend_jit_ir.c | 170 ++++++++++++++++++++++++---------- 1 file changed, 123 insertions(+), 47 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 4e5eee7d2e156..72baa08acdbbe 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -13845,106 +13845,182 @@ static int zend_jit_assign_dim_op(zend_jit_ctx *jit, } #ifdef HAVE_FFI -static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, - const zend_op *opline, - uint32_t op1_info, - uint32_t op1_def_info, - zend_jit_addr op1_addr, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t op1_data_info, - zend_jit_addr op3_addr, - zend_ssa_range *op1_data_range, - zend_ffi_type *op1_ffi_type) +static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, + const zend_op *opline, + uint8_t opcode, + zend_ffi_type *el_type, + ir_ref op1, + uint32_t op2_info, + zend_jit_addr op2_addr) { - zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - - // TODO: ce guard ??? - // TODO: ffi type guard ??? + ir_op op; + ir_type type; + ir_ref op2; - if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { - return 0; + switch (opcode) { + case ZEND_ADD: + op = IR_ADD; + break; + case ZEND_SUB: + op = IR_SUB; + break; + case ZEND_MUL: + op = IR_MUL; + break; + case ZEND_BW_OR: + op = IR_OR; + break; + case ZEND_BW_AND: + op = IR_AND; + break; + case ZEND_BW_XOR: + op = IR_XOR; + break; + case ZEND_SL: + // TODO: negative shift + op = IR_SHL; + break; + case ZEND_SR: + // TODO: negative shift + op = IR_SAR; /* TODO: SAR or SHR */ + break; + case ZEND_MOD: + // TODO: mod by zero and -1 + op = IR_MOD; + break; + default: + ZEND_UNREACHABLE(); + return 0; } - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - - - ZEND_ASSERT(opline->extended_value == ZEND_ADD); - switch (el_type->kind) { case ZEND_FFI_TYPE_FLOAT: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_F(ir_LOAD_F(ptr), ir_INT2F(jit_Z_LVAL(jit, op3_addr)))); - } else if (op1_data_info == MAY_BE_DOUBLE) { - ir_STORE(ptr, ir_ADD_F(ir_LOAD_F(ptr), ir_D2F(jit_Z_DVAL(jit, op3_addr)))); + ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); + type = IR_FLOAT; + if (op2_info == MAY_BE_LONG) { + op2 = ir_INT2F(jit_Z_LVAL(jit, op2_addr)); + } else if (op2_info == MAY_BE_DOUBLE) { + op2 = ir_D2F(jit_Z_DVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_DOUBLE: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_D(ir_LOAD_D(ptr), ir_INT2D(jit_Z_LVAL(jit, op3_addr)))); - } else if (op1_data_info == MAY_BE_DOUBLE) { - ir_STORE(ptr, ir_ADD_D(ir_LOAD_D(ptr), jit_Z_DVAL(jit, op3_addr))); + ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); + type = IR_DOUBLE; + if (op2_info == MAY_BE_LONG) { + op2 = ir_INT2D(jit_Z_LVAL(jit, op2_addr)); + } else if (op2_info == MAY_BE_DOUBLE) { + op2 = jit_Z_DVAL(jit, op2_addr); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_UINT8: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_U8(ir_LOAD_U8(ptr), ir_TRUNC_U8(jit_Z_LVAL(jit, op3_addr)))); + type = IR_U8; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U8(jit_Z_LVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_SINT8: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_I8(ir_LOAD_I8(ptr), ir_TRUNC_I8(jit_Z_LVAL(jit, op3_addr)))); + type = IR_I8; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_I8(jit_Z_LVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_UINT16: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_U16(ir_LOAD_U16(ptr), ir_TRUNC_U16(jit_Z_LVAL(jit, op3_addr)))); + type = IR_U16; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U16(jit_Z_LVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_SINT16: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_I16(ir_LOAD_I16(ptr), ir_TRUNC_I16(jit_Z_LVAL(jit, op3_addr)))); + type = IR_I16; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_I16(jit_Z_LVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_UINT32: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_U32(ir_LOAD_U32(ptr), ir_TRUNC_U32(jit_Z_LVAL(jit, op3_addr)))); + type = IR_U32; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U32(jit_Z_LVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_SINT32: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_I32(ir_LOAD_I32(ptr), ir_TRUNC_I32(jit_Z_LVAL(jit, op3_addr)))); + type = IR_I32; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_I32(jit_Z_LVAL(jit, op2_addr)); } else { ZEND_UNREACHABLE(); + return 0; } break; case ZEND_FFI_TYPE_UINT64: case ZEND_FFI_TYPE_SINT64: - if (op1_data_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_ADD_I64(ir_LOAD_I64(ptr), jit_Z_LVAL(jit, op3_addr))); + type = IR_I64; + if (op2_info == MAY_BE_LONG) { + op2 = jit_Z_LVAL(jit, op2_addr); } else { ZEND_UNREACHABLE(); + return 0; } break; default: ZEND_UNREACHABLE(); + return 0; + } + + ir_STORE(op1, ir_BINARY_OP(op, type, ir_LOAD(type, op1), op2)); + + return 1; +} + +static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, + const zend_op *opline, + uint32_t op1_info, + uint32_t op1_def_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t op1_data_info, + zend_jit_addr op3_addr, + zend_ssa_range *op1_data_range, + zend_ffi_type *op1_ffi_type) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + + // TODO: ce guard ??? + // TODO: ffi type guard ??? + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; + } + + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + el_type, ptr, op1_data_info, op3_addr)) { + return 0; } return 1; From 68d146e6b5c54496e86b75efff4673ef806c4e7c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 7 Jun 2024 12:42:51 +0300 Subject: [PATCH 004/101] JIT: Fix NULL dereference --- ext/opcache/jit/zend_jit_ir.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 72baa08acdbbe..44d8dbe3bc809 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12751,7 +12751,7 @@ static int zend_jit_ffi_abc(zend_jit_ctx *jit, jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); } } else if (!op2_range || op2_range->min < 0 || op2_range->max >= ffi_type->array.length) { - if (op2_range->max < 0 || op2_range->min >= ffi_type->array.length) { + if (op2_range && (op2_range->max < 0 || op2_range->min >= ffi_type->array.length)) { /* Always out of range */ exit_point = zend_jit_trace_get_exit_point(opline, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); @@ -13550,7 +13550,6 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - ZEND_ASSERT(!res_addr); switch (el_type->kind) { From e748d7a09473a556fc6e8d7d1cef9c1dfbfa14eb Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 7 Jun 2024 12:45:00 +0300 Subject: [PATCH 005/101] JIT: Fix FFI pointer access through FETCH_DIM, ASSIGN_DIM --- ext/opcache/jit/zend_jit_ir.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 44d8dbe3bc809..3ac122e8ceaa8 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12801,6 +12801,11 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); switch (el_type->kind) { @@ -13548,6 +13553,11 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); ZEND_ASSERT(!res_addr); @@ -14015,6 +14025,11 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, From 6e6147a93acf1d7463e1c6b4a8399f36beb360b9 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 7 Jun 2024 13:00:06 +0300 Subject: [PATCH 006/101] JIT: Fix memory leak in ext/ffi/tests/032.phpt --- ext/opcache/jit/zend_jit_ir.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 3ac122e8ceaa8..0cba0525fc6eb 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12861,6 +12861,12 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; } + if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + return 1; } #endif From 9d94552f3b753f315e30fdc5026bc59c0d763962 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 10 Jun 2024 13:11:56 +0300 Subject: [PATCH 007/101] Support for 32-bit build --- ext/opcache/jit/zend_jit_ir.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 0cba0525fc6eb..e692f156382a1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12833,6 +12833,7 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I16(ptr))); res_type = IS_LONG; break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 case ZEND_FFI_TYPE_UINT32: jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U32(ptr))); res_type = IS_LONG; @@ -12849,6 +12850,16 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I32(ptr)); res_type = IS_LONG; break; +#else + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U32(ptr)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I32(ptr)); + res_type = IS_LONG; + break; +#endif default: ZEND_UNREACHABLE(); } @@ -13615,6 +13626,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ZEND_UNREACHABLE(); } break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 case ZEND_FFI_TYPE_UINT32: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_U32(jit_Z_LVAL(jit, op3_addr))); @@ -13637,6 +13649,16 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ZEND_UNREACHABLE(); } break; +#else + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + if (val_info == MAY_BE_LONG) { + ir_STORE(ptr, jit_Z_LVAL(jit, op3_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; +#endif default: ZEND_UNREACHABLE(); } @@ -13969,6 +13991,7 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, return 0; } break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 case ZEND_FFI_TYPE_UINT32: type = IR_U32; if (op2_info == MAY_BE_LONG) { @@ -13997,6 +14020,18 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, return 0; } break; +#else + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + type = IR_I32; + if (op2_info == MAY_BE_LONG) { + op2 = jit_Z_LVAL(jit, op2_addr); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; +#endif default: ZEND_UNREACHABLE(); return 0; From 1899954f33ed3ac81bc3ac0c6482e5ce306af950 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 10 Jun 2024 14:20:39 +0300 Subject: [PATCH 008/101] Move "zend_ffi_cdata_ce" to Zend to allow JIT<->FFI interopability on Windows --- Zend/zend.c | 2 ++ Zend/zend.h | 1 + ext/ffi/ffi.c | 2 -- ext/ffi/php_ffi.h | 2 -- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index b4a084b1f95c7..2fb549575ef96 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -98,6 +98,8 @@ ZEND_API void (*zend_accel_schedule_restart_hook)(int reason) = NULL; ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size) = NULL; ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL; +ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; + /* This callback must be signal handler safe! */ void (*zend_on_timeout)(int seconds); diff --git a/Zend/zend.h b/Zend/zend.h index 0cf1faeb653fe..fd4a51eb7ab04 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -406,6 +406,7 @@ ZEND_API ZEND_COLD ZEND_NORETURN void zend_strerror_noreturn(int type, int errn, #define ZEND_STANDARD_CLASS_DEF_PTR zend_standard_class_def extern ZEND_API zend_class_entry *zend_standard_class_def; extern ZEND_API zend_utility_values zend_uv; +extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; /* If DTrace is available and enabled */ extern ZEND_API bool zend_dtrace_enabled; diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index c51c005c8c49a..38f0788e65c90 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -117,8 +117,6 @@ static zend_class_entry *zend_ffi_parser_exception_ce; static zend_class_entry *zend_ffi_ce; static zend_class_entry *zend_ffi_ctype_ce; -ZEND_API zend_class_entry *zend_ffi_cdata_ce; - static zend_object_handlers zend_ffi_handlers; static zend_object_handlers zend_ffi_cdata_handlers; static zend_object_handlers zend_ffi_cdata_value_handlers; diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 819bb4df69914..31d42f29075da 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -361,8 +361,6 @@ typedef struct _zend_ffi_ctype { zend_ffi_type *type; } zend_ffi_ctype; -extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; - #define ZEND_FFI_TYPE_OWNED (1<<0) #define ZEND_FFI_TYPE(t) \ From f6f9cf352e3131b6731b280ca10884d6144c2c2f Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 14 Jun 2024 11:44:40 +0300 Subject: [PATCH 009/101] Cache FFI types in opcache SHM (incomplete) --- Zend/zend.c | 4 + Zend/zend.h | 11 + Zend/zend_types.h | 2 + ext/ffi/ffi.c | 105 +++++---- ext/ffi/php_ffi.h | 2 + ext/opcache/ZendAccelerator.c | 296 ++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_vm_helpers.c | 10 +- 7 files changed, 388 insertions(+), 42 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 2fb549575ef96..fad7f68c4e8f4 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -99,6 +99,10 @@ ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, si ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL; ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; +ZEND_API zend_ffi* (*zend_ffi_cache_cdef_get)(zend_string *cdef) = NULL; +ZEND_API zend_ffi* (*zend_ffi_cache_cdef_add)(zend_string *cdef, zend_ffi *ffi) = NULL; +ZEND_API zend_result (*zend_ffi_cache_type_get)(zend_string *str, zend_ffi_dcl *dcl) = NULL; +ZEND_API zend_result (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl) = NULL; /* This callback must be signal handler safe! */ void (*zend_on_timeout)(int seconds); diff --git a/Zend/zend.h b/Zend/zend.h index fd4a51eb7ab04..37d7bf580930a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -406,8 +406,19 @@ ZEND_API ZEND_COLD ZEND_NORETURN void zend_strerror_noreturn(int type, int errn, #define ZEND_STANDARD_CLASS_DEF_PTR zend_standard_class_def extern ZEND_API zend_class_entry *zend_standard_class_def; extern ZEND_API zend_utility_values zend_uv; + +/* FFI/OPCache interopability API */ extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; +typedef struct _zend_ffi zend_ffi; +typedef struct _zend_ffi_dcl zend_ffi_dcl; + +ZEND_API extern zend_ffi* (*zend_ffi_cache_cdef_get)(zend_string *cdef); +ZEND_API extern zend_ffi* (*zend_ffi_cache_cdef_add)(zend_string *cdef, zend_ffi *ffi); +ZEND_API extern zend_result (*zend_ffi_cache_type_get)(zend_string *str, zend_ffi_dcl *dcl); +ZEND_API extern zend_result (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl); + + /* If DTrace is available and enabled */ extern ZEND_API bool zend_dtrace_enabled; END_EXTERN_C() diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 8f012868ddab4..3b973b90afab1 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -818,6 +818,8 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_STR_PERSISTENT GC_PERSISTENT /* allocated using malloc */ #define IS_STR_PERMANENT (1<<8) /* relives request boundary */ #define IS_STR_VALID_UTF8 (1<<9) /* valid UTF-8 according to PCRE */ +#define IS_STR_FFI_TYPE (1<<10) /* intrned string associated with FFI type */ +#define IS_STR_FFI_DECL (1<<11) /* intrned string associated with FFI declaration object */ /* array flags */ #define IS_ARRAY_IMMUTABLE GC_IMMUTABLE diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 38f0788e65c90..cb3a6050c071b 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -128,7 +128,7 @@ static zend_internal_function zend_ffi_cast_fn; static zend_internal_function zend_ffi_type_fn; /* forward declarations */ -static void _zend_ffi_type_dtor(zend_ffi_type *type); +//???static void _zend_ffi_type_dtor(zend_ffi_type *type); static void zend_ffi_finalize_type(zend_ffi_dcl *dcl); static bool zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2); static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type); @@ -2206,7 +2206,7 @@ static zend_object *zend_ffi_new(zend_class_entry *class_type) /* {{{ */ } /* }}} */ -static void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */ +ZEND_API void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */ { type = ZEND_FFI_TYPE(type); @@ -2964,6 +2964,13 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ FFI_G(tags) = NULL; if (code && ZSTR_LEN(code)) { + if (zend_ffi_cache_cdef_get) { + ffi = zend_ffi_cache_cdef_get(code); + if (ffi) { + RETURN_OBJ(&ffi->std); + } + } + /* Parse C definitions */ FFI_G(default_type_attr) = ZEND_FFI_ATTR_STORED; @@ -3013,6 +3020,10 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ ffi->symbols = FFI_G(symbols); ffi->tags = FFI_G(tags); + if (zend_ffi_cache_cdef_add) { + ffi = zend_ffi_cache_cdef_add(code, ffi); + } + FFI_G(symbols) = NULL; FFI_G(tags) = NULL; @@ -3756,7 +3767,10 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(default_type_attr) = 0; - if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { + if (zend_ffi_cache_type_get + && zend_ffi_cache_type_get(type_def, &dcl) == SUCCESS) { + /* pass */ + } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { zend_hash_destroy(FFI_G(tags)); @@ -3769,6 +3783,21 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(symbols) = NULL; } return; + } else { + if (clean_tags && FFI_G(tags)) { + zend_ffi_tags_cleanup(&dcl); + } + if (clean_symbols && FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + FFI_G(symbols) = NULL; + FFI_G(tags) = NULL; + + if (zend_ffi_cache_type_add) { + zend_ffi_cache_type_add(type_def, &dcl); + } } type = ZEND_FFI_TYPE(dcl.type); @@ -3776,17 +3805,6 @@ ZEND_METHOD(FFI, new) /* {{{ */ is_const = 1; } - if (clean_tags && FFI_G(tags)) { - zend_ffi_tags_cleanup(&dcl); - } - if (clean_symbols && FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - FFI_G(symbols) = NULL; - FFI_G(tags) = NULL; - type_ptr = dcl.type; } else { zend_ffi_ctype *ctype = (zend_ffi_ctype*) type_obj; @@ -3906,7 +3924,10 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(default_type_attr) = 0; - if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { + if (zend_ffi_cache_type_get + && zend_ffi_cache_type_get(type_def, &dcl) == SUCCESS) { + /* pass */ + } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { zend_hash_destroy(FFI_G(tags)); @@ -3919,6 +3940,21 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(symbols) = NULL; } return; + } else { + if (clean_tags && FFI_G(tags)) { + zend_ffi_tags_cleanup(&dcl); + } + if (clean_symbols && FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + FFI_G(symbols) = NULL; + FFI_G(tags) = NULL; + + if (zend_ffi_cache_type_add) { + zend_ffi_cache_type_add(type_def, &dcl); + } } type = ZEND_FFI_TYPE(dcl.type); @@ -3926,17 +3962,6 @@ ZEND_METHOD(FFI, cast) /* {{{ */ is_const = 1; } - if (clean_tags && FFI_G(tags)) { - zend_ffi_tags_cleanup(&dcl); - } - if (clean_symbols && FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - FFI_G(symbols) = NULL; - FFI_G(tags) = NULL; - type_ptr = dcl.type; } else { zend_ffi_ctype *ctype = (zend_ffi_ctype*) ztype; @@ -4078,7 +4103,10 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(default_type_attr) = 0; - if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { + if (zend_ffi_cache_type_get + && zend_ffi_cache_type_get(type_def, &dcl) == SUCCESS) { + /* pass */ + } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { zend_hash_destroy(FFI_G(tags)); @@ -4091,18 +4119,21 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(symbols) = NULL; } return; - } - - if (clean_tags && FFI_G(tags)) { - zend_ffi_tags_cleanup(&dcl); - } - if (clean_symbols && FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); + } else { + if (clean_tags && FFI_G(tags)) { + zend_ffi_tags_cleanup(&dcl); + } + if (clean_symbols && FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } FFI_G(symbols) = NULL; + FFI_G(tags) = NULL; + if (zend_ffi_cache_type_add) { + zend_ffi_cache_type_add(type_def, &dcl); + } } - FFI_G(symbols) = NULL; - FFI_G(tags) = NULL; ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce); ctype->type = dcl.type; diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 31d42f29075da..f59d3ec820e5d 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -369,4 +369,6 @@ typedef struct _zend_ffi_ctype { #define ZEND_FFI_TYPE_IS_OWNED(t) \ (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED) +ZEND_API void _zend_ffi_type_dtor(zend_ffi_type *type); + #endif /* PHP_FFI_H */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 406c539f44a27..50323c6baa5be 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2475,6 +2475,288 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, return new_ce; } +#if HAVE_FFI +#include "ext/ffi/php_ffi.h" + +static ssize_t accel_ffi_persist_type_calc(zend_ffi_type *type) +{ + HashTable *ht; + Bucket *b; + zval *zv; + zend_ffi_field *f; + ssize_t s, size = 0; + + if (!ZEND_FFI_TYPE_IS_OWNED(type) + && (type->attr & ZEND_FFI_ATTR_PERSISTENT)) { + return 0; + } + + type = ZEND_FFI_TYPE(type); + + if (zend_shared_alloc_get_xlat_entry(type)) { + return 0; + } + + zend_shared_alloc_register_xlat_entry(type, type); + + size = sizeof(zend_ffi_type); + switch (type->kind) { + case ZEND_FFI_TYPE_ENUM: + if (type->enumeration.tag_name) { + type->enumeration.tag_name = accel_new_interned_string(type->enumeration.tag_name); + if (!IS_ACCEL_INTERNED(type->enumeration.tag_name)) { + return -1; + } + } + break; + case ZEND_FFI_TYPE_POINTER: + s = accel_ffi_persist_type_calc(type->pointer.type); + if (s == -1) { + return -1; + } + size += s; + break; + case ZEND_FFI_TYPE_ARRAY: + s = accel_ffi_persist_type_calc(type->array.type); + if (s == -1) { + return -1; + } + size += s; + break; + case ZEND_FFI_TYPE_STRUCT: + if (type->record.tag_name) { + type->record.tag_name = accel_new_interned_string(type->record.tag_name); + if (!IS_ACCEL_INTERNED(type->record.tag_name)) { + return -1; + } + } + ht = &type->record.fields; + if (HT_IS_PACKED(ht)) { + size += HT_PACKED_USED_SIZE(ht); + } else { + size += HT_USED_SIZE(ht); + } + ZEND_HASH_FOREACH_BUCKET(ht, b) { + if (b->key) { + b->key = accel_new_interned_string(b->key); + if (!IS_ACCEL_INTERNED(b->key)) { + return -1; + } + } + size += sizeof(zend_ffi_field); + f = (zend_ffi_field*)Z_PTR(b->val); + s = accel_ffi_persist_type_calc(f->type); + if (s == -1) { + return -1; + } + size += s; + } ZEND_HASH_FOREACH_END(); + break; + case ZEND_FFI_TYPE_FUNC: + if (type->func.ret_type) { + s = accel_ffi_persist_type_calc(type->func.ret_type); + if (s == -1) { + return -1; + } + size += s; + } + if (type->func.args) { + ht = type->func.args; + size += sizeof(HashTable); + ZEND_ASSERT(HT_IS_PACKED(ht)); + size += HT_PACKED_USED_SIZE(ht); + ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) { + s = accel_ffi_persist_type_calc((zend_ffi_type*)Z_PTR_P(zv)); + if (s == -1) { + return -1; + } + size += s; + } ZEND_HASH_FOREACH_END(); + } + break; + default: + break; + } + + return size; +} + +static zend_ffi_type* accel_ffi_persist_type_copy(void** ptr, zend_ffi_type *type) +{ + HashTable *ht; + Bucket *b; + zval *zv; + zend_ffi_field *f; + zend_ffi_type *new_type; + + if (!ZEND_FFI_TYPE_IS_OWNED(type) + && (type->attr & ZEND_FFI_ATTR_PERSISTENT)) { + return type; + } + + type = ZEND_FFI_TYPE(type); + + new_type = zend_shared_alloc_get_xlat_entry(type); + if (new_type) { + return new_type; + } + + new_type = (zend_ffi_type*)*ptr; + (*ptr) = (void*)((char*)(*ptr) + sizeof(zend_ffi_type)); + memcpy(new_type, type, sizeof(zend_ffi_type)); + new_type->attr |= ZEND_FFI_ATTR_PERSISTENT; + + zend_shared_alloc_register_xlat_entry(type, new_type); + + switch (new_type->kind) { + case ZEND_FFI_TYPE_POINTER: + new_type->pointer.type = accel_ffi_persist_type_copy(ptr, new_type->pointer.type); + break; + case ZEND_FFI_TYPE_ARRAY: + new_type->array.type = accel_ffi_persist_type_copy(ptr, new_type->array.type); + break; + case ZEND_FFI_TYPE_STRUCT: + ht = &new_type->record.fields; + if (HT_IS_PACKED(ht)) { + memcpy(*ptr, ht->arPacked, HT_PACKED_USED_SIZE(ht)); + ht->arPacked = (zval*)(*ptr); + (*ptr) = (void*)((char*)(*ptr) + HT_PACKED_USED_SIZE(ht)); + } else { + memcpy(*ptr, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); + HT_SET_DATA_ADDR(ht, (Bucket*)(*ptr)); + (*ptr) = (void*)((char*)(*ptr) + HT_USED_SIZE(ht)); + } + ZEND_HASH_FOREACH_BUCKET(&new_type->record.fields, b) { + f = (zend_ffi_field*)(*ptr); + memcpy(f, Z_PTR(b->val), sizeof(zend_ffi_field)); + Z_PTR(b->val) = f; + (*ptr) = (void*)((char*)(*ptr) + sizeof(zend_ffi_field)); + f->type = accel_ffi_persist_type_copy(ptr, f->type); + } ZEND_HASH_FOREACH_END(); + break; + case ZEND_FFI_TYPE_FUNC: + if (new_type->func.ret_type) { + new_type->func.ret_type = accel_ffi_persist_type_copy(ptr, new_type->func.ret_type); + } + if (new_type->func.args) { + ht = (*ptr); + memcpy(ht, new_type->func.args, sizeof(HashTable)); + (*ptr) = (void*)((char*)(*ptr) + sizeof(HashTable)); + new_type->func.args = ht; + ZEND_ASSERT(HT_IS_PACKED(ht)); + memcpy(*ptr, ht->arPacked, HT_PACKED_USED_SIZE(ht)); + ht->arPacked = (zval*)(*ptr); + (*ptr) = (void*)((char*)(*ptr) + HT_PACKED_USED_SIZE(ht)); + ZEND_HASH_PACKED_FOREACH_VAL(ht, zv) { + Z_PTR_P(zv) = accel_ffi_persist_type_copy(ptr, (zend_ffi_type*)Z_PTR_P(zv)); + } ZEND_HASH_FOREACH_END(); + } + break; + default: + break; + } + + return new_type; +} + +static zend_ffi_dcl* accel_ffi_persist_type(zend_ffi_dcl *dcl) +{ + void *ptr; + zend_ffi_dcl *new_dcl; + ssize_t size; + + zend_shared_alloc_init_xlat_table(); + size = accel_ffi_persist_type_calc(ZEND_FFI_TYPE(dcl->type)); + zend_shared_alloc_destroy_xlat_table(); + + if (size == -1) { + return NULL; + } + ptr = zend_shared_alloc(sizeof(zend_ffi_dcl) + size); + if (!ptr) { + return NULL; + } + + zend_shared_alloc_init_xlat_table(); + + new_dcl = (zend_ffi_dcl*)ptr; + ptr = (void*)((char*)ptr + sizeof(zend_ffi_dcl)); + memcpy(new_dcl, dcl, sizeof(zend_ffi_dcl)); + new_dcl->type = accel_ffi_persist_type_copy(&ptr, new_dcl->type); + + zend_shared_alloc_destroy_xlat_table(); + + return new_dcl; +} + +static zend_result accel_ffi_cache_type_get(zend_string *str, zend_ffi_dcl *dcl) +{ + str = accel_find_interned_string(str); + if (str && (str->gc.u.type_info & IS_STR_FFI_TYPE)) { + // TODO: ??? + zend_ffi_dcl *ptr = (zend_ffi_dcl*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + memcpy(dcl, ptr, sizeof(zend_ffi_dcl)); + return SUCCESS; + } + return FAILURE; +} + +static zend_result accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl) +{ + zend_result ret = FAILURE; + + if (!ZEND_FFI_TYPE_IS_OWNED(dcl->type) + && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT)) { + return SUCCESS; + } + + SHM_UNPROTECT(); + zend_shared_alloc_lock(); + + if (accel_ffi_cache_type_get(str, dcl) == FAILURE) { + str = accel_new_interned_string(zend_string_copy(str)); + if (IS_ACCEL_INTERNED(str)) { + zend_ffi_dcl *new_dcl = accel_ffi_persist_type(dcl); + + if (new_dcl) { + // TODO: ??? + ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_TYPE|IS_STR_FFI_DECL))); + ZEND_ASSERT((char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p > 0 && + (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); + str->gc.u.type_info |= IS_STR_FFI_TYPE; + if (ZEND_FFI_TYPE_IS_OWNED(dcl->type)) { + _zend_ffi_type_dtor(dcl->type); + } + dcl->type = new_dcl->type; + // TODO: ??? + str->gc.refcount = (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p; + ret = SUCCESS; + } + } + } + + zend_shared_alloc_unlock(); + SHM_PROTECT(); + + return ret; +} + +static zend_ffi* accel_ffi_cache_cdef_get(zend_string *str) +{ + str = accel_find_interned_string(str); + if (str && (str->gc.u.type_info & IS_STR_FFI_DECL)) { + // TODO: ??? + } + return NULL; +} + +static zend_ffi* accel_ffi_cache_cdef_add(zend_string *str, zend_ffi *ffi) +{ + // TODO: ??? + return ffi; +} +#endif + #ifdef ZEND_WIN32 static zend_result accel_gen_uname_id(void) { @@ -3362,6 +3644,13 @@ static zend_result accel_post_startup(void) accelerator_orig_inheritance_cache_add = zend_inheritance_cache_add; zend_inheritance_cache_get = zend_accel_inheritance_cache_get; zend_inheritance_cache_add = zend_accel_inheritance_cache_add; + +#if HAVE_FFI + zend_ffi_cache_cdef_get = accel_ffi_cache_cdef_get; + zend_ffi_cache_cdef_add = accel_ffi_cache_cdef_add; + zend_ffi_cache_type_get = accel_ffi_cache_type_get; + zend_ffi_cache_type_add = accel_ffi_cache_type_add; +#endif } return SUCCESS; @@ -3416,6 +3705,13 @@ void accel_shutdown(void) zend_inheritance_cache_get = accelerator_orig_inheritance_cache_get; zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add; +#if HAVE_FFI + zend_ffi_cache_cdef_get = NULL; + zend_ffi_cache_cdef_add = NULL; + zend_ffi_cache_type_get = NULL; + zend_ffi_cache_type_add = NULL; +#endif + if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) { ini_entry->on_modify = orig_include_path_on_modify; } diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index cb322a96d60ea..92a0c247885db 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -716,12 +716,12 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #ifdef HAVE_FFI if (ce1 == zend_ffi_cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); -// if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { + if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); -// if (ffi_type->attr & ZEND_FFI_FLAG_PERSISTENT) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { op1_ffi_type = ffi_type; -// } -// } + } + } } #endif } else if (Z_TYPE_P(zv) == IS_ARRAY) { @@ -778,7 +778,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); - if (ffi_type->attr & ZEND_FFI_FLAG_PERSISTENT) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { op2_ffi_type = ffi_type; } } From 3d20a0a4b7801592d06cabbf3a27df8587fc7a52 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 14 Jun 2024 11:58:07 +0300 Subject: [PATCH 010/101] Make cached HashTables immutable --- ext/opcache/ZendAccelerator.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 50323c6baa5be..6dd5cf82d80af 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2617,6 +2617,11 @@ static zend_ffi_type* accel_ffi_persist_type_copy(void** ptr, zend_ffi_type *typ break; case ZEND_FFI_TYPE_STRUCT: ht = &new_type->record.fields; + GC_SET_REFCOUNT(ht, 2); + GC_ADD_FLAGS(ht, IS_ARRAY_IMMUTABLE); + HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; + ht->pDestructor = NULL; + ht->nInternalPointer = 0; if (HT_IS_PACKED(ht)) { memcpy(*ptr, ht->arPacked, HT_PACKED_USED_SIZE(ht)); ht->arPacked = (zval*)(*ptr); @@ -2643,6 +2648,11 @@ static zend_ffi_type* accel_ffi_persist_type_copy(void** ptr, zend_ffi_type *typ memcpy(ht, new_type->func.args, sizeof(HashTable)); (*ptr) = (void*)((char*)(*ptr) + sizeof(HashTable)); new_type->func.args = ht; + GC_SET_REFCOUNT(ht, 2); + GC_ADD_FLAGS(ht, IS_ARRAY_IMMUTABLE); + HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; + ht->pDestructor = NULL; + ht->nInternalPointer = 0; ZEND_ASSERT(HT_IS_PACKED(ht)); memcpy(*ptr, ht->arPacked, HT_PACKED_USED_SIZE(ht)); ht->arPacked = (zval*)(*ptr); From c3bee27674973360e00f9efd2d0d63bf3bece763 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 17 Jun 2024 15:49:34 +0300 Subject: [PATCH 011/101] Cache FFI types and scopes in opcache SHM --- Zend/zend.c | 8 +- Zend/zend.h | 10 +- Zend/zend_types.h | 2 +- ext/ffi/ffi.c | 120 +++++++++-------- ext/ffi/php_ffi.h | 35 ++++- ext/opcache/ZendAccelerator.c | 233 ++++++++++++++++++++++++++++++---- 6 files changed, 318 insertions(+), 90 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index fad7f68c4e8f4..8b8b808bf09a2 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -99,10 +99,10 @@ ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, si ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL; ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; -ZEND_API zend_ffi* (*zend_ffi_cache_cdef_get)(zend_string *cdef) = NULL; -ZEND_API zend_ffi* (*zend_ffi_cache_cdef_add)(zend_string *cdef, zend_ffi *ffi) = NULL; -ZEND_API zend_result (*zend_ffi_cache_type_get)(zend_string *str, zend_ffi_dcl *dcl) = NULL; -ZEND_API zend_result (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl) = NULL; +ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str) = NULL; +ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl) = NULL; +ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str) = NULL; +ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope) = NULL; /* This callback must be signal handler safe! */ void (*zend_on_timeout)(int seconds); diff --git a/Zend/zend.h b/Zend/zend.h index 37d7bf580930a..68cf5c6f249e5 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -410,13 +410,13 @@ extern ZEND_API zend_utility_values zend_uv; /* FFI/OPCache interopability API */ extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; -typedef struct _zend_ffi zend_ffi; typedef struct _zend_ffi_dcl zend_ffi_dcl; +typedef struct _zend_ffi_scope zend_ffi_scope; -ZEND_API extern zend_ffi* (*zend_ffi_cache_cdef_get)(zend_string *cdef); -ZEND_API extern zend_ffi* (*zend_ffi_cache_cdef_add)(zend_string *cdef, zend_ffi *ffi); -ZEND_API extern zend_result (*zend_ffi_cache_type_get)(zend_string *str, zend_ffi_dcl *dcl); -ZEND_API extern zend_result (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl); +ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str); +ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl); +ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str); +ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope); /* If DTrace is available and enabled */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3b973b90afab1..8ce99639e97da 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -819,7 +819,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_STR_PERMANENT (1<<8) /* relives request boundary */ #define IS_STR_VALID_UTF8 (1<<9) /* valid UTF-8 according to PCRE */ #define IS_STR_FFI_TYPE (1<<10) /* intrned string associated with FFI type */ -#define IS_STR_FFI_DECL (1<<11) /* intrned string associated with FFI declaration object */ +#define IS_STR_FFI_SCOPE (1<<11) /* intrned string associated with FFI declaration object */ /* array flags */ #define IS_ARRAY_IMMUTABLE GC_IMMUTABLE diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index cb3a6050c071b..c9c66d18eda5e 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -60,50 +60,16 @@ ZEND_DECLARE_MODULE_GLOBALS(ffi) -typedef enum _zend_ffi_tag_kind { - ZEND_FFI_TAG_ENUM, - ZEND_FFI_TAG_STRUCT, - ZEND_FFI_TAG_UNION -} zend_ffi_tag_kind; - static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"}; - -typedef struct _zend_ffi_tag { - zend_ffi_tag_kind kind; - zend_ffi_type *type; -} zend_ffi_tag; - #include "ffi_arginfo.h" -typedef enum _zend_ffi_symbol_kind { - ZEND_FFI_SYM_TYPE, - ZEND_FFI_SYM_CONST, - ZEND_FFI_SYM_VAR, - ZEND_FFI_SYM_FUNC -} zend_ffi_symbol_kind; - -typedef struct _zend_ffi_symbol { - zend_ffi_symbol_kind kind; - bool is_const; - zend_ffi_type *type; - union { - void *addr; - int64_t value; - }; -} zend_ffi_symbol; - -typedef struct _zend_ffi_scope { - HashTable *symbols; - HashTable *tags; -} zend_ffi_scope; - typedef struct _zend_ffi { zend_object std; DL_HANDLE lib; HashTable *symbols; HashTable *tags; - bool persistent; + bool persistent; } zend_ffi; #define ZEND_FFI_TYPE_MAKE_OWNED(t) \ @@ -128,7 +94,7 @@ static zend_internal_function zend_ffi_cast_fn; static zend_internal_function zend_ffi_type_fn; /* forward declarations */ -//???static void _zend_ffi_type_dtor(zend_ffi_type *type); +static void _zend_ffi_type_dtor(zend_ffi_type *type); static void zend_ffi_finalize_type(zend_ffi_dcl *dcl); static bool zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2); static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type); @@ -2206,7 +2172,7 @@ static zend_object *zend_ffi_new(zend_class_entry *class_type) /* {{{ */ } /* }}} */ -ZEND_API void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */ +static void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */ { type = ZEND_FFI_TYPE(type); @@ -2927,6 +2893,7 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ DL_HANDLE handle = NULL; char *err; void *addr; + bool persistent = false; ZEND_FFI_VALIDATE_API_RESTRICTION(); ZEND_PARSE_PARAMETERS_START(0, 2) @@ -2964,9 +2931,14 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ FFI_G(tags) = NULL; if (code && ZSTR_LEN(code)) { - if (zend_ffi_cache_cdef_get) { - ffi = zend_ffi_cache_cdef_get(code); - if (ffi) { + if (zend_ffi_cache_scope_get) { + zend_ffi_scope *scope = zend_ffi_cache_scope_get(code); + if (scope) { + ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); + ffi->lib = handle; + ffi->symbols = scope->symbols; + ffi->tags = scope->tags; + ffi->persistent = true; RETURN_OBJ(&ffi->std); } } @@ -3013,16 +2985,37 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ } } ZEND_HASH_FOREACH_END(); } + + if (zend_ffi_cache_scope_add) { + zend_ffi_scope scope, *cached_scope; + + scope.symbols = FFI_G(symbols); + scope.tags = FFI_G(tags); + cached_scope = zend_ffi_cache_scope_add(code, &scope); + if (cached_scope) { + if (FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + if (FFI_G(tags)) { + zend_hash_destroy(FFI_G(tags)); + efree(FFI_G(tags)); + FFI_G(tags) = NULL; + } + FFI_G(symbols) = cached_scope->symbols; + FFI_G(tags) = cached_scope->tags; + persistent = true; + } + } + } ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); ffi->lib = handle; ffi->symbols = FFI_G(symbols); ffi->tags = FFI_G(tags); - - if (zend_ffi_cache_cdef_add) { - ffi = zend_ffi_cache_cdef_add(code, ffi); - } + ffi->persistent = persistent; FFI_G(symbols) = NULL; FFI_G(tags) = NULL; @@ -3753,6 +3746,7 @@ ZEND_METHOD(FFI, new) /* {{{ */ if (type_def) { zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT; + zend_ffi_dcl *cached_dcl; if (!is_static_call) { zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This)); @@ -3768,8 +3762,8 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(default_type_attr) = 0; if (zend_ffi_cache_type_get - && zend_ffi_cache_type_get(type_def, &dcl) == SUCCESS) { - /* pass */ + && (cached_dcl = zend_ffi_cache_type_get(type_def))) { + memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { @@ -3796,7 +3790,13 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(tags) = NULL; if (zend_ffi_cache_type_add) { - zend_ffi_cache_type_add(type_def, &dcl); + cached_dcl = zend_ffi_cache_type_add(type_def, &dcl); + if (cached_dcl) { + if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { + _zend_ffi_type_dtor(dcl.type); + } + memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); + } } } @@ -3910,6 +3910,7 @@ ZEND_METHOD(FFI, cast) /* {{{ */ if (type_def) { zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT; + zend_ffi_dcl *cached_dcl; if (!is_static_call) { zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This)); @@ -3925,8 +3926,8 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(default_type_attr) = 0; if (zend_ffi_cache_type_get - && zend_ffi_cache_type_get(type_def, &dcl) == SUCCESS) { - /* pass */ + && (cached_dcl = zend_ffi_cache_type_get(type_def))) { + memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { @@ -3953,7 +3954,13 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(tags) = NULL; if (zend_ffi_cache_type_add) { - zend_ffi_cache_type_add(type_def, &dcl); + cached_dcl = zend_ffi_cache_type_add(type_def, &dcl); + if (cached_dcl) { + if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { + _zend_ffi_type_dtor(dcl.type); + } + memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); + } } } @@ -4075,6 +4082,7 @@ ZEND_METHOD(FFI, type) /* {{{ */ { zend_ffi_ctype *ctype; zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT; + zend_ffi_dcl *cached_dcl; zend_string *type_def; bool is_static_call = Z_TYPE(EX(This)) != IS_OBJECT; @@ -4104,8 +4112,8 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(default_type_attr) = 0; if (zend_ffi_cache_type_get - && zend_ffi_cache_type_get(type_def, &dcl) == SUCCESS) { - /* pass */ + && (cached_dcl = zend_ffi_cache_type_get(type_def))) { + memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { @@ -4131,7 +4139,13 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(symbols) = NULL; FFI_G(tags) = NULL; if (zend_ffi_cache_type_add) { - zend_ffi_cache_type_add(type_def, &dcl); + cached_dcl = zend_ffi_cache_type_add(type_def, &dcl); + if (cached_dcl) { + if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { + _zend_ffi_type_dtor(dcl.type); + } + memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); + } } } diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index f59d3ec820e5d..9a66e67a1072a 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -361,6 +361,39 @@ typedef struct _zend_ffi_ctype { zend_ffi_type *type; } zend_ffi_ctype; +typedef enum _zend_ffi_tag_kind { + ZEND_FFI_TAG_ENUM, + ZEND_FFI_TAG_STRUCT, + ZEND_FFI_TAG_UNION +} zend_ffi_tag_kind; + +typedef struct _zend_ffi_tag { + zend_ffi_tag_kind kind; + zend_ffi_type *type; +} zend_ffi_tag; + +typedef enum _zend_ffi_symbol_kind { + ZEND_FFI_SYM_TYPE, + ZEND_FFI_SYM_CONST, + ZEND_FFI_SYM_VAR, + ZEND_FFI_SYM_FUNC +} zend_ffi_symbol_kind; + +typedef struct _zend_ffi_symbol { + zend_ffi_symbol_kind kind; + bool is_const; + zend_ffi_type *type; + union { + void *addr; + int64_t value; + }; +} zend_ffi_symbol; + +typedef struct _zend_ffi_scope { + HashTable *symbols; + HashTable *tags; +} zend_ffi_scope; + #define ZEND_FFI_TYPE_OWNED (1<<0) #define ZEND_FFI_TYPE(t) \ @@ -369,6 +402,4 @@ typedef struct _zend_ffi_ctype { #define ZEND_FFI_TYPE_IS_OWNED(t) \ (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED) -ZEND_API void _zend_ffi_type_dtor(zend_ffi_type *type); - #endif /* PHP_FFI_H */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 6dd5cf82d80af..3f79585358a4e 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2699,21 +2699,20 @@ static zend_ffi_dcl* accel_ffi_persist_type(zend_ffi_dcl *dcl) return new_dcl; } -static zend_result accel_ffi_cache_type_get(zend_string *str, zend_ffi_dcl *dcl) +static zend_ffi_dcl* accel_ffi_cache_type_get(zend_string *str) { str = accel_find_interned_string(str); if (str && (str->gc.u.type_info & IS_STR_FFI_TYPE)) { // TODO: ??? zend_ffi_dcl *ptr = (zend_ffi_dcl*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); - memcpy(dcl, ptr, sizeof(zend_ffi_dcl)); - return SUCCESS; + return ptr; } - return FAILURE; + return NULL; } -static zend_result accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl) +static zend_ffi_dcl* accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl) { - zend_result ret = FAILURE; + zend_ffi_dcl *new_dcl = NULL; if (!ZEND_FFI_TYPE_IS_OWNED(dcl->type) && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT)) { @@ -2723,24 +2722,19 @@ static zend_result accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl) SHM_UNPROTECT(); zend_shared_alloc_lock(); - if (accel_ffi_cache_type_get(str, dcl) == FAILURE) { + new_dcl = accel_ffi_cache_type_get(str); + if (!new_dcl) { str = accel_new_interned_string(zend_string_copy(str)); if (IS_ACCEL_INTERNED(str)) { - zend_ffi_dcl *new_dcl = accel_ffi_persist_type(dcl); - + new_dcl = accel_ffi_persist_type(dcl); if (new_dcl) { // TODO: ??? - ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_TYPE|IS_STR_FFI_DECL))); + ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_TYPE|IS_STR_FFI_SCOPE))); ZEND_ASSERT((char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p > 0 && (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); str->gc.u.type_info |= IS_STR_FFI_TYPE; - if (ZEND_FFI_TYPE_IS_OWNED(dcl->type)) { - _zend_ffi_type_dtor(dcl->type); - } - dcl->type = new_dcl->type; // TODO: ??? str->gc.refcount = (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p; - ret = SUCCESS; } } } @@ -2748,22 +2742,211 @@ static zend_result accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl) zend_shared_alloc_unlock(); SHM_PROTECT(); - return ret; + return new_dcl; +} + +static ssize_t accel_ffi_persist_scope_calc(zend_ffi_scope *scope) +{ + HashTable *ht; + Bucket *b; + zend_ffi_symbol *sym; + zend_ffi_tag *tag; + ssize_t size = 0; + + if (scope->symbols) { + ht = scope->symbols; + size += sizeof(HashTable); + if (HT_IS_PACKED(ht)) { + size += HT_PACKED_USED_SIZE(ht); + } else { + size += HT_USED_SIZE(ht); + } + ZEND_HASH_FOREACH_BUCKET(ht, b) { + if (b->key) { + b->key = accel_new_interned_string(b->key); + if (!IS_ACCEL_INTERNED(b->key)) { + return -1; + } + } + size += sizeof(zend_ffi_symbol); + sym = (zend_ffi_symbol*)Z_PTR(b->val); + if (sym->type) { + ssize_t s = accel_ffi_persist_type_calc(sym->type); + if (s == -1) { + return -1; + } + size += s; + } + } ZEND_HASH_FOREACH_END(); + } + + if (scope->tags) { + ht = scope->tags; + size += sizeof(HashTable); + if (HT_IS_PACKED(ht)) { + size += HT_PACKED_USED_SIZE(ht); + } else { + size += HT_USED_SIZE(ht); + } + ZEND_HASH_FOREACH_BUCKET(ht, b) { + if (b->key) { + b->key = accel_new_interned_string(b->key); + if (!IS_ACCEL_INTERNED(b->key)) { + return -1; + } + } + size += sizeof(zend_ffi_tag); + tag = (zend_ffi_tag*)Z_PTR(b->val); + if (tag->type) { + ssize_t s = accel_ffi_persist_type_calc(tag->type); + if (s == -1) { + return -1; + } + size += s; + } + } ZEND_HASH_FOREACH_END(); + } + return size; +} + +static void accel_ffi_persist_scope_copy(void **ptr, zend_ffi_scope *scope) +{ + HashTable *ht; + Bucket *b; + zend_ffi_symbol *sym; + zend_ffi_tag *tag; + + if (scope->symbols) { + ht = (*ptr); + memcpy(ht, scope->symbols, sizeof(HashTable)); + (*ptr) = (void*)((char*)(*ptr) + sizeof(HashTable)); + scope->symbols = ht; + GC_SET_REFCOUNT(ht, 2); + GC_ADD_FLAGS(ht, IS_ARRAY_IMMUTABLE); + HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; + ht->pDestructor = NULL; + ht->nInternalPointer = 0; + if (HT_IS_PACKED(ht)) { + memcpy(*ptr, ht->arPacked, HT_PACKED_USED_SIZE(ht)); + ht->arPacked = (zval*)(*ptr); + (*ptr) = (void*)((char*)(*ptr) + HT_PACKED_USED_SIZE(ht)); + } else { + memcpy(*ptr, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); + HT_SET_DATA_ADDR(ht, (Bucket*)(*ptr)); + (*ptr) = (void*)((char*)(*ptr) + HT_USED_SIZE(ht)); + } + ZEND_HASH_FOREACH_BUCKET(ht, b) { + sym = (zend_ffi_symbol*)(*ptr); + memcpy(sym, Z_PTR(b->val), sizeof(zend_ffi_symbol)); + Z_PTR(b->val) = sym; + (*ptr) = (void*)((char*)(*ptr) + sizeof(zend_ffi_symbol)); + if (sym->type) { + sym->type = accel_ffi_persist_type_copy(ptr, sym->type); + } + } ZEND_HASH_FOREACH_END(); + } + + if (scope->tags) { + ht = (*ptr); + memcpy(ht, scope->tags, sizeof(HashTable)); + (*ptr) = (void*)((char*)(*ptr) + sizeof(HashTable)); + scope->tags = ht; + GC_SET_REFCOUNT(ht, 2); + GC_ADD_FLAGS(ht, IS_ARRAY_IMMUTABLE); + HT_FLAGS(ht) |= HASH_FLAG_STATIC_KEYS; + ht->pDestructor = NULL; + ht->nInternalPointer = 0; + if (HT_IS_PACKED(ht)) { + memcpy(*ptr, ht->arPacked, HT_PACKED_USED_SIZE(ht)); + ht->arPacked = (zval*)(*ptr); + (*ptr) = (void*)((char*)(*ptr) + HT_PACKED_USED_SIZE(ht)); + } else { + memcpy(*ptr, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); + HT_SET_DATA_ADDR(ht, (Bucket*)(*ptr)); + (*ptr) = (void*)((char*)(*ptr) + HT_USED_SIZE(ht)); + } + ZEND_HASH_FOREACH_BUCKET(ht, b) { + tag = (zend_ffi_tag*)(*ptr); + memcpy(tag, Z_PTR(b->val), sizeof(zend_ffi_tag)); + Z_PTR(b->val) = tag; + (*ptr) = (void*)((char*)(*ptr) + sizeof(zend_ffi_tag)); + if (tag->type) { + tag->type = accel_ffi_persist_type_copy(ptr, tag->type); + } + } ZEND_HASH_FOREACH_END(); + } +} + +static zend_ffi_scope* accel_ffi_persist_scope(zend_ffi_scope *scope) +{ + void *ptr; + zend_ffi_scope *new_scope; + ssize_t size; + + zend_shared_alloc_init_xlat_table(); + size = accel_ffi_persist_scope_calc(scope); + zend_shared_alloc_destroy_xlat_table(); + + if (size == -1) { + return NULL; + } + new_scope = zend_shared_alloc(sizeof(zend_ffi_scope) + size); + if (!new_scope) { + return NULL; + } + + new_scope->symbols = scope->symbols; + new_scope->tags = scope->tags; + ptr = (char*)new_scope + sizeof(zend_ffi_scope); + + zend_shared_alloc_init_xlat_table(); + + accel_ffi_persist_scope_copy(&ptr, new_scope); + + zend_shared_alloc_destroy_xlat_table(); + + return new_scope; } -static zend_ffi* accel_ffi_cache_cdef_get(zend_string *str) +static zend_ffi_scope* accel_ffi_cache_scope_get(zend_string *str) { str = accel_find_interned_string(str); - if (str && (str->gc.u.type_info & IS_STR_FFI_DECL)) { + if (str && (str->gc.u.type_info & IS_STR_FFI_SCOPE)) { // TODO: ??? + zend_ffi_scope *ptr = (zend_ffi_scope*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + return ptr; } return NULL; } -static zend_ffi* accel_ffi_cache_cdef_add(zend_string *str, zend_ffi *ffi) +static zend_ffi_scope* accel_ffi_cache_scope_add(zend_string *str, zend_ffi_scope *scope) { - // TODO: ??? - return ffi; + zend_ffi_scope *new_scope = NULL; + + SHM_UNPROTECT(); + zend_shared_alloc_lock(); + + new_scope = accel_ffi_cache_scope_get(str); + if (!new_scope) { + str = accel_new_interned_string(zend_string_copy(str)); + if (IS_ACCEL_INTERNED(str)) { + new_scope = accel_ffi_persist_scope(scope); + if (new_scope) { + // TODO: ??? + ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_TYPE|IS_STR_FFI_SCOPE))); + ZEND_ASSERT((char*)new_scope - (char*)ZSMMG(shared_segments)[0]->p > 0 && + (char*)new_scope - (char*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); + str->gc.u.type_info |= IS_STR_FFI_SCOPE; + // TODO: ??? + str->gc.refcount = (char*)new_scope - (char*)ZSMMG(shared_segments)[0]->p; + } + } + } + + zend_shared_alloc_unlock(); + SHM_PROTECT(); + + return new_scope; } #endif @@ -3656,10 +3839,10 @@ static zend_result accel_post_startup(void) zend_inheritance_cache_add = zend_accel_inheritance_cache_add; #if HAVE_FFI - zend_ffi_cache_cdef_get = accel_ffi_cache_cdef_get; - zend_ffi_cache_cdef_add = accel_ffi_cache_cdef_add; zend_ffi_cache_type_get = accel_ffi_cache_type_get; zend_ffi_cache_type_add = accel_ffi_cache_type_add; + zend_ffi_cache_scope_get = accel_ffi_cache_scope_get; + zend_ffi_cache_scope_add = accel_ffi_cache_scope_add; #endif } @@ -3716,10 +3899,10 @@ void accel_shutdown(void) zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add; #if HAVE_FFI - zend_ffi_cache_cdef_get = NULL; - zend_ffi_cache_cdef_add = NULL; zend_ffi_cache_type_get = NULL; zend_ffi_cache_type_add = NULL; + zend_ffi_cache_scope_get = NULL; + zend_ffi_cache_scope_add = NULL; #endif if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) { From a5b089b27c49d0bce8627ec4c9ad8e54833d59cf Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 17 Jun 2024 17:22:31 +0300 Subject: [PATCH 012/101] Fix incorrect return type --- ext/opcache/ZendAccelerator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 3f79585358a4e..82ab859110da1 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2716,7 +2716,7 @@ static zend_ffi_dcl* accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dc if (!ZEND_FFI_TYPE_IS_OWNED(dcl->type) && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT)) { - return SUCCESS; + return NULL; } SHM_UNPROTECT(); From e4e34f1e190fbbe19de06f42a44ba4336f41f439 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 17 Jun 2024 21:48:31 +0300 Subject: [PATCH 013/101] Cache FFI scope created by FFI::load() --- ext/ffi/ffi.c | 61 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index c9c66d18eda5e..d52fa00d4ecae 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -3008,7 +3008,6 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ persistent = true; } } - } ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); @@ -3209,7 +3208,8 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ { struct stat buf; int fd; - char *code, *code_pos, *scope_name, *lib, *err; + zend_string *code; + char *code_pos, *scope_name, *lib, *err; size_t code_size, scope_name_len; zend_ffi *ffi; DL_HANDLE handle = NULL; @@ -3218,6 +3218,7 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ zend_ffi_symbol *sym; zend_ffi_tag *tag; void *addr; + bool persistent = false; if (stat(filename, &buf) != 0) { if (preload) { @@ -3238,24 +3239,36 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ } code_size = buf.st_size; - code = emalloc(code_size + 1); + code = zend_string_alloc(code_size, 0); int open_flags = O_RDONLY; #ifdef PHP_WIN32 open_flags |= _O_BINARY; #endif fd = open(filename, open_flags, 0); - if (fd < 0 || read(fd, code, code_size) != code_size) { + if (fd < 0 || read(fd, ZSTR_VAL(code), code_size) != code_size) { if (preload) { zend_error(E_WARNING, "FFI: Failed pre-loading '%s', cannot read_file", filename); } else { zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', cannot read_file", filename); } - efree(code); + zend_string_release(code); close(fd); return NULL; } close(fd); - code[code_size] = 0; + ZSTR_VAL(code)[code_size] = 0; + + if (!preload && zend_ffi_cache_scope_get) { + zend_ffi_scope *scope = zend_ffi_cache_scope_get(code); + if (scope) { + ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); + ffi->lib = handle; + ffi->symbols = scope->symbols; + ffi->tags = scope->tags; + ffi->persistent = true; + return ffi; + } + } FFI_G(symbols) = NULL; FFI_G(tags) = NULL; @@ -3267,13 +3280,13 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ scope_name = NULL; scope_name_len = 0; lib = NULL; - code_pos = zend_ffi_parse_directives(filename, code, &scope_name, &lib, preload); + code_pos = zend_ffi_parse_directives(filename, ZSTR_VAL(code), &scope_name, &lib, preload); if (!code_pos) { - efree(code); + zend_string_release(code); FFI_G(persistent) = 0; return NULL; } - code_size -= code_pos - code; + code_size -= code_pos - ZSTR_VAL(code); if (zend_ffi_parse_decl(code_pos, code_size) == FAILURE) { if (preload) { @@ -3456,21 +3469,45 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ ffi->tags = scope->tags; ffi->persistent = 1; } else { + if (zend_ffi_cache_scope_add) { + zend_ffi_scope scope, *cached_scope; + + scope.symbols = FFI_G(symbols); + scope.tags = FFI_G(tags); + cached_scope = zend_ffi_cache_scope_add(code, &scope); + if (cached_scope) { + if (FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + if (FFI_G(tags)) { + zend_hash_destroy(FFI_G(tags)); + efree(FFI_G(tags)); + FFI_G(tags) = NULL; + } + FFI_G(symbols) = cached_scope->symbols; + FFI_G(tags) = cached_scope->tags; + persistent = true; + } + } + ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); ffi->lib = handle; ffi->symbols = FFI_G(symbols); ffi->tags = FFI_G(tags); + ffi->persistent = persistent; } - efree(code); + zend_string_release(code); FFI_G(symbols) = NULL; FFI_G(tags) = NULL; - FFI_G(persistent) = 0; + FFI_G(persistent) = persistent; return ffi; cleanup: - efree(code); + zend_string_release(code); if (FFI_G(symbols)) { zend_hash_destroy(FFI_G(symbols)); pefree(FFI_G(symbols), preload); From ff5e593d200fee14a090816af706a9a91b5721ea Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 18 Jun 2024 11:44:45 +0300 Subject: [PATCH 014/101] Add class abd FFI type guards --- ext/opcache/jit/zend_jit_ir.c | 136 ++++++++++++++++++++++++------- ext/opcache/jit/zend_jit_trace.c | 4 +- 2 files changed, 108 insertions(+), 32 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index e692f156382a1..d1faedc8cc9fd 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12725,6 +12725,23 @@ static int zend_jit_fetch_dim_read(zend_jit_ctx *jit, } #ifdef HAVE_FFI +static int zend_jit_class_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_class_entry *ce); + +static int zend_jit_ffi_type_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_ffi_type *ffi_type) +{ + int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, type))), ir_CONST_ADDR(ffi_type)), + ir_CONST_ADDR(exit_addr)); + + return 1; +} + static int zend_jit_ffi_abc(zend_jit_ctx *jit, const zend_op *opline, zend_ffi_type *ffi_type, @@ -12791,15 +12808,30 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, { uint32_t res_type; zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (ssa->var_info + && ssa_op->op1_use >= 0 + && ssa->var_info[ssa_op->op1_use].ce != zend_ffi_cdata_ce) { + if (!zend_jit_class_guard(jit, opline, obj_ref, zend_ffi_cdata_ce)) { + return 0; + } + if (ssa->var_info && ssa_op->op1_use >= 0) { + ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[ssa_op->op1_use].ce = zend_ffi_cdata_ce; + ssa->var_info[ssa_op->op1_use].is_instanceof = 0; + } + } - // TODO: ce guard ??? // TODO: ffi type guard ??? + if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { + return 0; + } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { return 0; } - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { @@ -13546,29 +13578,51 @@ static int zend_jit_assign_dim(zend_jit_ctx *jit, } #ifdef HAVE_FFI -static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, - const zend_op *opline, - uint32_t op1_info, - zend_jit_addr op1_addr, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t val_info, - zend_jit_addr op3_addr, - zend_jit_addr op3_def_addr, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type) +static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t val_info, + zend_jit_addr op3_addr, + zend_jit_addr op3_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (ssa->var_info + && ssa_op->op1_use >= 0 + && ssa->var_info[ssa_op->op1_use].ce != zend_ffi_cdata_ce) { + if (!zend_jit_class_guard(jit, opline, obj_ref, zend_ffi_cdata_ce)) { + return 0; + } + if (ssa->var_info && ssa_op->op1_use >= 0) { + ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[ssa_op->op1_use].ce = zend_ffi_cdata_ce; + ssa->var_info[ssa_op->op1_use].is_instanceof = 0; + } + if (ssa->var_info && ssa_op->op1_def >= 0) { + ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[ssa_op->op1_def].ce = zend_ffi_cdata_ce; + ssa->var_info[ssa_op->op1_def].is_instanceof = 0; + } + } - // TODO: ce guard ??? // TODO: ffi type guard ??? + if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { + return 0; + } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { return 0; } - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { @@ -14042,29 +14096,51 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, return 1; } -static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, - const zend_op *opline, - uint32_t op1_info, - uint32_t op1_def_info, - zend_jit_addr op1_addr, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t op1_data_info, - zend_jit_addr op3_addr, - zend_ssa_range *op1_data_range, - zend_ffi_type *op1_ffi_type) +static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + uint32_t op1_def_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t op1_data_info, + zend_jit_addr op3_addr, + zend_ssa_range *op1_data_range, + zend_ffi_type *op1_ffi_type) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (ssa->var_info + && ssa_op->op1_use >= 0 + && ssa->var_info[ssa_op->op1_use].ce != zend_ffi_cdata_ce) { + if (!zend_jit_class_guard(jit, opline, obj_ref, zend_ffi_cdata_ce)) { + return 0; + } + if (ssa->var_info && ssa_op->op1_use >= 0) { + ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[ssa_op->op1_use].ce = zend_ffi_cdata_ce; + ssa->var_info[ssa_op->op1_use].is_instanceof = 0; + } + if (ssa->var_info && ssa_op->op1_def >= 0) { + ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[ssa_op->op1_def].ce = zend_ffi_cdata_ce; + ssa->var_info[ssa_op->op1_def].is_instanceof = 0; + } + } - // TODO: ce guard ??? // TODO: ffi type guard ??? + if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { + return 0; + } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { return 0; } - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 5b35b16adcb00..e58f21d1eaf20 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4777,7 +4777,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM && op2_info == MAY_BE_LONG) { - if (!zend_jit_ffi_assign_dim_op(&ctx, opline, + if (!zend_jit_ffi_assign_dim_op(&ctx, opline, ssa, ssa_op, op1_info, op1_def_info, op1_addr, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, @@ -5112,7 +5112,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM && op2_info == MAY_BE_LONG) { - if (!zend_jit_ffi_assign_dim(&ctx, opline, + if (!zend_jit_ffi_assign_dim(&ctx, opline, ssa, ssa_op, op1_info, op1_addr, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, From 6032baaea0c8f790bf25dba48f2ec8cc76918e82 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 18 Jun 2024 12:04:30 +0300 Subject: [PATCH 015/101] Avoid repeatable FFI type guards (no LICM yet) --- ext/opcache/jit/zend_jit.c | 7 +++++ ext/opcache/jit/zend_jit_ir.c | 53 ++++++++++++++++++++++++-------- ext/opcache/jit/zend_jit_trace.c | 20 ++++++++++-- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index b4761c5ea4d74..071dad23b253a 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -44,6 +44,13 @@ #if HAVE_FFI # include "ext/ffi/php_ffi.h" + +# define FFI_TYPE_GUARD (1<<0) + +typedef struct zend_jit_ffi_info { + zend_ffi_type *type; + uint32_t info; +} zend_jit_ffi_info; #endif #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index d1faedc8cc9fd..3622860c82df4 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12804,7 +12804,8 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, zend_ssa_range *op2_range, uint32_t res_info, zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type) + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) { uint32_t res_type; zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); @@ -12823,9 +12824,15 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, } } - // TODO: ffi type guard ??? - if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { - return 0; + if (ffi_info + && ssa_op->op1_use >= 0 + && (ffi_info[ssa_op->op1_use].type != op1_ffi_type + || (ffi_info[ssa_op->op1_use].info & FFI_TYPE_GUARD))) { + if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { + return 0; + } + ffi_info[ssa_op->op1_use].info &= ~FFI_TYPE_GUARD; + ffi_info[ssa_op->op1_use].type = op1_ffi_type; } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { @@ -13591,7 +13598,8 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, zend_jit_addr op3_addr, zend_jit_addr op3_def_addr, zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type) + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); @@ -13614,9 +13622,19 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, } } - // TODO: ffi type guard ??? - if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { - return 0; + if (ffi_info + && ssa_op->op1_use >= 0 + && (ffi_info[ssa_op->op1_use].type != op1_ffi_type + || (ffi_info[ssa_op->op1_use].info & FFI_TYPE_GUARD))) { + if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { + return 0; + } + ffi_info[ssa_op->op1_use].info &= ~FFI_TYPE_GUARD; + ffi_info[ssa_op->op1_use].type = op1_ffi_type; + if (ssa_op->op1_def >= 0) { + ffi_info[ssa_op->op1_def].info &= ~FFI_TYPE_GUARD; + ffi_info[ssa_op->op1_def].type = op1_ffi_type; + } } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { @@ -14109,7 +14127,8 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, uint32_t op1_data_info, zend_jit_addr op3_addr, zend_ssa_range *op1_data_range, - zend_ffi_type *op1_ffi_type) + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); @@ -14132,9 +14151,19 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, } } - // TODO: ffi type guard ??? - if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { - return 0; + if (ffi_info + && ssa_op->op1_use >= 0 + && (ffi_info[ssa_op->op1_use].type != op1_ffi_type + || (ffi_info[ssa_op->op1_use].info & FFI_TYPE_GUARD))) { + if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { + return 0; + } + ffi_info[ssa_op->op1_use].info &= ~FFI_TYPE_GUARD; + ffi_info[ssa_op->op1_use].type = op1_ffi_type; + if (ssa_op->op1_def >= 0) { + ffi_info[ssa_op->op1_def].info &= ~FFI_TYPE_GUARD; + ffi_info[ssa_op->op1_def].type = op1_ffi_type; + } } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index e58f21d1eaf20..2f903f57945e6 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4121,6 +4121,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par int checked_stack; int peek_checked_stack; uint32_t frame_flags = 0; +#ifdef HAVE_FFI + zend_jit_ffi_info *ffi_info = NULL; +#endif JIT_G(current_trace) = trace_buffer; @@ -4777,11 +4780,15 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM && op2_info == MAY_BE_LONG) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } if (!zend_jit_ffi_assign_dim_op(&ctx, opline, ssa, ssa_op, op1_info, op1_def_info, op1_addr, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, - op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), op1_ffi_type)) { + op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), + op1_ffi_type, ffi_info)) { goto jit_failure; } } else @@ -5112,6 +5119,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM && op2_info == MAY_BE_LONG) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } if (!zend_jit_ffi_assign_dim(&ctx, opline, ssa, ssa_op, op1_info, op1_addr, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, @@ -5119,7 +5129,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op1_data_info, OP1_DATA_REG_ADDR(), (ctx.ra && (ssa_op+1)->op1_def >= 0) ? OP1_DATA_DEF_REG_ADDR() : 0, (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, - op1_ffi_type)) { + op1_ffi_type, ffi_info)) { goto jit_failure; } } else @@ -5918,10 +5928,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM && op2_info == MAY_BE_LONG) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } if (!zend_jit_ffi_fetch_dim_read(&ctx, opline, ssa, ssa_op, op1_info, op1_addr, avoid_refcounting, op2_info, OP2_REG_ADDR(), OP2_RANGE(), - res_info, RES_REG_ADDR(), op1_ffi_type)) { + res_info, RES_REG_ADDR(), + op1_ffi_type, ffi_info)) { goto jit_failure; } } else From 566b3eb160d5ed5ca13fe4f39736342b5810eadf Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 19 Jun 2024 15:04:00 +0300 Subject: [PATCH 016/101] JIT: Implement special support for FFI "struct" access --- ext/opcache/jit/zend_jit_ir.c | 436 +++++++++++++++++++------------ ext/opcache/jit/zend_jit_trace.c | 78 +++++- 2 files changed, 351 insertions(+), 163 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 3622860c82df4..1526bd775c4cc 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12792,62 +12792,14 @@ static int zend_jit_ffi_abc(zend_jit_ctx *jit, return 1; } -static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_avoid_refcounting, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t res_info, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) +static int zend_jit_ffi_read(zend_jit_ctx *jit, + zend_ffi_type *ffi_type, + ir_ref ptr, + zend_jit_addr res_addr) { uint32_t res_type; - zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (ssa->var_info - && ssa_op->op1_use >= 0 - && ssa->var_info[ssa_op->op1_use].ce != zend_ffi_cdata_ce) { - if (!zend_jit_class_guard(jit, opline, obj_ref, zend_ffi_cdata_ce)) { - return 0; - } - if (ssa->var_info && ssa_op->op1_use >= 0) { - ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[ssa_op->op1_use].ce = zend_ffi_cdata_ce; - ssa->var_info[ssa_op->op1_use].is_instanceof = 0; - } - } - - if (ffi_info - && ssa_op->op1_use >= 0 - && (ffi_info[ssa_op->op1_use].type != op1_ffi_type - || (ffi_info[ssa_op->op1_use].info & FFI_TYPE_GUARD))) { - if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { - return 0; - } - ffi_info[ssa_op->op1_use].info &= ~FFI_TYPE_GUARD; - ffi_info[ssa_op->op1_use].type = op1_ffi_type; - } - - if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { - return 0; - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - - if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - cdata_ref = ir_LOAD_A(cdata_ref); - } - - ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - switch (el_type->kind) { + switch (ffi_type->kind) { case ZEND_FFI_TYPE_FLOAT: jit_set_Z_DVAL(jit, res_addr, ir_F2D(ir_LOAD_F(ptr))); res_type = IS_DOUBLE; @@ -12902,10 +12854,95 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, default: ZEND_UNREACHABLE(); } + if (Z_MODE(res_addr) != IS_REG) { jit_set_Z_TYPE_INFO(jit, res_addr, res_type); } + return 1; +} + +static int zend_jit_ffi_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + ir_ref ref, + zend_ffi_type *ffi_type, + zend_jit_ffi_info *ffi_info) +{ + if (ssa->var_info + && use >= 0 + && ssa->var_info[use].ce != zend_ffi_cdata_ce) { + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_cdata_ce)) { + return 0; + } + ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[use].ce = zend_ffi_cdata_ce; + ssa->var_info[use].is_instanceof = 0; + if (def >= 0) { + ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[def].ce = zend_ffi_cdata_ce; + ssa->var_info[def].is_instanceof = 0; + } + } + + if (ffi_info + && use >= 0 + && (ffi_info[use].type != ffi_type + || (ffi_info[use].info & FFI_TYPE_GUARD))) { + if (!zend_jit_ffi_type_guard(jit, opline, ref, ffi_type)) { + return 0; + } + ffi_info[use].info &= ~FFI_TYPE_GUARD; + ffi_info[use].type = ffi_type; + if (def >= 0) { + ffi_info[def].info &= ~FFI_TYPE_GUARD; + ffi_info[def].type = ffi_type; + } + } + + return 1; +} + +static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_avoid_refcounting, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t res_info, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + if (!zend_jit_ffi_read(jit, el_type, ptr, res_addr)) { + return 0; + } + if (res_info & MAY_BE_GUARD) { // TODO: ??? ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; @@ -13585,115 +13622,55 @@ static int zend_jit_assign_dim(zend_jit_ctx *jit, } #ifdef HAVE_FFI -static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t val_info, - zend_jit_addr op3_addr, - zend_jit_addr op3_def_addr, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) +static int zend_jit_ffi_write(zend_jit_ctx *jit, + zend_ffi_type *ffi_type, + ir_ref ptr, + uint32_t val_info, + zend_jit_addr val_addr) { - zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (ssa->var_info - && ssa_op->op1_use >= 0 - && ssa->var_info[ssa_op->op1_use].ce != zend_ffi_cdata_ce) { - if (!zend_jit_class_guard(jit, opline, obj_ref, zend_ffi_cdata_ce)) { - return 0; - } - if (ssa->var_info && ssa_op->op1_use >= 0) { - ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[ssa_op->op1_use].ce = zend_ffi_cdata_ce; - ssa->var_info[ssa_op->op1_use].is_instanceof = 0; - } - if (ssa->var_info && ssa_op->op1_def >= 0) { - ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[ssa_op->op1_def].ce = zend_ffi_cdata_ce; - ssa->var_info[ssa_op->op1_def].is_instanceof = 0; - } - } - - if (ffi_info - && ssa_op->op1_use >= 0 - && (ffi_info[ssa_op->op1_use].type != op1_ffi_type - || (ffi_info[ssa_op->op1_use].info & FFI_TYPE_GUARD))) { - if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { - return 0; - } - ffi_info[ssa_op->op1_use].info &= ~FFI_TYPE_GUARD; - ffi_info[ssa_op->op1_use].type = op1_ffi_type; - if (ssa_op->op1_def >= 0) { - ffi_info[ssa_op->op1_def].info &= ~FFI_TYPE_GUARD; - ffi_info[ssa_op->op1_def].type = op1_ffi_type; - } - } - - if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { - return 0; - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - - if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - cdata_ref = ir_LOAD_A(cdata_ref); - } - - ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - - ZEND_ASSERT(!res_addr); - - switch (el_type->kind) { + switch (ffi_type->kind) { case ZEND_FFI_TYPE_FLOAT: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_INT2F(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_INT2F(jit_Z_LVAL(jit, val_addr))); } else if (val_info == MAY_BE_DOUBLE) { - ir_STORE(ptr, ir_D2F(jit_Z_DVAL(jit, op3_addr))); + ir_STORE(ptr, ir_D2F(jit_Z_DVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_DOUBLE: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_INT2D(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_INT2D(jit_Z_LVAL(jit, val_addr))); } else if (val_info == MAY_BE_DOUBLE) { - ir_STORE(ptr, jit_Z_DVAL(jit, op3_addr)); + ir_STORE(ptr, jit_Z_DVAL(jit, val_addr)); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_UINT8: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_U8(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_TRUNC_U8(jit_Z_LVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_SINT8: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_I8(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_UINT16: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_U16(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_TRUNC_U16(jit_Z_LVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_SINT16: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_I16(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_TRUNC_I16(jit_Z_LVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } @@ -13701,14 +13678,14 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, #ifdef ZEND_ENABLE_ZVAL_LONG64 case ZEND_FFI_TYPE_UINT32: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_U32(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_TRUNC_U32(jit_Z_LVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_SINT32: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_I32(jit_Z_LVAL(jit, op3_addr))); + ir_STORE(ptr, ir_TRUNC_I32(jit_Z_LVAL(jit, val_addr))); } else { ZEND_UNREACHABLE(); } @@ -13716,7 +13693,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, case ZEND_FFI_TYPE_UINT64: case ZEND_FFI_TYPE_SINT64: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, jit_Z_LVAL(jit, op3_addr)); + ir_STORE(ptr, jit_Z_LVAL(jit, val_addr)); } else { ZEND_UNREACHABLE(); } @@ -13725,7 +13702,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, case ZEND_FFI_TYPE_UINT32: case ZEND_FFI_TYPE_SINT32: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, jit_Z_LVAL(jit, op3_addr)); + ir_STORE(ptr, jit_Z_LVAL(jit, val_addr)); } else { ZEND_UNREACHABLE(); } @@ -13737,6 +13714,50 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, return 1; } + +static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr)) { + return 0; + } + + ZEND_ASSERT(!res_addr); + + return 1; +} #endif static int zend_jit_assign_dim_op(zend_jit_ctx *jit, @@ -14133,37 +14154,8 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - if (ssa->var_info - && ssa_op->op1_use >= 0 - && ssa->var_info[ssa_op->op1_use].ce != zend_ffi_cdata_ce) { - if (!zend_jit_class_guard(jit, opline, obj_ref, zend_ffi_cdata_ce)) { - return 0; - } - if (ssa->var_info && ssa_op->op1_use >= 0) { - ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[ssa_op->op1_use].ce = zend_ffi_cdata_ce; - ssa->var_info[ssa_op->op1_use].is_instanceof = 0; - } - if (ssa->var_info && ssa_op->op1_def >= 0) { - ssa->var_info[ssa_op->op1_def].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[ssa_op->op1_def].ce = zend_ffi_cdata_ce; - ssa->var_info[ssa_op->op1_def].is_instanceof = 0; - } - } - - if (ffi_info - && ssa_op->op1_use >= 0 - && (ffi_info[ssa_op->op1_use].type != op1_ffi_type - || (ffi_info[ssa_op->op1_use].info & FFI_TYPE_GUARD))) { - if (!zend_jit_ffi_type_guard(jit, opline, obj_ref, op1_ffi_type)) { - return 0; - } - ffi_info[ssa_op->op1_use].info &= ~FFI_TYPE_GUARD; - ffi_info[ssa_op->op1_use].type = op1_ffi_type; - if (ssa_op->op1_def >= 0) { - ffi_info[ssa_op->op1_def].info &= ~FFI_TYPE_GUARD; - ffi_info[ssa_op->op1_def].type = op1_ffi_type; - } + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; } if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { @@ -14637,6 +14629,53 @@ static int zend_jit_class_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool on_this, + bool delayed_fetch_this, + bool op1_avoid_refcounting, + zend_ffi_field *field, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + uint32_t res_info = RES_INFO(); + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (!zend_jit_ffi_read(jit, field_type, ptr, res_addr)) { + return 0; + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + if (!op1_avoid_refcounting) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + + return 1; +} +#endif + static int zend_jit_fetch_obj(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -15156,6 +15195,44 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit, return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool on_this, + bool delayed_fetch_this, + zend_ffi_field *field, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr)) { + return 0; + } + + ZEND_ASSERT(!res_addr); + + return 1; +} +#endif + static int zend_jit_assign_obj(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -15515,6 +15592,41 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit, return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool on_this, + bool delayed_fetch_this, + zend_ffi_field *field, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + field_type, ptr, val_info, val_addr)) { + return 0; + } + + return 1; +} +#endif + static int zend_jit_assign_obj_op(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 2f903f57945e6..b8ca0beb8e5a8 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4979,6 +4979,29 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } op1_data_info = OP1_DATA_INFO(); CHECK_OP1_DATA_TRACE_TYPE(); +#ifdef HAVE_FFI + if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { + zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + + if (field + && !field->bits + && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM + && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, on_this, delayed_fetch_this, field, + op1_data_info, OP1_DATA_REG_ADDR(), + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + goto done; + } + } +#endif if (!zend_jit_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce, @@ -5062,6 +5085,35 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } op1_data_info = OP1_DATA_INFO(); CHECK_OP1_DATA_TRACE_TYPE(); +#ifdef HAVE_FFI + if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { + zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + + if (field + && !field->bits + && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM + && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_assign_obj(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, on_this, delayed_fetch_this, field, + op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + if ((opline+1)->op1_type == IS_CV + && (ssa_op+1)->op1_def >= 0 + && ssa->vars[(ssa_op+1)->op1_def].alias == NO_ALIAS) { + ssa->var_info[(ssa_op+1)->op1_def].guarded_reference = ssa->var_info[(ssa_op+1)->op1_use].guarded_reference; + } + goto done; + } + } +#endif if (!zend_jit_assign_obj(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, @@ -5118,7 +5170,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM - && op2_info == MAY_BE_LONG) { + && op2_info == MAY_BE_LONG + && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -6174,6 +6227,29 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS; } } +#ifdef HAVE_FFI + if (opline->opcode == ZEND_FETCH_OBJ_R && op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { + zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + + if (field + && !field->bits + && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT + && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_fetch_obj(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, + on_this, delayed_fetch_this, avoid_refcounting, field, + RES_REG_ADDR(), + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + goto done; + } + } +#endif if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, avoid_refcounting, op1_ce, From bfcd3717d891cab10aae4ab9a94832d4bc318f1c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 20 Jun 2024 11:08:20 +0300 Subject: [PATCH 017/101] Fix incorrect 64-bit reads --- ext/opcache/jit/zend_jit_ir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1526bd775c4cc..697b7a9372ab1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12834,11 +12834,11 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, res_type = IS_LONG; break; case ZEND_FFI_TYPE_UINT64: - jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U32(ptr)); + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U64(ptr)); res_type = IS_LONG; break; case ZEND_FFI_TYPE_SINT64: - jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I32(ptr)); + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I64(ptr)); res_type = IS_LONG; break; #else From ad6a6c12544ffb3651ce764e42b36b5c40274bbd Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 20 Jun 2024 12:18:33 +0300 Subject: [PATCH 018/101] Prevent JIT code generation for 64-bit FFI data in 32-bit build --- ext/opcache/jit/zend_jit_ir.c | 8 ++++++++ ext/opcache/jit/zend_jit_trace.c | 27 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 697b7a9372ab1..6efdec261433e 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -18064,6 +18064,10 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && op2_info == MAY_BE_LONG) { return 1; } @@ -18123,6 +18127,10 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && op2_info == MAY_BE_LONG) { return 1; } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index b8ca0beb8e5a8..75e618ee5a4f6 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4779,6 +4779,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && op2_info == MAY_BE_LONG) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); @@ -4988,6 +4992,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && !field->bits && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); @@ -5094,6 +5102,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && !field->bits && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); @@ -5170,6 +5182,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && op2_info == MAY_BE_LONG && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { if (!ffi_info) { @@ -5980,6 +5996,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 +#endif && op2_info == MAY_BE_LONG) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); @@ -6235,7 +6255,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->bits && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM) { + && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM +#if defined(IR_TARGET_X86) + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_UINT64 + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_SINT64 +#endif + ) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } From e0063a8d7f8cbf5498b898b0081bbc8a78eba6db Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 21 Jun 2024 08:19:33 +0300 Subject: [PATCH 019/101] Synch register allocation rules --- ext/opcache/jit/zend_jit_ir.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 6efdec261433e..e78bfc25e7f8a 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -18119,7 +18119,8 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa return 1; } #ifdef HAVE_FFI - if (trace + if ((opline->opcode == ZEND_ASSIGN_DIM || opline->opcode == ZEND_ASSIGN_DIM_OP) + && trace && (trace+1)->op == ZEND_JIT_TRACE_OP1_TYPE && (trace+2)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; @@ -18132,7 +18133,11 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 #endif && op2_info == MAY_BE_LONG) { - return 1; + uint32_t op1_data_info = OP1_DATA_INFO(); + + if (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE) { + return 1; + } } } #endif From 693de69e604710bf06ccdcf3c92d7f37548a9480 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 21 Jun 2024 13:25:38 +0300 Subject: [PATCH 020/101] Support for assignment FFI CData to another CData array/struct --- ext/ffi/ffi.c | 4 +- ext/ffi/php_ffi.h | 2 + ext/opcache/jit/zend_jit.c | 33 ++++++++ ext/opcache/jit/zend_jit_internal.h | 2 + ext/opcache/jit/zend_jit_ir.c | 105 ++++++++++++++++++++------ ext/opcache/jit/zend_jit_trace.c | 90 +++++++++++----------- ext/opcache/jit/zend_jit_vm_helpers.c | 31 +++++++- 7 files changed, 192 insertions(+), 75 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index d52fa00d4ecae..6ef56e728572b 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -103,7 +103,7 @@ static ZEND_FUNCTION(ffi_trampoline); static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type); -static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); +//???static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); #if FFI_CLOSURES static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value); @@ -189,7 +189,7 @@ static bool zend_ffi_func_ptr_are_compatible(zend_ffi_type *dst_type, zend_ffi_t } /* }}} */ -static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */ +PHP_FFI_API bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */ { while (1) { if (dst_type == src_type) { diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 9a66e67a1072a..d9c7e26c0cea3 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -402,4 +402,6 @@ typedef struct _zend_ffi_scope { #define ZEND_FFI_TYPE_IS_OWNED(t) \ (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED) +PHP_FFI_API bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); + #endif /* PHP_FFI_H */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 071dad23b253a..6c037082a5a5d 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -51,6 +51,39 @@ typedef struct zend_jit_ffi_info { zend_ffi_type *type; uint32_t info; } zend_jit_ffi_info; + +static bool zend_jit_ffi_supported_type(zend_ffi_type *type) { +#if defined(IR_TARGET_X86) + if (ZEND_FFI_TYPE(type->kind == ZEND_FFI_TYPE_UINT64) + || ZEND_FFI_TYPE(type->kind == ZEND_FFI_TYPE_SINT64)) { + return false; + } +#endif + return true; +} + +static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type) +{ + dst_type = ZEND_FFI_TYPE(dst_type); + if (!zend_jit_ffi_supported_type(dst_type)) { + return false; + } else if (src_info == MAY_BE_LONG || src_info == MAY_BE_DOUBLE) { + return dst_type->kind < ZEND_FFI_TYPE_POINTER && dst_type->kind != ZEND_FFI_TYPE_VOID; + } else if (src_info == MAY_BE_FALSE || src_info == MAY_BE_TRUE || src_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + return dst_type->kind == ZEND_FFI_TYPE_BOOL; + } else if (src_type) { + if (!zend_jit_ffi_supported_type(src_type)) { + return false; + } + if (src_type->kind >= ZEND_FFI_TYPE_POINTER) { + return false; + } + if (dst_type == src_type || zend_ffi_is_compatible_type(dst_type, src_type)) { + return true; + } + } + return false; +} #endif #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 6556ae33f7a3c..fac2b2e7ed645 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -349,8 +349,10 @@ typedef enum _zend_jit_trace_op { ZEND_JIT_TRACE_VM, ZEND_JIT_TRACE_OP1_TYPE, ZEND_JIT_TRACE_OP2_TYPE, + ZEND_JIT_TRACE_OP3_TYPE, ZEND_JIT_TRACE_OP1_FFI_TYPE, ZEND_JIT_TRACE_OP2_FFI_TYPE, + ZEND_JIT_TRACE_OP3_FFI_TYPE, ZEND_JIT_TRACE_VAL_INFO, ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_DO_ICALL, diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index e78bfc25e7f8a..99569cf656540 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12851,6 +12851,16 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, res_type = IS_LONG; break; #endif + case ZEND_FFI_TYPE_BOOL: + jit_set_Z_TYPE_INFO(jit, res_addr, + ir_ADD_U32(ir_ZEXT_U32(ir_LOAD_U8(ptr)), ir_CONST_U32(IS_FALSE))); + return 1; + case ZEND_FFI_TYPE_CHAR: + jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( + ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), + ir_MUL_L(ir_ZEXT_L(ir_LOAD_U8(ptr)), ir_CONST_LONG(sizeof(void*)))))); + res_type = IS_STRING; + break; default: ZEND_UNREACHABLE(); } @@ -12905,6 +12915,11 @@ static int zend_jit_ffi_guard(zend_jit_ctx *jit, return 1; } +static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) +{ + return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); +} + static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, const zend_op *opline, zend_ssa *ssa, @@ -12931,7 +12946,8 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, return 0; } - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref cdata_ref = jit_FFI_CDATA_PTR(jit, obj_ref); +// ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { cdata_ref = ir_LOAD_A(cdata_ref); @@ -13626,7 +13642,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, zend_ffi_type *ffi_type, ir_ref ptr, uint32_t val_info, - zend_jit_addr val_addr) + zend_jit_addr val_addr, + zend_ffi_type *val_ffi_type) { switch (ffi_type->kind) { case ZEND_FFI_TYPE_FLOAT: @@ -13634,6 +13651,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, ir_STORE(ptr, ir_INT2F(jit_Z_LVAL(jit, val_addr))); } else if (val_info == MAY_BE_DOUBLE) { ir_STORE(ptr, ir_D2F(jit_Z_DVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_F(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13643,20 +13662,39 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, ir_STORE(ptr, ir_INT2D(jit_Z_LVAL(jit, val_addr))); } else if (val_info == MAY_BE_DOUBLE) { ir_STORE(ptr, jit_Z_DVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_D(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } break; + case ZEND_FFI_TYPE_BOOL: + if (val_info == MAY_BE_FALSE) { + ir_STORE(ptr, IR_FALSE); + return 1; + } else if (val_info == MAY_BE_TRUE) { + ir_STORE(ptr, IR_TRUE); + return 1; + } else if (val_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + ir_STORE(ptr, ir_SUB_U8(jit_Z_TYPE(jit, val_addr), ir_CONST_U8(IS_FALSE))); + return 1; + } + ZEND_FALLTHROUGH; case ZEND_FFI_TYPE_UINT8: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_U8(jit_Z_LVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_U8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } break; case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_CHAR: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_I8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13664,6 +13702,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, case ZEND_FFI_TYPE_UINT16: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_U16(jit_Z_LVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_U16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13671,6 +13711,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, case ZEND_FFI_TYPE_SINT16: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_I16(jit_Z_LVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_I16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13679,6 +13721,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, case ZEND_FFI_TYPE_UINT32: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_U32(jit_Z_LVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_U32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13686,6 +13730,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, case ZEND_FFI_TYPE_SINT32: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, ir_TRUNC_I32(jit_Z_LVAL(jit, val_addr))); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13694,6 +13740,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, case ZEND_FFI_TYPE_SINT64: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_I64(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13703,6 +13751,8 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, case ZEND_FFI_TYPE_SINT32: if (val_info == MAY_BE_LONG) { ir_STORE(ptr, jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ir_STORE(ptr, ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); } else { ZEND_UNREACHABLE(); } @@ -13729,6 +13779,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, zend_jit_addr val_def_addr, zend_jit_addr res_addr, zend_ffi_type *op1_ffi_type, + zend_ffi_type *val_ffi_type, zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); @@ -13750,7 +13801,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr)) { + if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr, val_ffi_type)) { return 0; } @@ -14048,6 +14099,16 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, return 0; } break; + case ZEND_FFI_TYPE_BOOL: + type = IR_U8; + ZEND_ASSERT(opcode == ZEND_BW_AND || opcode == ZEND_BW_OR); + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U8(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; case ZEND_FFI_TYPE_UINT8: type = IR_U8; if (op2_info == MAY_BE_LONG) { @@ -14058,6 +14119,7 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, } break; case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_CHAR: type = IR_I8; if (op2_info == MAY_BE_LONG) { op2 = ir_TRUNC_I8(jit_Z_LVAL(jit, op2_addr)); @@ -15211,6 +15273,7 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, zend_jit_addr val_def_addr, zend_jit_addr res_addr, zend_ffi_type *op1_ffi_type, + zend_ffi_type *val_ffi_type, zend_jit_ffi_info *ffi_info) { zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); @@ -15223,7 +15286,7 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); - if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr)) { + if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr, val_ffi_type)) { return 0; } @@ -18062,13 +18125,10 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - && op2_info == MAY_BE_LONG) { + && op2_info == MAY_BE_LONG + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind < ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { return 1; } } @@ -18124,20 +18184,19 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa && (trace+1)->op == ZEND_JIT_TRACE_OP1_TYPE && (trace+2)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; + zend_ffi_type *op3_ffi_type = NULL; + uint32_t op1_data_info = OP1_DATA_INFO(); + + if ((trace+3)->op == ZEND_JIT_TRACE_OP3_TYPE + && (trace+4)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { + op3_ffi_type = (zend_ffi_type*)(trace+4)->ptr; + } + if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - && op2_info == MAY_BE_LONG) { - uint32_t op1_data_info = OP1_DATA_INFO(); - - if (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE) { - return 1; - } + && op2_info == MAY_BE_LONG + && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { + return 1; } } #endif diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 75e618ee5a4f6..00397c8ee3b08 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4394,9 +4394,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par bool op1_indirect; zend_class_entry *op1_ce = NULL; zend_class_entry *op2_ce = NULL; + zend_class_entry *op3_ce = NULL; + (void)op3_ce; #ifdef HAVE_FFI zend_ffi_type *op1_ffi_type = NULL; zend_ffi_type *op2_ffi_type = NULL; + zend_ffi_type *op3_ffi_type = NULL; (void)op2_ffi_type; #endif bool gen_handler = false; @@ -4434,6 +4437,16 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op2_ffi_type = (zend_ffi_type*)(p+1)->ptr; p++; } +#endif + if ((p+1)->op == ZEND_JIT_TRACE_OP3_TYPE) { + op3_ce = (zend_class_entry*)(p+1)->ce; + p++; + } +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { + op3_ffi_type = (zend_ffi_type*)(p+1)->ptr; + p++; + } #endif if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) { val_type = (p+1)->op1_type; @@ -4777,13 +4790,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - && op2_info == MAY_BE_LONG) { + && op2_info == MAY_BE_LONG + && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -4990,13 +4998,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->bits - && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { + && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5100,13 +5102,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->bits - && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { + && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5114,7 +5110,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op1_info, op1_addr, on_this, delayed_fetch_this, field, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, - op1_ffi_type, ffi_info)) { + op1_ffi_type, op3_ffi_type, ffi_info)) { goto jit_failure; } if ((opline+1)->op1_type == IS_CV @@ -5180,14 +5176,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 -#endif && op2_info == MAY_BE_LONG - && (op1_data_info == MAY_BE_LONG || op1_data_info == MAY_BE_DOUBLE)) { + && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5198,7 +5188,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op1_data_info, OP1_DATA_REG_ADDR(), (ctx.ra && (ssa_op+1)->op1_def >= 0) ? OP1_DATA_DEF_REG_ADDR() : 0, (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, - op1_ffi_type, ffi_info)) { + op1_ffi_type, op3_ffi_type, ffi_info)) { goto jit_failure; } } else @@ -5994,13 +5984,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - && op2_info == MAY_BE_LONG) { + && op2_info == MAY_BE_LONG + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind < ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -6254,13 +6241,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->bits - && ZEND_FFI_TYPE(field->type)->kind >= ZEND_FFI_TYPE_FLOAT - && ZEND_FFI_TYPE(field->type)->kind <= ZEND_FFI_TYPE_ENUM -#if defined(IR_TARGET_X86) - && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_UINT64 - && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_SINT64 -#endif - ) { + && ZEND_FFI_TYPE(field->type)->kind < ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(field->type))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -8030,6 +8013,7 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { fprintf(stderr, " op1(%sobject of class %s: ffi_type)", ref, ZSTR_VAL(p->ce->name)); + p++; } else #endif fprintf(stderr, " op1(%sobject of class %s)", ref, @@ -8049,6 +8033,7 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa if ((p+1)->op == ZEND_JIT_TRACE_OP2_FFI_TYPE) { fprintf(stderr, " op2(%sobject of class %s: ffi_type)", ref, ZSTR_VAL(p->ce->name)); + p++; } else #endif fprintf(stderr, " op2(%sobject of class %s)", ref, @@ -8062,8 +8047,21 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa const char *ref = (op3_type & IS_TRACE_INDIRECT) ? ((op3_type & IS_TRACE_REFERENCE) ? "*&" : "*") : ((op3_type & IS_TRACE_REFERENCE) ? "&" : ""); - const char *type = ((op3_type & ~IS_TRACE_INDIRECT) == 0) ? "undef" : zend_get_type_by_const(op3_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)); - fprintf(stderr, " op3(%s%s)", ref, type); + if ((p+1)->op == ZEND_JIT_TRACE_OP3_TYPE) { + p++; +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { + fprintf(stderr, " op3(%sobject of class %s: ffi_type)", ref, + ZSTR_VAL(p->ce->name)); + p++; + } else +#endif + fprintf(stderr, " op3(%sobject of class %s)", ref, + ZSTR_VAL(p->ce->name)); + } else { + const char *type = ((op3_type & ~IS_TRACE_INDIRECT) == 0) ? "undef" : zend_get_type_by_const(op3_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)); + fprintf(stderr, " op3(%s%s)", ref, type); + } } } if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) { diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 92a0c247885db..d192aaf161bdf 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -613,9 +613,9 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, size_t offset; int idx, count; uint8_t trace_flags, op1_type, op2_type, op3_type; - zend_class_entry *ce1, *ce2; + zend_class_entry *ce1, *ce2, *ce3; #ifdef HAVE_FFI - zend_ffi_type *op1_ffi_type, *op2_ffi_type; + zend_ffi_type *op1_ffi_type, *op2_ffi_type, *op3_ffi_type; #endif const zend_op *link_to_enter_opline = NULL; int backtrack_link_to_enter = -1; @@ -686,9 +686,9 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } while (1) { - ce1 = ce2 = NULL; + ce1 = ce2 = ce3 = NULL; #ifdef HAVE_FFI - op1_ffi_type = op2_ffi_type = NULL; + op1_ffi_type = op2_ffi_type = op3_ffi_type = NULL; #endif op1_type = op2_type = op3_type = IS_UNKNOWN; if ((opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) @@ -810,6 +810,20 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, op3_type = Z_TYPE_P(zv); flags |= IS_TRACE_REFERENCE; } + if (Z_TYPE_P(zv) == IS_OBJECT) { + ce3 = Z_OBJCE_P(zv); +#ifdef HAVE_FFI + if (ce3 == zend_ffi_cdata_ce) { + zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); + if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { + zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { + op3_ffi_type = ffi_type; + } + } + } +#endif + } op3_type |= flags; } } @@ -834,6 +848,15 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #endif } + if (ce3) { + TRACE_RECORD(ZEND_JIT_TRACE_OP3_TYPE, 0, ce3); +#ifdef HAVE_FFI + if (op3_ffi_type) { + TRACE_RECORD(ZEND_JIT_TRACE_OP3_FFI_TYPE, 0, op3_ffi_type); + } +#endif + } + switch (opline->opcode) { case ZEND_FETCH_DIM_R: case ZEND_FETCH_DIM_W: From 83e6e8e2b642b04e559a902c07527af6c613594c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 24 Jun 2024 12:02:17 +0300 Subject: [PATCH 021/101] Disable modification of "const" fields --- ext/opcache/jit/zend_jit_trace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 00397c8ee3b08..7ea410f71f0cc 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4997,6 +4997,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (field + && !field->is_const && !field->bits && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { @@ -5101,6 +5102,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (field + && !field->is_const && !field->bits && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { From 7fb3a2cbce4f55f454938199ba707f080fb79440 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 24 Jun 2024 13:15:53 +0300 Subject: [PATCH 022/101] JIT to read FFI variables --- Zend/zend.c | 1 + Zend/zend.h | 1 + ext/ffi/ffi.c | 9 ------ ext/ffi/php_ffi.h | 8 +++++ ext/opcache/jit/zend_jit_internal.h | 1 + ext/opcache/jit/zend_jit_ir.c | 43 +++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 24 +++++++++++++++ ext/opcache/jit/zend_jit_vm_helpers.c | 9 ++++++ 8 files changed, 87 insertions(+), 9 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 8b8b808bf09a2..52cbcaae5edce 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -98,6 +98,7 @@ ZEND_API void (*zend_accel_schedule_restart_hook)(int reason) = NULL; ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size) = NULL; ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL; +ZEND_API zend_class_entry *zend_ffi_ce = NULL; ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str) = NULL; ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl) = NULL; diff --git a/Zend/zend.h b/Zend/zend.h index 68cf5c6f249e5..12f090696d45b 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -408,6 +408,7 @@ extern ZEND_API zend_class_entry *zend_standard_class_def; extern ZEND_API zend_utility_values zend_uv; /* FFI/OPCache interopability API */ +extern ZEND_API zend_class_entry *zend_ffi_ce; extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; typedef struct _zend_ffi_dcl zend_ffi_dcl; diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 6ef56e728572b..a1d20f4af8e21 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -64,14 +64,6 @@ static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"}; #include "ffi_arginfo.h" -typedef struct _zend_ffi { - zend_object std; - DL_HANDLE lib; - HashTable *symbols; - HashTable *tags; - bool persistent; -} zend_ffi; - #define ZEND_FFI_TYPE_MAKE_OWNED(t) \ ((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED)) @@ -80,7 +72,6 @@ typedef struct _zend_ffi { static zend_class_entry *zend_ffi_exception_ce; static zend_class_entry *zend_ffi_parser_exception_ce; -static zend_class_entry *zend_ffi_ce; static zend_class_entry *zend_ffi_ctype_ce; static zend_object_handlers zend_ffi_handlers; diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index d9c7e26c0cea3..85131056a0baf 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -394,6 +394,14 @@ typedef struct _zend_ffi_scope { HashTable *tags; } zend_ffi_scope; +typedef struct _zend_ffi { + zend_object std; + DL_HANDLE lib; + HashTable *symbols; + HashTable *tags; + bool persistent; +} zend_ffi; + #define ZEND_FFI_TYPE_OWNED (1<<0) #define ZEND_FFI_TYPE(t) \ diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index fac2b2e7ed645..3a5206405f1c3 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -353,6 +353,7 @@ typedef enum _zend_jit_trace_op { ZEND_JIT_TRACE_OP1_FFI_TYPE, ZEND_JIT_TRACE_OP2_FFI_TYPE, ZEND_JIT_TRACE_OP3_FFI_TYPE, + ZEND_JIT_TRACE_OP1_FFI_SYMBOLS, ZEND_JIT_TRACE_VAL_INFO, ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_DO_ICALL, diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 99569cf656540..9e78e7dc5cda9 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14736,6 +14736,49 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, return 1; } + +static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool on_this, + bool delayed_fetch_this, + bool op1_avoid_refcounting, + zend_ffi_symbol *sym, + zend_jit_addr res_addr/*???, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info*/) +{ + uint32_t res_info = RES_INFO(); + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); +//??? ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + +//??? if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { +//??? return 0; +//??? } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { + return 0; + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + if (!op1_avoid_refcounting) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + + return 1; +} #endif static int zend_jit_fetch_obj(zend_jit_ctx *jit, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 7ea410f71f0cc..ceea5de834c93 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4400,6 +4400,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par zend_ffi_type *op1_ffi_type = NULL; zend_ffi_type *op2_ffi_type = NULL; zend_ffi_type *op3_ffi_type = NULL; + HashTable *op1_ffi_symbols = NULL; (void)op2_ffi_type; #endif bool gen_handler = false; @@ -4426,6 +4427,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { op1_ffi_type = (zend_ffi_type*)(p+1)->ptr; p++; + } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { + op1_ffi_symbols = (HashTable*)(p+1)->ptr; + p++; } #endif if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) { @@ -6258,6 +6262,22 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (opline->opcode == ZEND_FETCH_OBJ_R && op1_ffi_symbols) { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + if (sym + && (sym->kind == ZEND_FFI_SYM_VAR || sym->kind == ZEND_FFI_SYM_CONST) + && ZEND_FFI_TYPE(sym->type)->kind < ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(sym->type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(sym->type))) { + if (!zend_jit_ffi_fetch_sym(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, + on_this, delayed_fetch_this, avoid_refcounting, sym, + RES_REG_ADDR())) { + goto jit_failure; + } + goto done; + } } #endif if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op, @@ -8016,6 +8036,10 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa fprintf(stderr, " op1(%sobject of class %s: ffi_type)", ref, ZSTR_VAL(p->ce->name)); p++; + } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { + fprintf(stderr, " op1(%sobject of class %s: ffi_symbols)", ref, + ZSTR_VAL(p->ce->name)); + p++; } else #endif fprintf(stderr, " op1(%sobject of class %s)", ref, diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index d192aaf161bdf..2a4b3db75d510 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -616,6 +616,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, zend_class_entry *ce1, *ce2, *ce3; #ifdef HAVE_FFI zend_ffi_type *op1_ffi_type, *op2_ffi_type, *op3_ffi_type; + HashTable *op1_ffi_symbols; #endif const zend_op *link_to_enter_opline = NULL; int backtrack_link_to_enter = -1; @@ -689,6 +690,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, ce1 = ce2 = ce3 = NULL; #ifdef HAVE_FFI op1_ffi_type = op2_ffi_type = op3_ffi_type = NULL; + op1_ffi_symbols = NULL; #endif op1_type = op2_type = op3_type = IS_UNKNOWN; if ((opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) @@ -722,6 +724,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, op1_ffi_type = ffi_type; } } + } else if (ce1 == zend_ffi_ce) { + zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(zv); + if (ffi->persistent && ffi->symbols) { + op1_ffi_symbols = ffi->symbols; + } } #endif } else if (Z_TYPE_P(zv) == IS_ARRAY) { @@ -835,6 +842,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #ifdef HAVE_FFI if (op1_ffi_type) { TRACE_RECORD(ZEND_JIT_TRACE_OP1_FFI_TYPE, 0, op1_ffi_type); + } else if (op1_ffi_symbols) { + TRACE_RECORD(ZEND_JIT_TRACE_OP1_FFI_SYMBOLS, 0, op1_ffi_symbols); } #endif } From e2ee57cb168bcb087ca5e7be2b72fb974a723c7a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 24 Jun 2024 22:07:40 +0300 Subject: [PATCH 023/101] Add FFI class and cdef guards --- ext/opcache/jit/zend_jit.c | 10 +++-- ext/opcache/jit/zend_jit_ir.c | 70 ++++++++++++++++++++++++++++---- ext/opcache/jit/zend_jit_trace.c | 6 ++- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 6c037082a5a5d..c2eb8da7fbd1c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -45,11 +45,15 @@ #if HAVE_FFI # include "ext/ffi/php_ffi.h" -# define FFI_TYPE_GUARD (1<<0) +# define FFI_TYPE_GUARD (1<<0) +# define FFI_SYMBOLS_GUARD (1<<1) typedef struct zend_jit_ffi_info { - zend_ffi_type *type; - uint32_t info; + union { + zend_ffi_type *type; + HashTable *symbols; + }; + uint32_t info; } zend_jit_ffi_info; static bool zend_jit_ffi_supported_type(zend_ffi_type *type) { diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 9e78e7dc5cda9..6b8f7089b04f1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -12915,6 +12915,63 @@ static int zend_jit_ffi_guard(zend_jit_ctx *jit, return 1; } +static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + zend_jit_addr addr, + HashTable *ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + ir_ref ref = IR_UNUSED; + + if (ssa->var_info + && use >= 0 + && ssa->var_info[use].ce != zend_ffi_ce) { + ref = jit_Z_PTR(jit, addr); + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_ce)) { + return 0; + } + ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[use].ce = zend_ffi_ce; + ssa->var_info[use].is_instanceof = 0; + if (def >= 0) { + ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[def].ce = zend_ffi_ce; + ssa->var_info[def].is_instanceof = 0; + } + } + + if (ffi_info + && use >= 0 + && (ffi_info[use].symbols != ffi_symbols + || (ffi_info[use].info & FFI_SYMBOLS_GUARD))) { + if (!ref) { + ref = jit_Z_PTR(jit, addr); + } + + int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(ref, offsetof(zend_ffi, symbols))), ir_CONST_ADDR(ffi_symbols)), + ir_CONST_ADDR(exit_addr)); + + ffi_info[use].info &= ~FFI_SYMBOLS_GUARD; + ffi_info[use].symbols = ffi_symbols; + if (def >= 0) { + ffi_info[def].info &= ~FFI_SYMBOLS_GUARD; + ffi_info[def].symbols = ffi_symbols; + } + } + + return 1; +} + static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) { return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); @@ -14749,17 +14806,16 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, bool delayed_fetch_this, bool op1_avoid_refcounting, zend_ffi_symbol *sym, - zend_jit_addr res_addr/*???, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info*/) + zend_jit_addr res_addr, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) { uint32_t res_info = RES_INFO(); zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); -//??? ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); -//??? if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { -//??? return 0; -//??? } + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } ir_ref ptr = ir_CONST_ADDR(sym->addr); if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index ceea5de834c93..8d1b53d58bf03 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6270,10 +6270,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && ZEND_FFI_TYPE(sym->type)->kind < ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(sym->type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(sym->type))) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } if (!zend_jit_ffi_fetch_sym(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, avoid_refcounting, sym, - RES_REG_ADDR())) { + RES_REG_ADDR(), + op1_ffi_symbols, ffi_info)) { goto jit_failure; } goto done; From 7fc144a5a58ff77b821ef7fc0a317e6402a5654d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 24 Jun 2024 23:03:05 +0300 Subject: [PATCH 024/101] JIT to write to FFI variables --- ext/opcache/jit/zend_jit_ir.c | 64 ++++++++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 40 ++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 6b8f7089b04f1..05d1dbed8c679 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -15393,6 +15393,40 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, return 1; } + +static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool on_this, + bool delayed_fetch_this, + zend_ffi_symbol *sym, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + HashTable *op1_ffi_symbols, + zend_ffi_type *val_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type)) { + return 0; + } + + ZEND_ASSERT(!res_addr); + + return 1; +} #endif static int zend_jit_assign_obj(zend_jit_ctx *jit, @@ -15787,6 +15821,36 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, return 1; } + +static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool on_this, + bool delayed_fetch_this, + zend_ffi_symbol *sym, + uint32_t val_info, + zend_jit_addr val_addr, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + sym_type, ptr, val_info, val_addr)) { + return 0; + } + + return 1; +} #endif static int zend_jit_assign_obj_op(zend_jit_ctx *jit, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 8d1b53d58bf03..c6707e2f07a79 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5015,6 +5015,23 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (op1_ffi_symbols) { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + if (sym + && sym->kind == ZEND_FFI_SYM_VAR + && zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_assign_sym_op(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, on_this, delayed_fetch_this, sym, + op1_data_info, OP1_DATA_REG_ADDR(), + op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + goto done; + } } #endif if (!zend_jit_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op, @@ -5126,6 +5143,29 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (op1_ffi_symbols) { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + if (sym + && sym->kind == ZEND_FFI_SYM_VAR + && zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_assign_sym(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, on_this, delayed_fetch_this, sym, + op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_symbols, op3_ffi_type, ffi_info)) { + goto jit_failure; + } + if ((opline+1)->op1_type == IS_CV + && (ssa_op+1)->op1_def >= 0 + && ssa->vars[(ssa_op+1)->op1_def].alias == NO_ALIAS) { + ssa->var_info[(ssa_op+1)->op1_def].guarded_reference = ssa->var_info[(ssa_op+1)->op1_use].guarded_reference; + } + goto done; + } } #endif if (!zend_jit_assign_obj(&ctx, opline, op_array, ssa, ssa_op, From f6c3acdd4ea6349cf8ccdfec7a6371fbcb7f6ecb Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 25 Jun 2024 11:22:32 +0300 Subject: [PATCH 025/101] Fix 32-bit build --- ext/opcache/jit/zend_jit.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index c2eb8da7fbd1c..f5b146c73fd0b 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -56,13 +56,14 @@ typedef struct zend_jit_ffi_info { uint32_t info; } zend_jit_ffi_info; -static bool zend_jit_ffi_supported_type(zend_ffi_type *type) { -#if defined(IR_TARGET_X86) - if (ZEND_FFI_TYPE(type->kind == ZEND_FFI_TYPE_UINT64) - || ZEND_FFI_TYPE(type->kind == ZEND_FFI_TYPE_SINT64)) { - return false; +static bool zend_jit_ffi_supported_type(zend_ffi_type *type) +{ + if (sizeof(void*) == 4) { + if (ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_UINT64 + || ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_SINT64) { + return false; + } } -#endif return true; } From 0ffbb8d86cb7c179239be7b5d7924832a1833173 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 25 Jun 2024 11:51:02 +0300 Subject: [PATCH 026/101] Fix Windows build --- ext/opcache/jit/zend_jit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index f5b146c73fd0b..6a905b6f5badf 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -83,7 +83,10 @@ static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, if (src_type->kind >= ZEND_FFI_TYPE_POINTER) { return false; } - if (dst_type == src_type || zend_ffi_is_compatible_type(dst_type, src_type)) { + if (dst_type == src_type +// TODO: calls between shared extensions doesn't work on Windows +// || zend_ffi_is_compatible_type(dst_type, src_type) + ) { return true; } } From 066a523539f0c2bea4ddfabdc55bdac78a00e347 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 27 Jun 2024 19:34:18 +0300 Subject: [PATCH 027/101] JIT support for FFI calls (incomplete) --- ext/opcache/jit/zend_jit.c | 30 ++ ext/opcache/jit/zend_jit_helpers.c | 34 +++ ext/opcache/jit/zend_jit_internal.h | 4 + ext/opcache/jit/zend_jit_ir.c | 390 +++++++++++++++++++++++++- ext/opcache/jit/zend_jit_trace.c | 88 +++++- ext/opcache/jit/zend_jit_vm_helpers.c | 1 + 6 files changed, 538 insertions(+), 9 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 6a905b6f5badf..179e1e1d1c080 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -67,6 +67,36 @@ static bool zend_jit_ffi_supported_type(zend_ffi_type *type) return true; } +static bool zend_jit_ffi_supported_func(zend_ffi_type *type) +{ + zend_ffi_type *t; + + type = ZEND_FFI_TYPE(type); + + if (type->func.abi != ZEND_FFI_ABI_CDECL + && type->func.abi != ZEND_FFI_ABI_FASTCALL) { + return false; + } + + t = ZEND_FFI_TYPE(type->func.ret_type); + if (t->kind == ZEND_FFI_TYPE_STRUCT + || !zend_jit_ffi_supported_type(t)) { + return false; + } + + if (type->func.args) { + ZEND_HASH_PACKED_FOREACH_PTR(type->func.args, t) { + t = ZEND_FFI_TYPE(t); + if (t->kind == ZEND_FFI_TYPE_STRUCT + || !zend_jit_ffi_supported_type(t)) { + return false; + } + } ZEND_HASH_FOREACH_END(); + } + + return true; +} + static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type) { dst_type = ZEND_FFI_TYPE(dst_type); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 56b8b2d69cac2..12f705e3a4e91 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3362,3 +3362,37 @@ static zend_string* ZEND_FASTCALL zend_jit_rope_end(zend_string **rope, uint32_t *target = '\0'; return ret; } + +#ifdef HAVE_FFI +static void ZEND_FASTCALL zend_jit_zval_string(zval *zv, const char *str) +{ + if (str) { + ZVAL_STRING(zv, str); + } else { + ZVAL_NULL(zv); + } +} + +static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, void *ptr) +{ + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER); + if (ptr) { + zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); + + // inlined zend_ffi_object_init() + GC_SET_REFCOUNT(&cdata->std, 1); + GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + cdata->std.ce = zend_ffi_cdata_ce; + cdata->std.handlers = zend_ffi_cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + cdata->std.properties = NULL; + zend_objects_store_put(&cdata->std); + cdata->type = type; + cdata->flags = 0; + cdata->ptr = (void*)&cdata->ptr_holder; + cdata->ptr_holder = ptr; + ZVAL_OBJ(zv, &cdata->std); + } else { + ZVAL_NULL(zv); + } +} +#endif diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 3a5206405f1c3..4329baaca1410 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -574,6 +574,8 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_MASK_CLOSURE_CALL 0x00000200 #define TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS 0x00000400 +#define TRACE_FRAME_MASK_FFI 0x00000800 + #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ zend_jit_trace_stack_frame *_frame = (frame); \ @@ -612,6 +614,8 @@ struct _zend_jit_trace_stack_frame { ((frame)->_info & TRACE_FRAME_MASK_CLOSURE_CALL) #define TRACE_FRAME_ALWAYS_RELEASE_THIS(frame) \ ((frame)->_info & TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS) +#define TRACE_FRAME_FFI(frame) \ + ((frame)->_info & TRACE_FRAME_MASK_FFI) #define TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(frame) do { \ (frame)->_info |= (0xffffu << TRACE_FRAME_SHIFT_NUM_ARGS); \ diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 05d1dbed8c679..1e8f2c08e36ac 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3100,6 +3100,11 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_rope_end); REGISTER_HELPER(zend_fcall_interrupt); +#ifdef HAVE_FFI + REGISTER_HELPER(zend_jit_zval_string); + REGISTER_HELPER(zend_jit_zval_ffi_ptr); +#endif + #ifndef ZTS REGISTER_DATA(EG(current_execute_data)); REGISTER_DATA(EG(exception)); @@ -10611,6 +10616,386 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen return 1; } +#ifdef HAVE_FFI +static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) +{ + return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); +} + +static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool on_this, + bool delayed_fetch_this, + zend_ffi_symbol *sym, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + return 1; +} + +static int zend_jit_ffi_send_val(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + zend_jit_addr op1_def_addr, + zend_ffi_type *op1_ffi_type) +{ + zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; + zend_jit_trace_stack *stack = call->stack; + zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; + zend_ffi_type *type; + ir_ref ref = IR_UNUSED; + + ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); + type = ZEND_FFI_TYPE(sym->type); + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); + if (type->attr & ZEND_FFI_ATTR_VARIADIC) { + ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) >= zend_hash_num_elements(type->func.args)); + } else { + ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) == zend_hash_num_elements(type->func.args)); + } + ZEND_ASSERT(opline->op2.num > 0 && opline->op2.num <= TRACE_FRAME_NUM_ARGS(call)); + + if (opline->op2.num - 1 < zend_hash_num_elements(type->func.args)) { + type = zend_hash_index_find_ptr(type->func.args, opline->op2.num - 1); + type = ZEND_FFI_TYPE(type); + + switch (type->kind) { + case ZEND_FFI_TYPE_FLOAT: + if (op1_info == MAY_BE_LONG) { + ref = ir_INT2F((jit_Z_LVAL(jit, op1_addr))); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = ir_D2F(jit_Z_DVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_DOUBLE: + if (op1_info == MAY_BE_LONG) { + ref = ir_INT2D((jit_Z_LVAL(jit, op1_addr))); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = jit_Z_DVAL(jit, op1_addr); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT8: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_U8(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT8: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_I8(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT16: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_U16(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT16: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_I16(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_U32(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT32: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_I32(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else { + ZEND_UNREACHABLE(); + } + break; +#else + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else { + ZEND_UNREACHABLE(); + } + break; +#endif + case ZEND_FFI_TYPE_BOOL: + if (op1_info == MAY_BE_NULL || op1_info == MAY_BE_FALSE) { + ref = IR_FALSE; + } else if (op1_info == MAY_BE_TRUE) { + ref = IR_TRUE; + } else if (op1_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + ref = ir_SUB_U8(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_FALSE)); + } else if (op1_info == MAY_BE_LONG) { + ref = ir_NE(jit_Z_LVAL(jit, op1_addr), ir_CONST_LONG(0)); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = ir_NE(jit_Z_DVAL(jit, op1_addr), ir_CONST_DOUBLE(0.0)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_CHAR: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_C(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_POINTER: + if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING + && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); + } else if (op1_ffi_type + && op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type)) { + ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); + } else if (op1_ffi_type + && op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY + && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type)) { + ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + default: + ZEND_UNREACHABLE(); + } + } else { + if (op1_info == MAY_BE_NULL) { + ref = IR_NULL; + } else if (op1_info == MAY_BE_FALSE) { + ref = IR_FALSE; + } else if (op1_info == MAY_BE_TRUE) { + ref = IR_TRUE; + } else if (op1_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + ref = ir_SUB_U8(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_FALSE)); + } else if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = jit_Z_DVAL(jit, op1_addr); + } else if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) { + ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); + } else { + ZEND_UNREACHABLE(); + } + } + + SET_STACK_REF(stack, opline->op2.num - 1, ref); + + return 1; +} + +static int zend_jit_ffi_do_call_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + zend_jit_addr res_addr) +{ + zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; + zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; + uint32_t i, num_args; + zend_ffi_type *type; + ir_type ret_type = IR_VOID; + ir_ref ref = IR_UNUSED; + + ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); + type = ZEND_FFI_TYPE(sym->type); + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); + + switch (ZEND_FFI_TYPE(type->func.ret_type)->kind) { + case ZEND_FFI_TYPE_VOID: + ret_type = IR_VOID; + break; + case ZEND_FFI_TYPE_FLOAT: + ret_type = IR_FLOAT; + break; + case ZEND_FFI_TYPE_DOUBLE: + ret_type = IR_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + ret_type = IR_U8; + break; + case ZEND_FFI_TYPE_SINT8: + ret_type = IR_I8; + break; + case ZEND_FFI_TYPE_UINT16: + ret_type = IR_U16; + break; + case ZEND_FFI_TYPE_SINT16: + ret_type = IR_I16; + break; + case ZEND_FFI_TYPE_UINT32: + ret_type = IR_U32; + break; + case ZEND_FFI_TYPE_SINT32: + ret_type = IR_I32; + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT64: + ret_type = IR_U64; + break; + case ZEND_FFI_TYPE_SINT64: + ret_type = IR_I64; + break; +#endif + case ZEND_FFI_TYPE_BOOL: + ret_type = IR_BOOL; + break; + case ZEND_FFI_TYPE_CHAR: + ret_type = IR_CHAR; + break; + case ZEND_FFI_TYPE_POINTER: + ret_type = IR_ADDR; + break; + default: + ZEND_UNREACHABLE(); + } + + num_args = TRACE_FRAME_NUM_ARGS(call); + if (num_args) { + ir_ref *args = alloca(sizeof(ir_ref) * num_args); + zend_jit_trace_stack *stack = call->stack; + + for (i = 0; i < num_args; i++) { + args[i] = STACK_REF(stack, i); + } + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + ref = ir_CALL_N(ret_type, ir_CONST_FC_FUNC(sym->addr), num_args, args); + } else { + ref = ir_CALL_N(ret_type, ir_CONST_FUNC(sym->addr), num_args, args); + } + } else { + ZEND_ASSERT(!type->func.args); + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + ref = ir_CALL(ret_type, ir_CONST_FC_FUNC(sym->addr)); + } else { + ref = ir_CALL(ret_type, ir_CONST_FUNC(sym->addr)); + } + } + + if (RETURN_VALUE_USED(opline)) { + zend_ffi_type *ret_type = ZEND_FFI_TYPE(type->func.ret_type); + uint32_t res_type = IS_UNDEF; + + switch (ret_type->kind) { + case ZEND_FFI_TYPE_VOID: + res_type = IS_NULL; + break; + case ZEND_FFI_TYPE_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ref); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT8: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT16: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT16: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT64: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT64: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; +#else + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; +#endif + case ZEND_FFI_TYPE_BOOL: + jit_set_Z_TYPE_INFO(jit, res_addr, + ir_ADD_U32(ir_ZEXT_U32(ref), ir_CONST_U32(IS_FALSE))); + return 1; + case ZEND_FFI_TYPE_CHAR: + jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( + ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), + ir_MUL_L(ir_ZEXT_L(ref), ir_CONST_LONG(sizeof(void*)))))); + res_type = IS_STRING; + break; + case ZEND_FFI_TYPE_POINTER: + if ((ret_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(ret_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); + return 1; + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ret_type), ref); + return 1; + } + break; + default: + ZEND_UNREACHABLE(); + } + + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + } + + return 1; +} +#endif + static int zend_jit_constructor(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, int call_level, int next_block) { ir_ref if_skip_constructor = jit_IF_ex(jit, jit_CMP_IP(jit, IR_NE, opline), next_block); @@ -12972,11 +13357,6 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, return 1; } -static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) -{ - return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); -} - static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, const zend_op *opline, zend_ssa *ssa, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index c6707e2f07a79..3e667d58566bc 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -3916,6 +3916,9 @@ static bool zend_jit_trace_next_is_send_result(const zend_op *oplin || ((opline+1)->opcode == ZEND_SEND_VAL_EX && frame && frame->call +#ifdef HAVE_FFI + && !TRACE_FRAME_FFI(frame->call) +#endif && frame->call->func && !ARG_MUST_BE_SENT_BY_REF(frame->call->func, (opline+1)->op2.num))) && (opline+1)->op1_type == IS_TMP_VAR @@ -4123,6 +4126,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par uint32_t frame_flags = 0; #ifdef HAVE_FFI zend_jit_ffi_info *ffi_info = NULL; + zend_ffi_symbol *frame_ffi_sym = NULL; #endif JIT_G(current_trace) = trace_buffer; @@ -4458,6 +4462,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } frame_flags = 0; +#ifdef HAVE_FFI + frame_ffi_sym = NULL; +#endif switch (opline->opcode) { case ZEND_INIT_FCALL: @@ -5446,6 +5453,17 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } op1_info = OP1_INFO(); CHECK_OP1_TRACE_TYPE(); +#ifdef HAVE_FFI + if (JIT_G(current_frame) + && JIT_G(current_frame)->call + && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + if (!zend_jit_ffi_send_val(&ctx, opline, op_array, ssa, ssa_op, + op1_info, OP1_REG_ADDR(), 0, op1_ffi_type)) { + goto jit_failure; + } + goto done; + } +#endif if (!zend_jit_send_val(&ctx, opline, op1_info, OP1_REG_ADDR())) { goto jit_failure; @@ -5498,6 +5516,17 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } op1_info = OP1_INFO(); CHECK_OP1_TRACE_TYPE(); +#ifdef HAVE_FFI + if (JIT_G(current_frame) + && JIT_G(current_frame)->call + && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + if (!zend_jit_ffi_send_val(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_def_addr, op1_ffi_type)) { + goto jit_failure; + } + goto done; + } +#endif if (!zend_jit_send_var(&ctx, opline, op_array, op1_info, op1_addr, op1_def_addr)) { goto jit_failure; @@ -5548,6 +5577,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par TRACE_FRAME_SET_LAST_SEND_UNKNOWN(JIT_G(current_frame)->call); break; } +#ifdef HAVE_FFI + if (TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + /* FFI arguments alwyas sent by value ??? */ + goto done; + } +#endif if (!zend_jit_check_func_arg(&ctx, opline)) { goto jit_failure; } @@ -5565,6 +5600,16 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_DO_ICALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: +#ifdef HAVE_FFI + if (JIT_G(current_frame) + && JIT_G(current_frame)->call + && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + if (!zend_jit_ffi_do_call_sym(&ctx, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) { + goto jit_failure; + } + goto done; + } +#endif if (!zend_jit_do_fcall(&ctx, opline, op_array, op_array_ssa, frame->call_level, -1, p + 1)) { goto jit_failure; } @@ -6281,7 +6326,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } #ifdef HAVE_FFI - if (opline->opcode == ZEND_FETCH_OBJ_R && op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { + if ((opline->opcode == ZEND_FETCH_OBJ_R + || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) + && op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, Z_STR_P(RT_CONSTANT(opline, opline->op2))); @@ -6302,11 +6349,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } - } else if (opline->opcode == ZEND_FETCH_OBJ_R && op1_ffi_symbols) { + } else if ((opline->opcode == ZEND_FETCH_OBJ_R + || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) + && op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym - && (sym->kind == ZEND_FFI_SYM_VAR || sym->kind == ZEND_FFI_SYM_CONST) + && sym->kind == ZEND_FFI_SYM_VAR && ZEND_FFI_TYPE(sym->type)->kind < ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(sym->type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(sym->type))) { @@ -6614,6 +6663,28 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS; } } +#ifdef HAVE_FFI + if (op1_ffi_symbols) { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + if (sym + && sym->kind == ZEND_FFI_SYM_FUNC + && zend_jit_ffi_supported_func(ZEND_FFI_TYPE(sym->type))) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_init_call_sym(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, + on_this, delayed_fetch_this, sym, + op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_sym = sym; + goto done; + } + } +#endif frame_flags = TRACE_FRAME_MASK_NESTED; if (!zend_jit_init_method_call(&ctx, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, @@ -6656,6 +6727,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_SEND_UNPACK: if (JIT_G(current_frame) && JIT_G(current_frame)->call) { +#ifdef HAVE_FFI + if (TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + goto jit_failure; + } +#endif TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(JIT_G(current_frame)->call); } break; @@ -7238,6 +7314,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par call = top; TRACE_FRAME_INIT(call, p->func, frame_flags, num_args); + if (TRACE_FRAME_FFI(call)) { + ZEND_ASSERT(frame_ffi_sym != NULL); + call->call_opline = (const zend_op*)(void*)frame_ffi_sym; + } call->prev = frame->call; if (!(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) { TRACE_FRAME_SET_LAST_SEND_BY_VAL(call); @@ -8220,7 +8300,7 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa level, ' ', (p->func && p->func->common.scope) ? ZSTR_VAL(p->func->common.scope->name) : "", (p->func && p->func->common.scope) ? "::" : "", - (p->func && p->func->common.function_name) ? ZSTR_VAL(p->func->common.function_name) : "???"); + (p->func && p->func->common.function_name) ? ZSTR_VAL(p->func->common.function_name) : "???"); } else { fprintf(stderr, " %*c>skip\n", level, ' '); diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 2a4b3db75d510..5791adb630f57 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1219,6 +1219,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, func = NULL; ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_MEGAMORPHIC); } + if (!func) { info = ZEND_JIT_TRACE_NUM_ARGS_INFO(ZEND_CALL_NUM_ARGS(EX(call))); } From fe2de1030afac870af98f51541cbd83192fc37dc Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 27 Jun 2024 21:22:38 +0300 Subject: [PATCH 028/101] Fix memory leaks --- ext/opcache/jit/zend_jit_ir.c | 20 ++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 8 ++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1e8f2c08e36ac..3030beeb661f3 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -15744,6 +15744,7 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, + bool op1_indirect, bool on_this, bool delayed_fetch_this, zend_ffi_field *field, @@ -15771,6 +15772,10 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr); + if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + return 1; } @@ -15781,6 +15786,7 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, + bool op1_indirect, bool on_this, bool delayed_fetch_this, zend_ffi_symbol *sym, @@ -15805,6 +15811,10 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr); + if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + return 1; } #endif @@ -16176,6 +16186,7 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, + bool op1_indirect, bool on_this, bool delayed_fetch_this, zend_ffi_field *field, @@ -16199,6 +16210,10 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, return 0; } + if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + return 1; } @@ -16209,6 +16224,7 @@ static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, + bool op1_indirect, bool on_this, bool delayed_fetch_this, zend_ffi_symbol *sym, @@ -16229,6 +16245,10 @@ static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, return 0; } + if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + return 1; } #endif diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 3e667d58566bc..2ebaa6f56ed52 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5015,7 +5015,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, on_this, delayed_fetch_this, field, + op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, field, op1_data_info, OP1_DATA_REG_ADDR(), op1_ffi_type, ffi_info)) { goto jit_failure; @@ -5032,7 +5032,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_sym_op(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, on_this, delayed_fetch_this, sym, + op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, sym, op1_data_info, OP1_DATA_REG_ADDR(), op1_ffi_symbols, ffi_info)) { goto jit_failure; @@ -5137,7 +5137,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_obj(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, on_this, delayed_fetch_this, field, + op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, field, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, op1_ffi_type, op3_ffi_type, ffi_info)) { @@ -5160,7 +5160,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_sym(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, on_this, delayed_fetch_this, sym, + op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, sym, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, op1_ffi_symbols, op3_ffi_type, ffi_info)) { From 861c6c68b0797f6e42387218344ea677176a9fa5 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 27 Jun 2024 21:24:39 +0300 Subject: [PATCH 029/101] iFix build wthout FFI --- ext/opcache/jit/zend_jit_trace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 2ebaa6f56ed52..34a6c8ec39c47 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -7314,10 +7314,12 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par call = top; TRACE_FRAME_INIT(call, p->func, frame_flags, num_args); +#ifdef HAVE_FFI if (TRACE_FRAME_FFI(call)) { ZEND_ASSERT(frame_ffi_sym != NULL); call->call_opline = (const zend_op*)(void*)frame_ffi_sym; } +#endif call->prev = frame->call; if (!(p->info & ZEND_JIT_TRACE_FAKE_INIT_CALL)) { TRACE_FRAME_SET_LAST_SEND_BY_VAL(call); From 779125535b990b57a36a1883317b1780402d26e3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 27 Jun 2024 21:35:26 +0300 Subject: [PATCH 030/101] Make predefined types pesistent --- ext/ffi/ffi.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index a1d20f4af8e21..aa2014724ee24 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -5615,25 +5615,25 @@ ZEND_MINFO_FUNCTION(ffi) } /* }}} */ -static const zend_ffi_type zend_ffi_type_void = {.kind=ZEND_FFI_TYPE_VOID, .size=1, .align=1}; -static const zend_ffi_type zend_ffi_type_char = {.kind=ZEND_FFI_TYPE_CHAR, .size=1, .align=_Alignof(char)}; -static const zend_ffi_type zend_ffi_type_bool = {.kind=ZEND_FFI_TYPE_BOOL, .size=1, .align=_Alignof(uint8_t)}; -static const zend_ffi_type zend_ffi_type_sint8 = {.kind=ZEND_FFI_TYPE_SINT8, .size=1, .align=_Alignof(int8_t)}; -static const zend_ffi_type zend_ffi_type_uint8 = {.kind=ZEND_FFI_TYPE_UINT8, .size=1, .align=_Alignof(uint8_t)}; -static const zend_ffi_type zend_ffi_type_sint16 = {.kind=ZEND_FFI_TYPE_SINT16, .size=2, .align=_Alignof(int16_t)}; -static const zend_ffi_type zend_ffi_type_uint16 = {.kind=ZEND_FFI_TYPE_UINT16, .size=2, .align=_Alignof(uint16_t)}; -static const zend_ffi_type zend_ffi_type_sint32 = {.kind=ZEND_FFI_TYPE_SINT32, .size=4, .align=_Alignof(int32_t)}; -static const zend_ffi_type zend_ffi_type_uint32 = {.kind=ZEND_FFI_TYPE_UINT32, .size=4, .align=_Alignof(uint32_t)}; -static const zend_ffi_type zend_ffi_type_sint64 = {.kind=ZEND_FFI_TYPE_SINT64, .size=8, .align=_Alignof(int64_t)}; -static const zend_ffi_type zend_ffi_type_uint64 = {.kind=ZEND_FFI_TYPE_UINT64, .size=8, .align=_Alignof(uint64_t)}; -static const zend_ffi_type zend_ffi_type_float = {.kind=ZEND_FFI_TYPE_FLOAT, .size=sizeof(float), .align=_Alignof(float)}; -static const zend_ffi_type zend_ffi_type_double = {.kind=ZEND_FFI_TYPE_DOUBLE, .size=sizeof(double), .align=_Alignof(double)}; +static const zend_ffi_type zend_ffi_type_void = {.kind=ZEND_FFI_TYPE_VOID, .size=1, .align=1, .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_char = {.kind=ZEND_FFI_TYPE_CHAR, .size=1, .align=_Alignof(char), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_bool = {.kind=ZEND_FFI_TYPE_BOOL, .size=1, .align=_Alignof(uint8_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_sint8 = {.kind=ZEND_FFI_TYPE_SINT8, .size=1, .align=_Alignof(int8_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_uint8 = {.kind=ZEND_FFI_TYPE_UINT8, .size=1, .align=_Alignof(uint8_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_sint16 = {.kind=ZEND_FFI_TYPE_SINT16, .size=2, .align=_Alignof(int16_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_uint16 = {.kind=ZEND_FFI_TYPE_UINT16, .size=2, .align=_Alignof(uint16_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_sint32 = {.kind=ZEND_FFI_TYPE_SINT32, .size=4, .align=_Alignof(int32_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_uint32 = {.kind=ZEND_FFI_TYPE_UINT32, .size=4, .align=_Alignof(uint32_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_sint64 = {.kind=ZEND_FFI_TYPE_SINT64, .size=8, .align=_Alignof(int64_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_uint64 = {.kind=ZEND_FFI_TYPE_UINT64, .size=8, .align=_Alignof(uint64_t), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_float = {.kind=ZEND_FFI_TYPE_FLOAT, .size=sizeof(float), .align=_Alignof(float), .attr=ZEND_FFI_ATTR_PERSISTENT}; +static const zend_ffi_type zend_ffi_type_double = {.kind=ZEND_FFI_TYPE_DOUBLE, .size=sizeof(double), .align=_Alignof(double), .attr=ZEND_FFI_ATTR_PERSISTENT}; #ifdef HAVE_LONG_DOUBLE -static const zend_ffi_type zend_ffi_type_long_double = {.kind=ZEND_FFI_TYPE_LONGDOUBLE, .size=sizeof(long double), .align=_Alignof(long double)}; +static const zend_ffi_type zend_ffi_type_long_double = {.kind=ZEND_FFI_TYPE_LONGDOUBLE, .size=sizeof(long double), .align=_Alignof(long double), .attr=ZEND_FFI_ATTR_PERSISTENT}; #endif -static const zend_ffi_type zend_ffi_type_ptr = {.kind=ZEND_FFI_TYPE_POINTER, .size=sizeof(void*), .align=_Alignof(void*), .pointer.type = (zend_ffi_type*)&zend_ffi_type_void}; +static const zend_ffi_type zend_ffi_type_ptr = {.kind=ZEND_FFI_TYPE_POINTER, .size=sizeof(void*), .align=_Alignof(void*), .attr=ZEND_FFI_ATTR_PERSISTENT, .pointer.type = (zend_ffi_type*)&zend_ffi_type_void}; static const struct { const char *name; From 00cb74f04847a9a40258edaabb95d60386c8a017 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 27 Jun 2024 22:47:41 +0300 Subject: [PATCH 031/101] Reset persistent attribute --- ext/ffi/ffi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index aa2014724ee24..be7cff904b793 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -6667,6 +6667,7 @@ void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* { zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent)); memcpy(type, ZEND_FFI_TYPE(dcl->type), sizeof(zend_ffi_type)); + type->attr &= ~ZEND_FFI_ATTR_PERSISTENT; type->attr |= FFI_G(default_type_attr); type->align = dcl->align; dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type); From bad5a61bc33e787f0c910d7b78c51deedefce443 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 3 Jul 2024 13:04:38 +0300 Subject: [PATCH 032/101] Make FFI type cache context senseive --- Zend/zend.c | 4 +-- Zend/zend.h | 4 +-- ext/ffi/ffi.c | 58 +++++++++++++++++--------------- ext/opcache/ZendAccelerator.c | 62 ++++++++++++++++++++++------------- 4 files changed, 75 insertions(+), 53 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 52cbcaae5edce..25c3cbfc827db 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -100,8 +100,8 @@ ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_b ZEND_API zend_class_entry *zend_ffi_ce = NULL; ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; -ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str) = NULL; -ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl) = NULL; +ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *context) = NULL; +ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context) = NULL; ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str) = NULL; ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope) = NULL; diff --git a/Zend/zend.h b/Zend/zend.h index 12f090696d45b..9369e2c5e7b86 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -414,8 +414,8 @@ extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; typedef struct _zend_ffi_dcl zend_ffi_dcl; typedef struct _zend_ffi_scope zend_ffi_scope; -ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str); -ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl); +ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *context); +ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context); ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str); ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope); diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index be7cff904b793..1b20e9af85679 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -3790,7 +3790,7 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(default_type_attr) = 0; if (zend_ffi_cache_type_get - && (cached_dcl = zend_ffi_cache_type_get(type_def))) { + && (cached_dcl = zend_ffi_cache_type_get(type_def, FFI_G(symbols)))) { memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); @@ -3809,16 +3809,9 @@ ZEND_METHOD(FFI, new) /* {{{ */ if (clean_tags && FFI_G(tags)) { zend_ffi_tags_cleanup(&dcl); } - if (clean_symbols && FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - FFI_G(symbols) = NULL; - FFI_G(tags) = NULL; if (zend_ffi_cache_type_add) { - cached_dcl = zend_ffi_cache_type_add(type_def, &dcl); + cached_dcl = zend_ffi_cache_type_add(type_def, &dcl, FFI_G(symbols)); if (cached_dcl) { if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { _zend_ffi_type_dtor(dcl.type); @@ -3826,6 +3819,14 @@ ZEND_METHOD(FFI, new) /* {{{ */ memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } } + + if (clean_symbols && FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + FFI_G(symbols) = NULL; + FFI_G(tags) = NULL; } type = ZEND_FFI_TYPE(dcl.type); @@ -3954,7 +3955,7 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(default_type_attr) = 0; if (zend_ffi_cache_type_get - && (cached_dcl = zend_ffi_cache_type_get(type_def))) { + && (cached_dcl = zend_ffi_cache_type_get(type_def, FFI_G(symbols)))) { memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); @@ -3973,16 +3974,9 @@ ZEND_METHOD(FFI, cast) /* {{{ */ if (clean_tags && FFI_G(tags)) { zend_ffi_tags_cleanup(&dcl); } - if (clean_symbols && FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - FFI_G(symbols) = NULL; - FFI_G(tags) = NULL; if (zend_ffi_cache_type_add) { - cached_dcl = zend_ffi_cache_type_add(type_def, &dcl); + cached_dcl = zend_ffi_cache_type_add(type_def, &dcl, FFI_G(symbols)); if (cached_dcl) { if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { _zend_ffi_type_dtor(dcl.type); @@ -3990,6 +3984,14 @@ ZEND_METHOD(FFI, cast) /* {{{ */ memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } } + + if (clean_symbols && FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + FFI_G(symbols) = NULL; + FFI_G(tags) = NULL; } type = ZEND_FFI_TYPE(dcl.type); @@ -4140,7 +4142,7 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(default_type_attr) = 0; if (zend_ffi_cache_type_get - && (cached_dcl = zend_ffi_cache_type_get(type_def))) { + && (cached_dcl = zend_ffi_cache_type_get(type_def, FFI_G(symbols)))) { memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); @@ -4159,15 +4161,9 @@ ZEND_METHOD(FFI, type) /* {{{ */ if (clean_tags && FFI_G(tags)) { zend_ffi_tags_cleanup(&dcl); } - if (clean_symbols && FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - FFI_G(symbols) = NULL; - FFI_G(tags) = NULL; + if (zend_ffi_cache_type_add) { - cached_dcl = zend_ffi_cache_type_add(type_def, &dcl); + cached_dcl = zend_ffi_cache_type_add(type_def, &dcl, FFI_G(symbols)); if (cached_dcl) { if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { _zend_ffi_type_dtor(dcl.type); @@ -4175,6 +4171,14 @@ ZEND_METHOD(FFI, type) /* {{{ */ memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } } + + if (clean_symbols && FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + FFI_G(symbols) = NULL; + FFI_G(tags) = NULL; } ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 82ab859110da1..aaf4e7bb2bfcf 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2478,6 +2478,14 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, #if HAVE_FFI #include "ext/ffi/php_ffi.h" +typedef struct _accel_ffi_cache_type_entry accel_ffi_cache_type_entry; + +struct _accel_ffi_cache_type_entry { + accel_ffi_cache_type_entry *next; + void *context; + zend_ffi_dcl dcl; +}; + static ssize_t accel_ffi_persist_type_calc(zend_ffi_type *type) { HashTable *ht; @@ -2669,9 +2677,10 @@ static zend_ffi_type* accel_ffi_persist_type_copy(void** ptr, zend_ffi_type *typ return new_type; } -static zend_ffi_dcl* accel_ffi_persist_type(zend_ffi_dcl *dcl) +static accel_ffi_cache_type_entry* accel_ffi_persist_type(zend_ffi_dcl *dcl, void *context) { void *ptr; + accel_ffi_cache_type_entry *entry; zend_ffi_dcl *new_dcl; ssize_t size; @@ -2682,59 +2691,68 @@ static zend_ffi_dcl* accel_ffi_persist_type(zend_ffi_dcl *dcl) if (size == -1) { return NULL; } - ptr = zend_shared_alloc(sizeof(zend_ffi_dcl) + size); - if (!ptr) { + entry = zend_shared_alloc(sizeof(accel_ffi_cache_type_entry) + size); + if (!entry) { return NULL; } zend_shared_alloc_init_xlat_table(); - new_dcl = (zend_ffi_dcl*)ptr; - ptr = (void*)((char*)ptr + sizeof(zend_ffi_dcl)); + entry->next = NULL; + entry->context = context; + new_dcl = &entry->dcl; memcpy(new_dcl, dcl, sizeof(zend_ffi_dcl)); + ptr = entry + 1; new_dcl->type = accel_ffi_persist_type_copy(&ptr, new_dcl->type); zend_shared_alloc_destroy_xlat_table(); - return new_dcl; + return entry; } -static zend_ffi_dcl* accel_ffi_cache_type_get(zend_string *str) +static zend_ffi_dcl* accel_ffi_cache_type_get(zend_string *str, void *context) { str = accel_find_interned_string(str); if (str && (str->gc.u.type_info & IS_STR_FFI_TYPE)) { // TODO: ??? - zend_ffi_dcl *ptr = (zend_ffi_dcl*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); - return ptr; + accel_ffi_cache_type_entry *ptr = + (accel_ffi_cache_type_entry*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + while (ptr && ptr->context != context) { + ptr = ptr->next; + } + if (ptr) { + return &ptr->dcl; + } } return NULL; } -static zend_ffi_dcl* accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl) +static zend_ffi_dcl* accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dcl, void *context) { zend_ffi_dcl *new_dcl = NULL; - if (!ZEND_FFI_TYPE_IS_OWNED(dcl->type) - && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT)) { - return NULL; - } - SHM_UNPROTECT(); zend_shared_alloc_lock(); - new_dcl = accel_ffi_cache_type_get(str); + new_dcl = accel_ffi_cache_type_get(str, context); if (!new_dcl) { str = accel_new_interned_string(zend_string_copy(str)); if (IS_ACCEL_INTERNED(str)) { - new_dcl = accel_ffi_persist_type(dcl); - if (new_dcl) { + accel_ffi_cache_type_entry *ptr = accel_ffi_persist_type(dcl, context); + if (ptr) { // TODO: ??? - ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_TYPE|IS_STR_FFI_SCOPE))); - ZEND_ASSERT((char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p > 0 && + ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_SCOPE))); + ZEND_ASSERT((char*)ptr - (char*)ZSMMG(shared_segments)[0]->p > 0 && (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); - str->gc.u.type_info |= IS_STR_FFI_TYPE; + if (str->gc.u.type_info & IS_STR_FFI_TYPE) { + ptr->next = (accel_ffi_cache_type_entry*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + } else { + ptr->next = NULL; + str->gc.u.type_info |= IS_STR_FFI_TYPE; + } // TODO: ??? - str->gc.refcount = (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p; + str->gc.refcount = (char*)ptr - (char*)ZSMMG(shared_segments)[0]->p; + new_dcl = &ptr->dcl; } } } From 72e86a5799c5d2c0bd836043dc6e4186faa0ace5 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 3 Jul 2024 13:24:11 +0300 Subject: [PATCH 033/101] Cleanup pointer compression --- ext/opcache/ZendAccelerator.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index aaf4e7bb2bfcf..e2aa5bba491d8 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2710,13 +2710,25 @@ static accel_ffi_cache_type_entry* accel_ffi_persist_type(zend_ffi_dcl *dcl, voi return entry; } +static uint32_t accel_ffi_cache_ptr_to_offset(const void *ptr) +{ + ZEND_ASSERT(((uintptr_t)ptr & 0x7) == 0); /* should be 8 byte aligned */ + ZEND_ASSERT(((uintptr_t)ZSMMG(shared_segments)[0]->p & 0x7) == 0); + ZEND_ASSERT((uint64_t*)ptr - (uint64_t*)ZSMMG(shared_segments)[0]->p > 0 && + (uint64_t*)ptr - (uint64_t*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); + return (uint64_t*)ptr - (uint64_t*)ZSMMG(shared_segments)[0]->p; +} + +static void* accel_ffi_cache_offset_to_ptr(uint32_t offset) +{ + return (uint64_t*)ZSMMG(shared_segments)[0]->p + offset; +} + static zend_ffi_dcl* accel_ffi_cache_type_get(zend_string *str, void *context) { str = accel_find_interned_string(str); if (str && (str->gc.u.type_info & IS_STR_FFI_TYPE)) { - // TODO: ??? - accel_ffi_cache_type_entry *ptr = - (accel_ffi_cache_type_entry*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + accel_ffi_cache_type_entry *ptr = accel_ffi_cache_offset_to_ptr(str->gc.refcount); while (ptr && ptr->context != context) { ptr = ptr->next; } @@ -2740,18 +2752,14 @@ static zend_ffi_dcl* accel_ffi_cache_type_add(zend_string *str, zend_ffi_dcl *dc if (IS_ACCEL_INTERNED(str)) { accel_ffi_cache_type_entry *ptr = accel_ffi_persist_type(dcl, context); if (ptr) { - // TODO: ??? ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_SCOPE))); - ZEND_ASSERT((char*)ptr - (char*)ZSMMG(shared_segments)[0]->p > 0 && - (char*)new_dcl - (char*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); if (str->gc.u.type_info & IS_STR_FFI_TYPE) { - ptr->next = (accel_ffi_cache_type_entry*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + ptr->next = accel_ffi_cache_offset_to_ptr(str->gc.refcount); } else { ptr->next = NULL; str->gc.u.type_info |= IS_STR_FFI_TYPE; } - // TODO: ??? - str->gc.refcount = (char*)ptr - (char*)ZSMMG(shared_segments)[0]->p; + str->gc.refcount = accel_ffi_cache_ptr_to_offset(ptr); new_dcl = &ptr->dcl; } } @@ -2930,8 +2938,7 @@ static zend_ffi_scope* accel_ffi_cache_scope_get(zend_string *str) { str = accel_find_interned_string(str); if (str && (str->gc.u.type_info & IS_STR_FFI_SCOPE)) { - // TODO: ??? - zend_ffi_scope *ptr = (zend_ffi_scope*)((char*)ZSMMG(shared_segments)[0]->p + str->gc.refcount); + zend_ffi_scope *ptr = (zend_ffi_scope*)accel_ffi_cache_offset_to_ptr(str->gc.refcount); return ptr; } return NULL; @@ -2950,13 +2957,9 @@ static zend_ffi_scope* accel_ffi_cache_scope_add(zend_string *str, zend_ffi_scop if (IS_ACCEL_INTERNED(str)) { new_scope = accel_ffi_persist_scope(scope); if (new_scope) { - // TODO: ??? ZEND_ASSERT(!(str->gc.u.type_info & (IS_STR_CLASS_NAME_MAP_PTR|IS_STR_FFI_TYPE|IS_STR_FFI_SCOPE))); - ZEND_ASSERT((char*)new_scope - (char*)ZSMMG(shared_segments)[0]->p > 0 && - (char*)new_scope - (char*)ZSMMG(shared_segments)[0]->p < 0x7fffffff); str->gc.u.type_info |= IS_STR_FFI_SCOPE; - // TODO: ??? - str->gc.refcount = (char*)new_scope - (char*)ZSMMG(shared_segments)[0]->p; + str->gc.refcount = accel_ffi_cache_ptr_to_offset(new_scope); } } } From 05bee9d325048ac37bcf81b1e01d08a4199b402c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 00:18:44 +0300 Subject: [PATCH 034/101] Add symbols guard --- ext/opcache/jit/zend_jit_ir.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 3030beeb661f3..cf197f13dfe83 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10622,6 +10622,15 @@ static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); } +static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + zend_jit_addr addr, + HashTable *ffi_symbols, + zend_jit_ffi_info *ffi_info); + static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -10635,6 +10644,10 @@ static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, HashTable *op1_ffi_symbols, zend_jit_ffi_info *ffi_info) { + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + return 1; } From 3605a6f04d970252e91296be3d56d9e260c3c146 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 01:16:00 +0300 Subject: [PATCH 035/101] Allow JIT for reading FFI arrays/structs/pointers --- ext/opcache/jit/zend_jit_helpers.c | 23 +++++++++++++++++++++++ ext/opcache/jit/zend_jit_ir.c | 18 ++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 3 --- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 12f705e3a4e91..baba3ff9f8406 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3395,4 +3395,27 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, v ZVAL_NULL(zv); } } + +static void ZEND_FASTCALL zend_jit_zval_ffi_obj(zval *zv, zend_ffi_type *type, void *ptr) +{ + if (!ptr) { + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER); + ZVAL_NULL(zv); + } else { + zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); + + // inlined zend_ffi_object_init() + GC_SET_REFCOUNT(&cdata->std, 1); + GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + cdata->std.ce = zend_ffi_cdata_ce; + cdata->std.handlers = zend_ffi_cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + cdata->std.properties = NULL; + zend_objects_store_put(&cdata->std); + cdata->type = type; + cdata->flags = 0; + cdata->ptr = ptr; + cdata->ptr_holder = NULL; + ZVAL_OBJ(zv, &cdata->std); + } +} #endif diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index cf197f13dfe83..232e42c64734d 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3103,6 +3103,7 @@ static void zend_jit_setup_disasm(void) #ifdef HAVE_FFI REGISTER_HELPER(zend_jit_zval_string); REGISTER_HELPER(zend_jit_zval_ffi_ptr); + REGISTER_HELPER(zend_jit_zval_ffi_obj); #endif #ifndef ZTS @@ -13259,6 +13260,23 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, ir_MUL_L(ir_ZEXT_L(ir_LOAD_U8(ptr)), ir_CONST_LONG(sizeof(void*)))))); res_type = IS_STRING; break; + case ZEND_FFI_TYPE_ARRAY: + case ZEND_FFI_TYPE_STRUCT: + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_obj), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ptr); + return 1; + case ZEND_FFI_TYPE_POINTER: + if ((ffi_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ptr); + return 1; + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ir_LOAD_A(ptr)); + return 1; + } + break; + break; default: ZEND_UNREACHABLE(); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 34a6c8ec39c47..526a0e0806070 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6076,7 +6076,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && op2_info == MAY_BE_LONG - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind < ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { if (!ffi_info) { @@ -6334,7 +6333,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->bits - && ZEND_FFI_TYPE(field->type)->kind < ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(field->type))) { if (!ffi_info) { @@ -6356,7 +6354,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym && sym->kind == ZEND_FFI_SYM_VAR - && ZEND_FFI_TYPE(sym->type)->kind < ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(sym->type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(sym->type))) { if (!ffi_info) { From cb6cbffb5369aed7c3d9a19fc764c9e9a83ea8da Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 10:12:21 +0300 Subject: [PATCH 036/101] Fix registers update --- ext/opcache/jit/zend_jit_ir.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 232e42c64734d..563a60a1c503a 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14257,6 +14257,15 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, return 0; } + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { return 0; } @@ -15794,6 +15803,15 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, return 0; } + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); @@ -15835,6 +15853,15 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, return 0; } + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + ir_ref ptr = ir_CONST_ADDR(sym->addr); if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type)) { return 0; From 09f4381aae8a1e282ef8882ed314e31921c92424 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 10:25:15 +0300 Subject: [PATCH 037/101] FFI and CData are "final" classes. Their object handlers can't be called for $this. --- ext/opcache/jit/zend_jit_ir.c | 22 ++++------------------ ext/opcache/jit/zend_jit_trace.c | 14 +++++++------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 563a60a1c503a..00d895ffbd086 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10639,8 +10639,6 @@ static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, - bool on_this, - bool delayed_fetch_this, zend_ffi_symbol *sym, HashTable *op1_ffi_symbols, zend_jit_ffi_info *ffi_info) @@ -15177,8 +15175,6 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, bool op1_indirect, - bool on_this, - bool delayed_fetch_this, bool op1_avoid_refcounting, zend_ffi_field *field, zend_jit_addr res_addr, @@ -15222,8 +15218,6 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, bool op1_indirect, - bool on_this, - bool delayed_fetch_this, bool op1_avoid_refcounting, zend_ffi_symbol *sym, zend_jit_addr res_addr, @@ -15785,8 +15779,6 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, bool op1_indirect, - bool on_this, - bool delayed_fetch_this, zend_ffi_field *field, uint32_t val_info, zend_jit_addr val_addr, @@ -15821,7 +15813,7 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr); - if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -15836,8 +15828,6 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, bool op1_indirect, - bool on_this, - bool delayed_fetch_this, zend_ffi_symbol *sym, uint32_t val_info, zend_jit_addr val_addr, @@ -15869,7 +15859,7 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr); - if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -16245,8 +16235,6 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, bool op1_indirect, - bool on_this, - bool delayed_fetch_this, zend_ffi_field *field, uint32_t val_info, zend_jit_addr val_addr, @@ -16268,7 +16256,7 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, return 0; } - if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -16283,8 +16271,6 @@ static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, bool op1_indirect, - bool on_this, - bool delayed_fetch_this, zend_ffi_symbol *sym, uint32_t val_info, zend_jit_addr val_addr, @@ -16303,7 +16289,7 @@ static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, return 0; } - if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) { + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 526a0e0806070..41d39cf99cf50 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5015,7 +5015,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, field, + op1_info, op1_addr, op1_indirect, field, op1_data_info, OP1_DATA_REG_ADDR(), op1_ffi_type, ffi_info)) { goto jit_failure; @@ -5032,7 +5032,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_sym_op(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, sym, + op1_info, op1_addr, op1_indirect, sym, op1_data_info, OP1_DATA_REG_ADDR(), op1_ffi_symbols, ffi_info)) { goto jit_failure; @@ -5137,7 +5137,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_obj(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, field, + op1_info, op1_addr, op1_indirect, field, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, op1_ffi_type, op3_ffi_type, ffi_info)) { @@ -5160,7 +5160,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_sym(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, op1_indirect, on_this, delayed_fetch_this, sym, + op1_info, op1_addr, op1_indirect, sym, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, op1_ffi_symbols, op3_ffi_type, ffi_info)) { @@ -6340,7 +6340,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } if (!zend_jit_ffi_fetch_obj(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_indirect, - on_this, delayed_fetch_this, avoid_refcounting, field, + avoid_refcounting, field, RES_REG_ADDR(), op1_ffi_type, ffi_info)) { goto jit_failure; @@ -6361,7 +6361,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } if (!zend_jit_ffi_fetch_sym(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_indirect, - on_this, delayed_fetch_this, avoid_refcounting, sym, + avoid_refcounting, sym, RES_REG_ADDR(), op1_ffi_symbols, ffi_info)) { goto jit_failure; @@ -6672,7 +6672,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } if (!zend_jit_ffi_init_call_sym(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, - on_this, delayed_fetch_this, sym, + sym, op1_ffi_symbols, ffi_info)) { goto jit_failure; } From 05aea3639a1e2f90fcac36ddeb007a84ee6ea9bc Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 13:34:58 +0300 Subject: [PATCH 038/101] Support for FFI pointer math --- ext/opcache/jit/zend_jit.c | 11 +++++++++++ ext/opcache/jit/zend_jit_ir.c | 11 +++++++++++ ext/opcache/jit/zend_jit_trace.c | 9 ++++++--- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 179e1e1d1c080..84f5b189a46c4 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -122,6 +122,17 @@ static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, } return false; } + +static bool zend_jit_ffi_compatible_addr_op(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type, uint8_t opcode) +{ + if (dst_type->kind == ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(dst_type->pointer.type)->size != 0 + && src_info == MAY_BE_LONG + && (opcode == ZEND_ADD || opcode == ZEND_SUB)) { + return true; + } + return false; +} #endif #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 00d895ffbd086..49111e9ad9a32 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14662,6 +14662,17 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, } break; #endif + case ZEND_FFI_TYPE_POINTER: + ZEND_ASSERT(opcode == ZEND_ADD || opcode == ZEND_SUB); + ZEND_ASSERT(ZEND_FFI_TYPE(el_type->pointer.type)->size != 0); + type = IR_ADDR; + if (op2_info == MAY_BE_LONG) { + op2 = ir_MUL_A(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ZEND_FFI_TYPE(el_type->pointer.type)->size)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; default: ZEND_UNREACHABLE(); return 0; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 41d39cf99cf50..19f5b2c3ebe63 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4802,7 +4802,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && op2_info == MAY_BE_LONG - && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { + && (zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type) + || zend_jit_ffi_compatible_addr_op(op1_ffi_type->array.type, op1_data_info, op3_ffi_type, opline->extended_value))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5010,7 +5011,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->is_const && !field->bits - && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { + && (zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type) + || zend_jit_ffi_compatible_addr_op(field->type, op1_data_info, op3_ffi_type, opline->extended_value))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5027,7 +5029,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym && sym->kind == ZEND_FFI_SYM_VAR - && zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type)) { + && (zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type) + || zend_jit_ffi_compatible_addr_op(sym->type, op1_data_info, op3_ffi_type, opline->extended_value))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } From 7b432e4f0bd64f03c4aefc7dbe838c4b2cddbe46 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 17:18:40 +0300 Subject: [PATCH 039/101] Support for ASSIGN_OBJ/DIM with result --- ext/opcache/jit/zend_jit_ir.c | 159 +++++++++++++++++++++++++++------- 1 file changed, 126 insertions(+), 33 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 49111e9ad9a32..a6aedbee15fd3 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14109,119 +14109,212 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, ir_ref ptr, uint32_t val_info, zend_jit_addr val_addr, - zend_ffi_type *val_ffi_type) + zend_ffi_type *val_ffi_type, + zend_jit_addr res_addr) { + ir_ref ref = IR_UNUSED; + switch (ffi_type->kind) { case ZEND_FFI_TYPE_FLOAT: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_INT2F(jit_Z_LVAL(jit, val_addr))); + ref = ir_INT2F(jit_Z_LVAL(jit, val_addr)); } else if (val_info == MAY_BE_DOUBLE) { - ir_STORE(ptr, ir_D2F(jit_Z_DVAL(jit, val_addr))); + ref = ir_D2F(jit_Z_DVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_F(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_F(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_F2D(ref); + jit_set_Z_DVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_DOUBLE); + } + } break; case ZEND_FFI_TYPE_DOUBLE: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_INT2D(jit_Z_LVAL(jit, val_addr))); + ref = ir_INT2D(jit_Z_LVAL(jit, val_addr)); } else if (val_info == MAY_BE_DOUBLE) { - ir_STORE(ptr, jit_Z_DVAL(jit, val_addr)); + ref = jit_Z_DVAL(jit, val_addr); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_D(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_D(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + jit_set_Z_DVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_DOUBLE); + } + } break; case ZEND_FFI_TYPE_BOOL: if (val_info == MAY_BE_FALSE) { ir_STORE(ptr, IR_FALSE); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_FALSE); + } return 1; } else if (val_info == MAY_BE_TRUE) { ir_STORE(ptr, IR_TRUE); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_TRUE); + } return 1; } else if (val_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { - ir_STORE(ptr, ir_SUB_U8(jit_Z_TYPE(jit, val_addr), ir_CONST_U8(IS_FALSE))); + if (res_addr) { + ref = jit_Z_TYPE_INFO(jit, val_addr); + jit_set_Z_TYPE_INFO_ex(jit, res_addr, ref); + ref = ir_TRUNC_U8(ref); + } else { + ref = jit_Z_TYPE(jit, val_addr); + } + ir_STORE(ptr, ir_SUB_U8(ref, ir_CONST_U8(IS_FALSE))); return 1; } ZEND_FALLTHROUGH; case ZEND_FFI_TYPE_UINT8: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_U8(jit_Z_LVAL(jit, val_addr))); + ref = ir_TRUNC_U8(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_U8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_U8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_ZEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; case ZEND_FFI_TYPE_SINT8: case ZEND_FFI_TYPE_CHAR: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr))); + ref = ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_I8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_I8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_SEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; case ZEND_FFI_TYPE_UINT16: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_U16(jit_Z_LVAL(jit, val_addr))); + ref = ir_TRUNC_U16(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_U16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_U16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_ZEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; case ZEND_FFI_TYPE_SINT16: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_I16(jit_Z_LVAL(jit, val_addr))); + ref = ir_TRUNC_I16(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_I16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_I16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_SEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; #ifdef ZEND_ENABLE_ZVAL_LONG64 case ZEND_FFI_TYPE_UINT32: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_U32(jit_Z_LVAL(jit, val_addr))); + ref = ir_TRUNC_U32(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_U32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_U32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_ZEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; case ZEND_FFI_TYPE_SINT32: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, ir_TRUNC_I32(jit_Z_LVAL(jit, val_addr))); + ref = ir_TRUNC_I32(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_SEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; case ZEND_FFI_TYPE_UINT64: case ZEND_FFI_TYPE_SINT64: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, jit_Z_LVAL(jit, val_addr)); + ref = jit_Z_LVAL(jit, val_addr); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_I64(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_I64(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; #else case ZEND_FFI_TYPE_UINT32: case ZEND_FFI_TYPE_SINT32: if (val_info == MAY_BE_LONG) { - ir_STORE(ptr, jit_Z_LVAL(jit, val_addr)); + ref = jit_Z_LVAL(jit, val_addr); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ir_STORE(ptr, ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)))); + ref = ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); } else { ZEND_UNREACHABLE(); } + ir_STORE(ptr, ref); + if (res_addr) { + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } break; #endif default: @@ -14276,12 +14369,12 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr, val_ffi_type)) { + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + + if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { return 0; } - ZEND_ASSERT(!res_addr); - return 1; } #endif @@ -15818,12 +15911,12 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); - if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr, val_ffi_type)) { + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + + if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { return 0; } - ZEND_ASSERT(!res_addr); - if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -15863,13 +15956,13 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, } } + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + ir_ref ptr = ir_CONST_ADDR(sym->addr); - if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type)) { + if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { return 0; } - ZEND_ASSERT(!res_addr); - if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } From 3a14b04a48eb418e4b922eb3a2afbb310d5eb2f1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 18:17:19 +0300 Subject: [PATCH 040/101] Support for FFI pointer assignment --- ext/opcache/jit/zend_jit.c | 20 ++++++++++++++++++++ ext/opcache/jit/zend_jit_ir.c | 31 +++++++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 9 ++++++--- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 84f5b189a46c4..8133d58c57d1c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -123,6 +123,26 @@ static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, return false; } +static bool zend_jit_ffi_compatible_addr(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type) +{ + if (dst_type->kind == ZEND_FFI_TYPE_POINTER) { + if (src_info == MAY_BE_NULL) { + return true; + } else if (src_type + && src_type->kind == ZEND_FFI_TYPE_POINTER + && (dst_type == src_type + || ZEND_FFI_TYPE(dst_type->pointer.type) == ZEND_FFI_TYPE(src_type->pointer.type) + || ZEND_FFI_TYPE(dst_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID + || ZEND_FFI_TYPE(src_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID +// TODO: calls between shared extensions doesn't work on Windows +// || zend_ffi_is_compatible_type(dst_type, src_type) + )) { + return true; + } + } + return false; +} + static bool zend_jit_ffi_compatible_addr_op(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type, uint8_t opcode) { if (dst_type->kind == ZEND_FFI_TYPE_POINTER diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index a6aedbee15fd3..f99820b165b83 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14317,6 +14317,25 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, } break; #endif + case ZEND_FFI_TYPE_POINTER: + if (val_info == MAY_BE_NULL) { + ref = IR_NULL; + } else if (val_ffi_type + && val_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && (val_ffi_type == ffi_type + || ZEND_FFI_TYPE(val_ffi_type->pointer.type) == ZEND_FFI_TYPE(ffi_type->pointer.type) + || ZEND_FFI_TYPE(val_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID + || ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID)) { + ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ref); + } + break; default: ZEND_UNREACHABLE(); } @@ -14375,6 +14394,8 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, return 0; } + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + return 1; } #endif @@ -14816,6 +14837,8 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, return 0; } + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, op1_data_info, opline); + return 1; } #endif @@ -15917,6 +15940,8 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, return 0; } + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -15963,6 +15988,8 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, return 0; } + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -16360,6 +16387,8 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, return 0; } + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -16393,6 +16422,8 @@ static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, return 0; } + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + if (!op1_indirect) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 19f5b2c3ebe63..d803f899db154 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5135,7 +5135,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->is_const && !field->bits - && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { + && (zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type) + || zend_jit_ffi_compatible_addr(field->type, op1_data_info, op3_ffi_type))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5158,7 +5159,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym && sym->kind == ZEND_FFI_SYM_VAR - && zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type)) { + && (zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type) + || zend_jit_ffi_compatible_addr(sym->type, op1_data_info, op3_ffi_type))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5233,7 +5235,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && op2_info == MAY_BE_LONG - && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { + && (zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type) + || zend_jit_ffi_compatible_addr(op1_ffi_type->array.type, op1_data_info, op3_ffi_type))) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } From 735482fa7c9a2e349c27a8df60ef0dd9239d1746 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 4 Jul 2024 23:28:33 +0300 Subject: [PATCH 041/101] FFI JIT for FETCH_DIM/OBJ_W --- Zend/zend.c | 1 + Zend/zend.h | 5 ++- ext/ffi/ffi.c | 18 +++++++++ ext/opcache/jit/zend_jit_ir.c | 69 +++++++++++++++++++++----------- ext/opcache/jit/zend_jit_trace.c | 30 ++++++++++++-- 5 files changed, 95 insertions(+), 28 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 25c3cbfc827db..84a346fc97431 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -100,6 +100,7 @@ ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_b ZEND_API zend_class_entry *zend_ffi_ce = NULL; ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; +ZEND_API zend_ffi_cdata* (*zend_ffi_cdata_create)(void *ptr, zend_ffi_type *type) = NULL; ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *context) = NULL; ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context) = NULL; ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str) = NULL; diff --git a/Zend/zend.h b/Zend/zend.h index 9369e2c5e7b86..980a8619f9b14 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -411,9 +411,12 @@ extern ZEND_API zend_utility_values zend_uv; extern ZEND_API zend_class_entry *zend_ffi_ce; extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; -typedef struct _zend_ffi_dcl zend_ffi_dcl; +typedef struct _zend_ffi_dcl zend_ffi_dcl; typedef struct _zend_ffi_scope zend_ffi_scope; +typedef struct _zend_ffi_cdata zend_ffi_cdata; +typedef struct _zend_ffi_type zend_ffi_type; +ZEND_API extern zend_ffi_cdata* (*zend_ffi_cdata_create)(void *ptr, zend_ffi_type *type); ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *context); ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context); ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str); diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 1b20e9af85679..4a31b60a162f7 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -355,6 +355,22 @@ static ffi_type *zend_ffi_get_type(zend_ffi_type *type) /* {{{ */ } /* }}} */ +static zend_ffi_cdata* _zend_ffi_cdata_create(void *ptr, zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); + + zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce); + cdata->std.handlers = + (type->kind < ZEND_FFI_TYPE_POINTER) ? + &zend_ffi_cdata_value_handlers : + &zend_ffi_cdata_handlers; + cdata->type = type; + cdata->flags = 0; + cdata->ptr = ptr; + return cdata; +} +/* }}} */ + static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */ { zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); @@ -5580,6 +5596,8 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_ctype_handlers.get_properties = zend_fake_get_properties; zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc; + zend_ffi_cdata_create = _zend_ffi_cdata_create; + if (FFI_G(preload)) { return zend_ffi_preload(FFI_G(preload)); } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index f99820b165b83..ae35ca9d7c6e3 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -13386,20 +13386,21 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, return 1; } -static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_avoid_refcounting, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t res_info, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) +static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool op1_avoid_refcounting, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t res_info, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); @@ -13421,8 +13422,15 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - if (!zend_jit_ffi_read(jit, el_type, ptr, res_addr)) { - return 0; + if (opline->opcode == ZEND_FETCH_DIM_W || opline->opcode == ZEND_FETCH_DIM_RW) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(el_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, el_type, ptr, res_addr)) { + return 0; + } } if (res_info & MAY_BE_GUARD) { @@ -13430,7 +13438,7 @@ static int zend_jit_ffi_fetch_dim_read(zend_jit_ctx *jit, ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; } - if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting) { + if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting && !op1_indirect) { if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -15319,8 +15327,15 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); - if (!zend_jit_ffi_read(jit, field_type, ptr, res_addr)) { - return 0; + if (opline->opcode == ZEND_FETCH_OBJ_W) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(field_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, field_type, ptr, res_addr)) { + return 0; + } } if (res_info & MAY_BE_GUARD) { @@ -15328,7 +15343,7 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; } - if (!op1_avoid_refcounting) { + if (!op1_avoid_refcounting && !op1_indirect) { if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } @@ -15359,8 +15374,16 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, } ir_ref ptr = ir_CONST_ADDR(sym->addr); - if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { - return 0; + + if (opline->opcode == ZEND_FETCH_OBJ_W) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(sym_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { + return 0; + } } if (res_info & MAY_BE_GUARD) { @@ -15368,7 +15391,7 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; } - if (!op1_avoid_refcounting) { + if (!op1_avoid_refcounting && !op1_indirect) { if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index d803f899db154..2f59c4469c320 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6087,8 +6087,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } - if (!zend_jit_ffi_fetch_dim_read(&ctx, opline, ssa, ssa_op, - op1_info, op1_addr, avoid_refcounting, + if (!zend_jit_ffi_fetch_dim(&ctx, opline, ssa, ssa_op, + op1_info, op1_addr, 0, avoid_refcounting, op2_info, OP2_REG_ADDR(), OP2_RANGE(), res_info, RES_REG_ADDR(), op1_ffi_type, ffi_info)) { @@ -6117,9 +6117,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } op1_info = OP1_INFO(); op1_addr = OP1_REG_ADDR(); + op1_indirect = 0; if (opline->op1_type == IS_VAR) { if (orig_op1_type != IS_UNKNOWN && (orig_op1_type & IS_TRACE_INDIRECT)) { + op1_indirect = 1; if (!zend_jit_fetch_indirect_var(&ctx, opline, orig_op1_type, &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) { goto jit_failure; @@ -6144,6 +6146,24 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); op1_def_info = OP1_DEF_INFO(); +#ifdef HAVE_FFI + if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && op2_info == MAY_BE_LONG + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_fetch_dim(&ctx, opline, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, avoid_refcounting, + op2_info, OP2_REG_ADDR(), OP2_RANGE(), + res_info, RES_REG_ADDR(), + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + } else +#endif if (!zend_jit_fetch_dim(&ctx, opline, op1_info, op1_addr, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, @@ -6332,7 +6352,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } #ifdef HAVE_FFI if ((opline->opcode == ZEND_FETCH_OBJ_R - || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) + || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG + || opline->opcode == ZEND_FETCH_OBJ_W) && op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, Z_STR_P(RT_CONSTANT(opline, opline->op2))); @@ -6354,7 +6375,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto done; } } else if ((opline->opcode == ZEND_FETCH_OBJ_R - || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG) + || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG + || opline->opcode == ZEND_FETCH_OBJ_W) && op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, Z_STR_P(RT_CONSTANT(opline, opline->op2))); From a873c71079d8b727afc71e1bb3b322cc6959af71 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 5 Jul 2024 09:56:53 +0300 Subject: [PATCH 042/101] Fix uninitialized data access --- ext/opcache/jit/zend_jit_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 2f59c4469c320..48cd465ff3b1a 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6158,7 +6158,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (!zend_jit_ffi_fetch_dim(&ctx, opline, ssa, ssa_op, op1_info, op1_addr, op1_indirect, avoid_refcounting, op2_info, OP2_REG_ADDR(), OP2_RANGE(), - res_info, RES_REG_ADDR(), + RES_INFO(), RES_REG_ADDR(), op1_ffi_type, ffi_info)) { goto jit_failure; } From 8d39cfe7e49f291e085605c98fc630dfb34cfec4 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 5 Jul 2024 10:00:57 +0300 Subject: [PATCH 043/101] Fix calling convention --- ext/opcache/jit/zend_jit_ir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index ae35ca9d7c6e3..fe7497cb0547c 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -13424,7 +13424,7 @@ static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_DIM_W || opline->opcode == ZEND_FETCH_DIM_RW) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), ptr, ir_CONST_ADDR(el_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { @@ -15329,7 +15329,7 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_OBJ_W) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), ptr, ir_CONST_ADDR(field_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { @@ -15377,7 +15377,7 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_OBJ_W) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), ptr, ir_CONST_ADDR(sym_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { From 9ae91586cc5dbb4e0b61aea21f588bbf1ed493e6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 5 Jul 2024 11:16:39 +0300 Subject: [PATCH 044/101] FFI JIT for FETCH_DIM/OBJ_W + ASSIGN_DIM --- ext/opcache/jit/zend_jit_ir.c | 5 +++++ ext/opcache/jit/zend_jit_trace.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index fe7497cb0547c..36d2193057544 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14357,6 +14357,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, + bool op1_indirect, uint32_t op2_info, zend_jit_addr op2_addr, zend_ssa_range *op2_range, @@ -14404,6 +14405,10 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + return 1; } #endif diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 48cd465ff3b1a..f158a60c50c49 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5241,7 +5241,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_dim(&ctx, opline, ssa, ssa_op, - op1_info, op1_addr, + op1_info, op1_addr, op1_indirect, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, op1_data_info, OP1_DATA_REG_ADDR(), From ed6ee1ea7a9d166bb05e9064957e0fb4a7b04e8e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 5 Jul 2024 11:43:12 +0300 Subject: [PATCH 045/101] FFI JIT for FETCH_DIM_RW + ASSIGN_DIM_OP --- ext/opcache/jit/zend_jit_ir.c | 5 +++++ ext/opcache/jit/zend_jit_trace.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 36d2193057544..ef1f11cc206a1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14817,6 +14817,7 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, uint32_t op1_info, uint32_t op1_def_info, zend_jit_addr op1_addr, + bool op1_indirect, uint32_t op2_info, zend_jit_addr op2_addr, zend_ssa_range *op2_range, @@ -14852,6 +14853,10 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, op1_data_info, opline); + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + return 1; } #endif diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index f158a60c50c49..a08b61c8fc82e 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4808,7 +4808,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_assign_dim_op(&ctx, opline, ssa, ssa_op, - op1_info, op1_def_info, op1_addr, + op1_info, op1_def_info, op1_addr, op1_indirect, op2_info, (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), From 0d04e48a454d7cd8abc60b8a28e2a0612f1aeb8b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 5 Jul 2024 14:33:14 +0300 Subject: [PATCH 046/101] FFI JIT for PRE/POST_INC/DEC_OBJ --- ext/opcache/jit/zend_jit_ir.c | 269 +++++++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 47 ++++++ 2 files changed, 316 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index ef1f11cc206a1..d5c0ba02629d7 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -16892,6 +16892,275 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit, return 1; } +#ifdef HAVE_FFI +static int zend_jit_ffi_incdec_helper(zend_jit_ctx *jit, + const zend_op *opline, + uint8_t opcode, + zend_ffi_type *el_type, + ir_ref op1, + zend_jit_addr res_addr) +{ + ir_op op; + ir_type type; + ir_ref op2; + ir_ref ref = IR_UNUSED; + + switch (opcode) { + case ZEND_PRE_INC_OBJ: + case ZEND_POST_INC_OBJ: + op = IR_ADD; + break; + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_DEC_OBJ: + op = IR_SUB; + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + + switch (el_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + type = IR_FLOAT; + op2 = ir_CONST_FLOAT(1.0); + break; + case ZEND_FFI_TYPE_DOUBLE: + type = IR_DOUBLE; + op2 = ir_CONST_DOUBLE(1.0); + break; + case ZEND_FFI_TYPE_UINT8: + type = IR_U8; + op2 = ir_CONST_U8(1); + break; + case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_CHAR: + type = IR_I8; + op2 = ir_CONST_I8(1); + break; + case ZEND_FFI_TYPE_UINT16: + type = IR_U16; + op2 = ir_CONST_U16(1); + break; + case ZEND_FFI_TYPE_SINT16: + type = IR_I16; + op2 = ir_CONST_I16(1); + break; + case ZEND_FFI_TYPE_UINT32: + type = IR_U32; + op2 = ir_CONST_U32(1); + break; + case ZEND_FFI_TYPE_SINT32: + type = IR_I32; + op2 = ir_CONST_I32(1); + break; + case ZEND_FFI_TYPE_UINT64: + type = IR_U64; + op2 = ir_CONST_U64(1); + break; + case ZEND_FFI_TYPE_SINT64: + type = IR_I64; + op2 = ir_CONST_I64(1); + break; + case ZEND_FFI_TYPE_POINTER: + ZEND_ASSERT(ZEND_FFI_TYPE(el_type->pointer.type)->size != 0); + type = IR_ADDR; + op2 = ir_CONST_LONG(ZEND_FFI_TYPE(el_type->pointer.type)->size); + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + + ref = ir_LOAD(type, op1); + + if (res_addr && (opcode == ZEND_POST_INC_OBJ || opcode == ZEND_POST_DEC_OBJ)) { + uint32_t res_type = IS_UNDEF; + + switch (type) { + case IR_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); + res_type = IS_DOUBLE; + break; + case IR_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ref); + res_type = IS_DOUBLE; + break; + case IR_I8: + case IR_I16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_I32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_I64: +#else + case IR_I32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_U8: + case IR_U16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_U32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_U64: +#else + case IR_U32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_ADDR: + if ((el_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(el_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(el_type), ref); + } + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + if (res_type && Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + } + + ref = ir_BINARY_OP(op, type, ref, op2); + ir_STORE(op1, ref); + + if (res_addr && (opcode == ZEND_PRE_INC_OBJ || opcode == ZEND_PRE_DEC_OBJ)) { + uint32_t res_type = IS_UNDEF; + + switch (type) { + case IR_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); + res_type = IS_DOUBLE; + break; + case IR_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ref); + res_type = IS_DOUBLE; + break; + case IR_I8: + case IR_I16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_I32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_I64: +#else + case IR_I32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_U8: + case IR_U16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_U32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_U64: +#else + case IR_U32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_ADDR: + if ((el_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(el_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(el_type), ref); + } + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + if (res_type && Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + } + + return 1; +} + +static int zend_jit_ffi_incdec_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_field *field, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, field_type, ptr, res_addr)) { + return 0; + } + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_incdec_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_symbol *sym, + zend_jit_addr res_addr, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, sym_type, ptr, res_addr)) { + return 0; + } + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} +#endif + static int zend_jit_incdec_obj(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index a08b61c8fc82e..dfb91170aae15 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4907,6 +4907,53 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par on_this = op_array->opcodes[op_array_ssa->vars[op_array_ssa->ops[opline-op_array->opcodes].op1_use].definition].opcode == ZEND_FETCH_THIS; } } +#ifdef HAVE_FFI + if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { + zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + + if (field + && !field->is_const + && !field->bits + && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(field->type) + && (ZEND_FFI_TYPE(field->type)->kind < ZEND_FFI_TYPE_POINTER + || (ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(ZEND_FFI_TYPE(field->type)->pointer.type)->size != 0))) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_incdec_obj(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, field, + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + goto done; + } + } else if (op1_ffi_symbols) { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + if (sym + && sym->kind == ZEND_FFI_SYM_VAR + && ZEND_FFI_TYPE(sym->type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(sym->type) + && (ZEND_FFI_TYPE(sym->type)->kind < ZEND_FFI_TYPE_POINTER + || (ZEND_FFI_TYPE(sym->type)->kind == ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(ZEND_FFI_TYPE(sym->type)->pointer.type)->size != 0))) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_incdec_sym(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, sym, + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + goto done; + } + } +#endif if (!zend_jit_incdec_obj(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce, From 42cc7cd442cc921454143fd8254842b7d45fc544 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 25 Jul 2024 12:10:00 +0300 Subject: [PATCH 047/101] Remove useless checks --- ext/opcache/jit/zend_jit_trace.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index dfb91170aae15..84233e846836c 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6398,10 +6398,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } #ifdef HAVE_FFI - if ((opline->opcode == ZEND_FETCH_OBJ_R - || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG - || opline->opcode == ZEND_FETCH_OBJ_W) - && op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { + if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_STRUCT) { zend_ffi_field *field = zend_hash_find_ptr(&op1_ffi_type->record.fields, Z_STR_P(RT_CONSTANT(opline, opline->op2))); From 1857948fbe38ad24ed3a43e35660ec77493cc4db Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 25 Jul 2024 12:21:11 +0300 Subject: [PATCH 048/101] Move FFI related JIT code generation into zend_jit_ir_ffi.c --- ext/opcache/jit/Makefile.frag | 3 +- ext/opcache/jit/Makefile.frag.w32 | 1 + ext/opcache/jit/zend_jit.c | 4 + ext/opcache/jit/zend_jit_ir.c | 1965 ++--------------------------- ext/opcache/jit/zend_jit_ir_ffi.c | 1806 ++++++++++++++++++++++++++ 5 files changed, 1896 insertions(+), 1883 deletions(-) create mode 100644 ext/opcache/jit/zend_jit_ir_ffi.c diff --git a/ext/opcache/jit/Makefile.frag b/ext/opcache/jit/Makefile.frag index 863556d6744d7..7667d0bc005a5 100644 --- a/ext/opcache/jit/Makefile.frag +++ b/ext/opcache/jit/Makefile.frag @@ -18,7 +18,8 @@ $(builddir)/jit/ir/ir.lo: \ $(builddir)/jit/zend_jit.lo: \ $(srcdir)/jit/zend_jit_helpers.c \ - $(srcdir)/jit/zend_jit_ir.c + $(srcdir)/jit/zend_jit_ir.c \ + $(srcdir)/jit/zend_jit_ir_ffi.c # For non-GNU make, jit/zend_jit.lo and ./jit/zend_jit.lo are considered distinct targets. # Use this workaround to allow building from inside ext/opcache. diff --git a/ext/opcache/jit/Makefile.frag.w32 b/ext/opcache/jit/Makefile.frag.w32 index 7caf814588705..ec12dea4fe267 100644 --- a/ext/opcache/jit/Makefile.frag.w32 +++ b/ext/opcache/jit/Makefile.frag.w32 @@ -33,6 +33,7 @@ $(BUILD_DIR)\ext\opcache\jit\ir\ir.obj: \ $(BUILD_DIR)\ext\opcache\jit\zend_jit.obj: \ ext\opcache\jit\zend_jit_ir.c \ + ext\opcache\jit\zend_jit_ir_ffi.c \ ext\opcache\jit\zend_jit_helpers.c \ ext\opcache\jit\ir\ir.h \ ext\opcache\jit\ir\ir_builder.h diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 8133d58c57d1c..67050f830509b 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -915,6 +915,10 @@ static bool zend_jit_may_be_modified(const zend_function *func, const zend_op_ar #include "jit/zend_jit_ir.c" +#if HAVE_FFI +# include "jit/zend_jit_ir_ffi.c" +#endif + #if defined(__clang__) # pragma clang diagnostic pop #endif diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index d5c0ba02629d7..13ded51aa222f 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10617,397 +10617,6 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen return 1; } -#ifdef HAVE_FFI -static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) -{ - return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); -} - -static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - int use, - int def, - zend_jit_addr addr, - HashTable *ffi_symbols, - zend_jit_ffi_info *ffi_info); - -static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - zend_ffi_symbol *sym, - HashTable *op1_ffi_symbols, - zend_jit_ffi_info *ffi_info) -{ - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { - return 0; - } - - return 1; -} - -static int zend_jit_ffi_send_val(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - zend_jit_addr op1_def_addr, - zend_ffi_type *op1_ffi_type) -{ - zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; - zend_jit_trace_stack *stack = call->stack; - zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; - zend_ffi_type *type; - ir_ref ref = IR_UNUSED; - - ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); - type = ZEND_FFI_TYPE(sym->type); - ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); - if (type->attr & ZEND_FFI_ATTR_VARIADIC) { - ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) >= zend_hash_num_elements(type->func.args)); - } else { - ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) == zend_hash_num_elements(type->func.args)); - } - ZEND_ASSERT(opline->op2.num > 0 && opline->op2.num <= TRACE_FRAME_NUM_ARGS(call)); - - if (opline->op2.num - 1 < zend_hash_num_elements(type->func.args)) { - type = zend_hash_index_find_ptr(type->func.args, opline->op2.num - 1); - type = ZEND_FFI_TYPE(type); - - switch (type->kind) { - case ZEND_FFI_TYPE_FLOAT: - if (op1_info == MAY_BE_LONG) { - ref = ir_INT2F((jit_Z_LVAL(jit, op1_addr))); - } else if (op1_info == MAY_BE_DOUBLE) { - ref = ir_D2F(jit_Z_DVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_DOUBLE: - if (op1_info == MAY_BE_LONG) { - ref = ir_INT2D((jit_Z_LVAL(jit, op1_addr))); - } else if (op1_info == MAY_BE_DOUBLE) { - ref = jit_Z_DVAL(jit, op1_addr); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_UINT8: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_U8(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_SINT8: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_I8(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_UINT16: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_U16(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_SINT16: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_I16(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case ZEND_FFI_TYPE_UINT32: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_U32(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_SINT32: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_I32(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_UINT64: - case ZEND_FFI_TYPE_SINT64: - if (op1_info == MAY_BE_LONG) { - ref = jit_Z_LVAL(jit, op1_addr); - } else { - ZEND_UNREACHABLE(); - } - break; -#else - case ZEND_FFI_TYPE_UINT32: - case ZEND_FFI_TYPE_SINT32: - if (op1_info == MAY_BE_LONG) { - ref = jit_Z_LVAL(jit, op1_addr); - } else { - ZEND_UNREACHABLE(); - } - break; -#endif - case ZEND_FFI_TYPE_BOOL: - if (op1_info == MAY_BE_NULL || op1_info == MAY_BE_FALSE) { - ref = IR_FALSE; - } else if (op1_info == MAY_BE_TRUE) { - ref = IR_TRUE; - } else if (op1_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { - ref = ir_SUB_U8(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_FALSE)); - } else if (op1_info == MAY_BE_LONG) { - ref = ir_NE(jit_Z_LVAL(jit, op1_addr), ir_CONST_LONG(0)); - } else if (op1_info == MAY_BE_DOUBLE) { - ref = ir_NE(jit_Z_DVAL(jit, op1_addr), ir_CONST_DOUBLE(0.0)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_CHAR: - if (op1_info == MAY_BE_LONG) { - ref = ir_TRUNC_C(jit_Z_LVAL(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - case ZEND_FFI_TYPE_POINTER: - if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING - && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); - } else if (op1_ffi_type - && op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER - && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type)) { - ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); - } else if (op1_ffi_type - && op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY - && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type)) { - ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); - } else { - ZEND_UNREACHABLE(); - } - break; - default: - ZEND_UNREACHABLE(); - } - } else { - if (op1_info == MAY_BE_NULL) { - ref = IR_NULL; - } else if (op1_info == MAY_BE_FALSE) { - ref = IR_FALSE; - } else if (op1_info == MAY_BE_TRUE) { - ref = IR_TRUE; - } else if (op1_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { - ref = ir_SUB_U8(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_FALSE)); - } else if (op1_info == MAY_BE_LONG) { - ref = jit_Z_LVAL(jit, op1_addr); - } else if (op1_info == MAY_BE_DOUBLE) { - ref = jit_Z_DVAL(jit, op1_addr); - } else if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) { - ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); - } else { - ZEND_UNREACHABLE(); - } - } - - SET_STACK_REF(stack, opline->op2.num - 1, ref); - - return 1; -} - -static int zend_jit_ffi_do_call_sym(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - zend_jit_addr res_addr) -{ - zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; - zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; - uint32_t i, num_args; - zend_ffi_type *type; - ir_type ret_type = IR_VOID; - ir_ref ref = IR_UNUSED; - - ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); - type = ZEND_FFI_TYPE(sym->type); - ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); - - switch (ZEND_FFI_TYPE(type->func.ret_type)->kind) { - case ZEND_FFI_TYPE_VOID: - ret_type = IR_VOID; - break; - case ZEND_FFI_TYPE_FLOAT: - ret_type = IR_FLOAT; - break; - case ZEND_FFI_TYPE_DOUBLE: - ret_type = IR_DOUBLE; - break; - case ZEND_FFI_TYPE_UINT8: - ret_type = IR_U8; - break; - case ZEND_FFI_TYPE_SINT8: - ret_type = IR_I8; - break; - case ZEND_FFI_TYPE_UINT16: - ret_type = IR_U16; - break; - case ZEND_FFI_TYPE_SINT16: - ret_type = IR_I16; - break; - case ZEND_FFI_TYPE_UINT32: - ret_type = IR_U32; - break; - case ZEND_FFI_TYPE_SINT32: - ret_type = IR_I32; - break; -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case ZEND_FFI_TYPE_UINT64: - ret_type = IR_U64; - break; - case ZEND_FFI_TYPE_SINT64: - ret_type = IR_I64; - break; -#endif - case ZEND_FFI_TYPE_BOOL: - ret_type = IR_BOOL; - break; - case ZEND_FFI_TYPE_CHAR: - ret_type = IR_CHAR; - break; - case ZEND_FFI_TYPE_POINTER: - ret_type = IR_ADDR; - break; - default: - ZEND_UNREACHABLE(); - } - - num_args = TRACE_FRAME_NUM_ARGS(call); - if (num_args) { - ir_ref *args = alloca(sizeof(ir_ref) * num_args); - zend_jit_trace_stack *stack = call->stack; - - for (i = 0; i < num_args; i++) { - args[i] = STACK_REF(stack, i); - } - if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { - ref = ir_CALL_N(ret_type, ir_CONST_FC_FUNC(sym->addr), num_args, args); - } else { - ref = ir_CALL_N(ret_type, ir_CONST_FUNC(sym->addr), num_args, args); - } - } else { - ZEND_ASSERT(!type->func.args); - if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { - ref = ir_CALL(ret_type, ir_CONST_FC_FUNC(sym->addr)); - } else { - ref = ir_CALL(ret_type, ir_CONST_FUNC(sym->addr)); - } - } - - if (RETURN_VALUE_USED(opline)) { - zend_ffi_type *ret_type = ZEND_FFI_TYPE(type->func.ret_type); - uint32_t res_type = IS_UNDEF; - - switch (ret_type->kind) { - case ZEND_FFI_TYPE_VOID: - res_type = IS_NULL; - break; - case ZEND_FFI_TYPE_FLOAT: - jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); - res_type = IS_DOUBLE; - break; - case ZEND_FFI_TYPE_DOUBLE: - jit_set_Z_DVAL(jit, res_addr, ref); - res_type = IS_DOUBLE; - break; - case ZEND_FFI_TYPE_UINT8: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT8: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_UINT16: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT16: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); - res_type = IS_LONG; - break; -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case ZEND_FFI_TYPE_UINT32: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT32: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_UINT64: - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT64: - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; -#else - case ZEND_FFI_TYPE_UINT32: - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT32: - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; -#endif - case ZEND_FFI_TYPE_BOOL: - jit_set_Z_TYPE_INFO(jit, res_addr, - ir_ADD_U32(ir_ZEXT_U32(ref), ir_CONST_U32(IS_FALSE))); - return 1; - case ZEND_FFI_TYPE_CHAR: - jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( - ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), - ir_MUL_L(ir_ZEXT_L(ref), ir_CONST_LONG(sizeof(void*)))))); - res_type = IS_STRING; - break; - case ZEND_FFI_TYPE_POINTER: - if ((ret_type->attr & ZEND_FFI_ATTR_CONST) - && ZEND_FFI_TYPE(ret_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); - return 1; - } else { - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), - jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ret_type), ref); - return 1; - } - break; - default: - ZEND_UNREACHABLE(); - } - - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, res_type); - } - } - - return 1; -} -#endif - static int zend_jit_constructor(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, int call_level, int next_block) { ir_ref if_skip_constructor = jit_IF_ex(jit, jit_CMP_IP(jit, IR_NE, opline), next_block); @@ -13121,400 +12730,73 @@ static int zend_jit_fetch_dim_read(zend_jit_ctx *jit, return 1; } -#ifdef HAVE_FFI -static int zend_jit_class_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_class_entry *ce); - -static int zend_jit_ffi_type_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_ffi_type *ffi_type) +static zend_jit_addr zend_jit_prepare_array_update(zend_jit_ctx *jit, + const zend_op *opline, + uint32_t *op1_info_ptr, + zend_jit_addr op1_addr, + ir_ref *if_type, + ir_ref *ht_ref, + int *may_throw) { - int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); - const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + ir_ref ref = IR_UNUSED; + ir_ref array_reference_end = IR_UNUSED, array_reference_ref = IR_UNUSED; + ir_refs *array_inputs, *array_values; + uint32_t op1_info = *op1_info_ptr; - if (!exit_addr) { - return 0; - } + ir_refs_init(array_inputs, 4); + ir_refs_init(array_values, 4); - ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, type))), ir_CONST_ADDR(ffi_type)), - ir_CONST_ADDR(exit_addr)); + ref = jit_ZVAL_ADDR(jit, op1_addr); + if (op1_info & MAY_BE_REF) { + ir_ref if_reference, if_array, end1, ref2; - return 1; -} + *may_throw = 1; + if_reference = jit_if_Z_TYPE(jit, op1_addr, IS_REFERENCE); + ir_IF_FALSE(if_reference); + end1 = ir_END(); + ir_IF_TRUE_cold(if_reference); + array_reference_ref = ir_ADD_OFFSET(jit_Z_PTR_ref(jit, ref), offsetof(zend_reference, val)); + if_array = jit_if_Z_TYPE_ref(jit, array_reference_ref, ir_CONST_U8(IS_ARRAY)); + ir_IF_TRUE(if_array); + array_reference_end = ir_END(); + ir_IF_FALSE_cold(if_array); + if (opline->opcode != ZEND_FETCH_DIM_RW && opline->opcode != ZEND_ASSIGN_DIM_OP) { + jit_SET_EX_OPLINE(jit, opline); + } + ref2 = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_prepare_assign_dim_ref), ref); + ir_GUARD(ref2, jit_STUB_ADDR(jit, jit_stub_exception_handler_undef)); -static int zend_jit_ffi_abc(zend_jit_ctx *jit, - const zend_op *opline, - zend_ffi_type *ffi_type, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range) -{ - int32_t exit_point; - const void *exit_addr; + ir_MERGE_WITH(end1); + ref = ir_PHI_2(IR_ADDR, ref2, ref); + op1_addr = ZEND_ADDR_REF_ZVAL(ref); + } - ZEND_ASSERT(op2_info == MAY_BE_LONG); - if (ffi_type->kind == ZEND_FFI_TYPE_ARRAY - && !(ffi_type->attr & (ZEND_FFI_ATTR_VLA|ZEND_FFI_ATTR_INCOMPLETE_ARRAY))) { - if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { - zval *zv = Z_ZV(op2_addr); - ZEND_ASSERT(Z_TYPE_P(zv) == IS_LONG); - if (Z_LVAL_P(zv) < 0 || Z_LVAL_P(zv) >= ffi_type->array.length) { - /* Always out of range */ - exit_point = zend_jit_trace_get_exit_point(opline, 0); - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!exit_addr) { - return 0; - } - jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); - } - } else if (!op2_range || op2_range->min < 0 || op2_range->max >= ffi_type->array.length) { - if (op2_range && (op2_range->max < 0 || op2_range->min >= ffi_type->array.length)) { - /* Always out of range */ - exit_point = zend_jit_trace_get_exit_point(opline, 0); - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!exit_addr) { - return 0; - } - jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); - } else { - /* Array Bounds Check */ - exit_point = zend_jit_trace_get_exit_point(opline, 0); - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!exit_addr) { - return 0; - } - ir_GUARD(ir_ULT(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ffi_type->array.length)), - ir_CONST_ADDR(exit_addr)); + if (op1_info & MAY_BE_ARRAY) { + ir_ref op1_ref = ref; + + if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { + *if_type = jit_if_Z_TYPE(jit, op1_addr, IS_ARRAY); + ir_IF_TRUE(*if_type); + } + if (array_reference_end) { + ir_MERGE_WITH(array_reference_end); + op1_ref = ir_PHI_2(IR_ADDR, ref, array_reference_ref); + } + // JIT: SEPARATE_ARRAY() + ref = jit_Z_PTR_ref(jit, op1_ref); + if (RC_MAY_BE_N(op1_info)) { + if (RC_MAY_BE_1(op1_info)) { + ir_ref if_refcount_1 = ir_IF(ir_EQ(jit_GC_REFCOUNT(jit, ref), ir_CONST_U32(1))); + ir_IF_TRUE(if_refcount_1); + ir_refs_add(array_inputs, ir_END()); + ir_refs_add(array_values, ref); + ir_IF_FALSE(if_refcount_1); } - } - } - - return 1; -} - -static int zend_jit_ffi_read(zend_jit_ctx *jit, - zend_ffi_type *ffi_type, - ir_ref ptr, - zend_jit_addr res_addr) -{ - uint32_t res_type; - - switch (ffi_type->kind) { - case ZEND_FFI_TYPE_FLOAT: - jit_set_Z_DVAL(jit, res_addr, ir_F2D(ir_LOAD_F(ptr))); - res_type = IS_DOUBLE; - break; - case ZEND_FFI_TYPE_DOUBLE: - jit_set_Z_DVAL(jit, res_addr, ir_LOAD_D(ptr)); - res_type = IS_DOUBLE; - break; - case ZEND_FFI_TYPE_UINT8: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U8(ptr))); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT8: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I8(ptr))); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_UINT16: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U16(ptr))); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT16: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I16(ptr))); - res_type = IS_LONG; - break; -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case ZEND_FFI_TYPE_UINT32: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U32(ptr))); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT32: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I32(ptr))); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_UINT64: - jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U64(ptr)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT64: - jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I64(ptr)); - res_type = IS_LONG; - break; -#else - case ZEND_FFI_TYPE_UINT32: - jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U32(ptr)); - res_type = IS_LONG; - break; - case ZEND_FFI_TYPE_SINT32: - jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I32(ptr)); - res_type = IS_LONG; - break; -#endif - case ZEND_FFI_TYPE_BOOL: - jit_set_Z_TYPE_INFO(jit, res_addr, - ir_ADD_U32(ir_ZEXT_U32(ir_LOAD_U8(ptr)), ir_CONST_U32(IS_FALSE))); - return 1; - case ZEND_FFI_TYPE_CHAR: - jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( - ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), - ir_MUL_L(ir_ZEXT_L(ir_LOAD_U8(ptr)), ir_CONST_LONG(sizeof(void*)))))); - res_type = IS_STRING; - break; - case ZEND_FFI_TYPE_ARRAY: - case ZEND_FFI_TYPE_STRUCT: - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_obj), - jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ptr); - return 1; - case ZEND_FFI_TYPE_POINTER: - if ((ffi_type->attr & ZEND_FFI_ATTR_CONST) - && ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ptr); - return 1; - } else { - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), - jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ir_LOAD_A(ptr)); - return 1; - } - break; - break; - default: - ZEND_UNREACHABLE(); - } - - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, res_type); - } - - return 1; -} - -static int zend_jit_ffi_guard(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - int use, - int def, - ir_ref ref, - zend_ffi_type *ffi_type, - zend_jit_ffi_info *ffi_info) -{ - if (ssa->var_info - && use >= 0 - && ssa->var_info[use].ce != zend_ffi_cdata_ce) { - if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_cdata_ce)) { - return 0; - } - ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[use].ce = zend_ffi_cdata_ce; - ssa->var_info[use].is_instanceof = 0; - if (def >= 0) { - ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[def].ce = zend_ffi_cdata_ce; - ssa->var_info[def].is_instanceof = 0; - } - } - - if (ffi_info - && use >= 0 - && (ffi_info[use].type != ffi_type - || (ffi_info[use].info & FFI_TYPE_GUARD))) { - if (!zend_jit_ffi_type_guard(jit, opline, ref, ffi_type)) { - return 0; - } - ffi_info[use].info &= ~FFI_TYPE_GUARD; - ffi_info[use].type = ffi_type; - if (def >= 0) { - ffi_info[def].info &= ~FFI_TYPE_GUARD; - ffi_info[def].type = ffi_type; - } - } - - return 1; -} - -static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - int use, - int def, - zend_jit_addr addr, - HashTable *ffi_symbols, - zend_jit_ffi_info *ffi_info) -{ - ir_ref ref = IR_UNUSED; - - if (ssa->var_info - && use >= 0 - && ssa->var_info[use].ce != zend_ffi_ce) { - ref = jit_Z_PTR(jit, addr); - if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_ce)) { - return 0; - } - ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[use].ce = zend_ffi_ce; - ssa->var_info[use].is_instanceof = 0; - if (def >= 0) { - ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[def].ce = zend_ffi_ce; - ssa->var_info[def].is_instanceof = 0; - } - } - - if (ffi_info - && use >= 0 - && (ffi_info[use].symbols != ffi_symbols - || (ffi_info[use].info & FFI_SYMBOLS_GUARD))) { - if (!ref) { - ref = jit_Z_PTR(jit, addr); - } - - int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); - const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); - - if (!exit_addr) { - return 0; - } - - ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(ref, offsetof(zend_ffi, symbols))), ir_CONST_ADDR(ffi_symbols)), - ir_CONST_ADDR(exit_addr)); - - ffi_info[use].info &= ~FFI_SYMBOLS_GUARD; - ffi_info[use].symbols = ffi_symbols; - if (def >= 0) { - ffi_info[def].info &= ~FFI_SYMBOLS_GUARD; - ffi_info[def].symbols = ffi_symbols; - } - } - - return 1; -} - -static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - bool op1_avoid_refcounting, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t res_info, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } - - if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { - return 0; - } - - ir_ref cdata_ref = jit_FFI_CDATA_PTR(jit, obj_ref); -// ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - - if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - cdata_ref = ir_LOAD_A(cdata_ref); - } - - ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - - if (opline->opcode == ZEND_FETCH_DIM_W || opline->opcode == ZEND_FETCH_DIM_RW) { - jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), - ptr, ir_CONST_ADDR(el_type))); - jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); - } else { - if (!zend_jit_ffi_read(jit, el_type, ptr, res_addr)) { - return 0; - } - } - - if (res_info & MAY_BE_GUARD) { - // TODO: ??? - ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; - } - - if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting && !op1_indirect) { - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - } - - return 1; -} -#endif - -static zend_jit_addr zend_jit_prepare_array_update(zend_jit_ctx *jit, - const zend_op *opline, - uint32_t *op1_info_ptr, - zend_jit_addr op1_addr, - ir_ref *if_type, - ir_ref *ht_ref, - int *may_throw) -{ - ir_ref ref = IR_UNUSED; - ir_ref array_reference_end = IR_UNUSED, array_reference_ref = IR_UNUSED; - ir_refs *array_inputs, *array_values; - uint32_t op1_info = *op1_info_ptr; - - ir_refs_init(array_inputs, 4); - ir_refs_init(array_values, 4); - - ref = jit_ZVAL_ADDR(jit, op1_addr); - if (op1_info & MAY_BE_REF) { - ir_ref if_reference, if_array, end1, ref2; - - *may_throw = 1; - if_reference = jit_if_Z_TYPE(jit, op1_addr, IS_REFERENCE); - ir_IF_FALSE(if_reference); - end1 = ir_END(); - ir_IF_TRUE_cold(if_reference); - array_reference_ref = ir_ADD_OFFSET(jit_Z_PTR_ref(jit, ref), offsetof(zend_reference, val)); - if_array = jit_if_Z_TYPE_ref(jit, array_reference_ref, ir_CONST_U8(IS_ARRAY)); - ir_IF_TRUE(if_array); - array_reference_end = ir_END(); - ir_IF_FALSE_cold(if_array); - if (opline->opcode != ZEND_FETCH_DIM_RW && opline->opcode != ZEND_ASSIGN_DIM_OP) { - jit_SET_EX_OPLINE(jit, opline); - } - ref2 = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_prepare_assign_dim_ref), ref); - ir_GUARD(ref2, jit_STUB_ADDR(jit, jit_stub_exception_handler_undef)); - - ir_MERGE_WITH(end1); - ref = ir_PHI_2(IR_ADDR, ref2, ref); - op1_addr = ZEND_ADDR_REF_ZVAL(ref); - } - - if (op1_info & MAY_BE_ARRAY) { - ir_ref op1_ref = ref; - - if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) { - *if_type = jit_if_Z_TYPE(jit, op1_addr, IS_ARRAY); - ir_IF_TRUE(*if_type); - } - if (array_reference_end) { - ir_MERGE_WITH(array_reference_end); - op1_ref = ir_PHI_2(IR_ADDR, ref, array_reference_ref); - } - // JIT: SEPARATE_ARRAY() - ref = jit_Z_PTR_ref(jit, op1_ref); - if (RC_MAY_BE_N(op1_info)) { - if (RC_MAY_BE_1(op1_info)) { - ir_ref if_refcount_1 = ir_IF(ir_EQ(jit_GC_REFCOUNT(jit, ref), ir_CONST_U32(1))); - ir_IF_TRUE(if_refcount_1); - ir_refs_add(array_inputs, ir_END()); - ir_refs_add(array_values, ref); - ir_IF_FALSE(if_refcount_1); - } - ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_array_dup), op1_ref); - } - if (array_inputs->count || (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) { - ir_refs_add(array_inputs, ir_END()); - ir_refs_add(array_values, ref); + ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_array_dup), op1_ref); + } + if (array_inputs->count || (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL))) { + ir_refs_add(array_inputs, ir_END()); + ir_refs_add(array_values, ref); } } @@ -14111,308 +13393,6 @@ static int zend_jit_assign_dim(zend_jit_ctx *jit, return 1; } -#ifdef HAVE_FFI -static int zend_jit_ffi_write(zend_jit_ctx *jit, - zend_ffi_type *ffi_type, - ir_ref ptr, - uint32_t val_info, - zend_jit_addr val_addr, - zend_ffi_type *val_ffi_type, - zend_jit_addr res_addr) -{ - ir_ref ref = IR_UNUSED; - - switch (ffi_type->kind) { - case ZEND_FFI_TYPE_FLOAT: - if (val_info == MAY_BE_LONG) { - ref = ir_INT2F(jit_Z_LVAL(jit, val_addr)); - } else if (val_info == MAY_BE_DOUBLE) { - ref = ir_D2F(jit_Z_DVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_F(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_F2D(ref); - jit_set_Z_DVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_DOUBLE); - } - } - break; - case ZEND_FFI_TYPE_DOUBLE: - if (val_info == MAY_BE_LONG) { - ref = ir_INT2D(jit_Z_LVAL(jit, val_addr)); - } else if (val_info == MAY_BE_DOUBLE) { - ref = jit_Z_DVAL(jit, val_addr); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_D(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - jit_set_Z_DVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_DOUBLE); - } - } - break; - case ZEND_FFI_TYPE_BOOL: - if (val_info == MAY_BE_FALSE) { - ir_STORE(ptr, IR_FALSE); - if (res_addr) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_FALSE); - } - return 1; - } else if (val_info == MAY_BE_TRUE) { - ir_STORE(ptr, IR_TRUE); - if (res_addr) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_TRUE); - } - return 1; - } else if (val_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { - if (res_addr) { - ref = jit_Z_TYPE_INFO(jit, val_addr); - jit_set_Z_TYPE_INFO_ex(jit, res_addr, ref); - ref = ir_TRUNC_U8(ref); - } else { - ref = jit_Z_TYPE(jit, val_addr); - } - ir_STORE(ptr, ir_SUB_U8(ref, ir_CONST_U8(IS_FALSE))); - return 1; - } - ZEND_FALLTHROUGH; - case ZEND_FFI_TYPE_UINT8: - if (val_info == MAY_BE_LONG) { - ref = ir_TRUNC_U8(jit_Z_LVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_U8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_ZEXT_L(ref); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; - case ZEND_FFI_TYPE_SINT8: - case ZEND_FFI_TYPE_CHAR: - if (val_info == MAY_BE_LONG) { - ref = ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_I8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_SEXT_L(ref); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; - case ZEND_FFI_TYPE_UINT16: - if (val_info == MAY_BE_LONG) { - ref = ir_TRUNC_U16(jit_Z_LVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_U16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_ZEXT_L(ref); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; - case ZEND_FFI_TYPE_SINT16: - if (val_info == MAY_BE_LONG) { - ref = ir_TRUNC_I16(jit_Z_LVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_I16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_SEXT_L(ref); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case ZEND_FFI_TYPE_UINT32: - if (val_info == MAY_BE_LONG) { - ref = ir_TRUNC_U32(jit_Z_LVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_U32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_ZEXT_L(ref); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; - case ZEND_FFI_TYPE_SINT32: - if (val_info == MAY_BE_LONG) { - ref = ir_TRUNC_I32(jit_Z_LVAL(jit, val_addr)); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ref = ir_SEXT_L(ref); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; - case ZEND_FFI_TYPE_UINT64: - case ZEND_FFI_TYPE_SINT64: - if (val_info == MAY_BE_LONG) { - ref = jit_Z_LVAL(jit, val_addr); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_I64(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; -#else - case ZEND_FFI_TYPE_UINT32: - case ZEND_FFI_TYPE_SINT32: - if (val_info == MAY_BE_LONG) { - ref = jit_Z_LVAL(jit, val_addr); - } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { - ref = ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - } - break; -#endif - case ZEND_FFI_TYPE_POINTER: - if (val_info == MAY_BE_NULL) { - ref = IR_NULL; - } else if (val_ffi_type - && val_ffi_type->kind == ZEND_FFI_TYPE_POINTER - && (val_ffi_type == ffi_type - || ZEND_FFI_TYPE(val_ffi_type->pointer.type) == ZEND_FFI_TYPE(ffi_type->pointer.type) - || ZEND_FFI_TYPE(val_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID - || ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID)) { - ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); - } else { - ZEND_UNREACHABLE(); - } - ir_STORE(ptr, ref); - if (res_addr) { - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), - jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ref); - } - break; - default: - ZEND_UNREACHABLE(); - } - - return 1; -} - -static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t val_info, - zend_jit_addr val_addr, - zend_jit_addr val_def_addr, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_ffi_type *val_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } - - if (val_addr != val_def_addr && val_def_addr) { - if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { - return 0; - } - if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { - val_addr = val_def_addr; - } - } - - if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { - return 0; - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - - if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - cdata_ref = ir_LOAD_A(cdata_ref); - } - - ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - - ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); - - if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { - return 0; - } - - jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} -#endif - static int zend_jit_assign_dim_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info, @@ -14627,240 +13607,6 @@ static int zend_jit_assign_dim_op(zend_jit_ctx *jit, return 1; } -#ifdef HAVE_FFI -static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, - const zend_op *opline, - uint8_t opcode, - zend_ffi_type *el_type, - ir_ref op1, - uint32_t op2_info, - zend_jit_addr op2_addr) -{ - ir_op op; - ir_type type; - ir_ref op2; - - switch (opcode) { - case ZEND_ADD: - op = IR_ADD; - break; - case ZEND_SUB: - op = IR_SUB; - break; - case ZEND_MUL: - op = IR_MUL; - break; - case ZEND_BW_OR: - op = IR_OR; - break; - case ZEND_BW_AND: - op = IR_AND; - break; - case ZEND_BW_XOR: - op = IR_XOR; - break; - case ZEND_SL: - // TODO: negative shift - op = IR_SHL; - break; - case ZEND_SR: - // TODO: negative shift - op = IR_SAR; /* TODO: SAR or SHR */ - break; - case ZEND_MOD: - // TODO: mod by zero and -1 - op = IR_MOD; - break; - default: - ZEND_UNREACHABLE(); - return 0; - } - - switch (el_type->kind) { - case ZEND_FFI_TYPE_FLOAT: - ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); - type = IR_FLOAT; - if (op2_info == MAY_BE_LONG) { - op2 = ir_INT2F(jit_Z_LVAL(jit, op2_addr)); - } else if (op2_info == MAY_BE_DOUBLE) { - op2 = ir_D2F(jit_Z_DVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_DOUBLE: - ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); - type = IR_DOUBLE; - if (op2_info == MAY_BE_LONG) { - op2 = ir_INT2D(jit_Z_LVAL(jit, op2_addr)); - } else if (op2_info == MAY_BE_DOUBLE) { - op2 = jit_Z_DVAL(jit, op2_addr); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_BOOL: - type = IR_U8; - ZEND_ASSERT(opcode == ZEND_BW_AND || opcode == ZEND_BW_OR); - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_U8(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_UINT8: - type = IR_U8; - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_U8(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_SINT8: - case ZEND_FFI_TYPE_CHAR: - type = IR_I8; - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_I8(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_UINT16: - type = IR_U16; - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_U16(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_SINT16: - type = IR_I16; - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_I16(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case ZEND_FFI_TYPE_UINT32: - type = IR_U32; - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_U32(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_SINT32: - type = IR_I32; - if (op2_info == MAY_BE_LONG) { - op2 = ir_TRUNC_I32(jit_Z_LVAL(jit, op2_addr)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - case ZEND_FFI_TYPE_UINT64: - case ZEND_FFI_TYPE_SINT64: - type = IR_I64; - if (op2_info == MAY_BE_LONG) { - op2 = jit_Z_LVAL(jit, op2_addr); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; -#else - case ZEND_FFI_TYPE_UINT32: - case ZEND_FFI_TYPE_SINT32: - type = IR_I32; - if (op2_info == MAY_BE_LONG) { - op2 = jit_Z_LVAL(jit, op2_addr); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; -#endif - case ZEND_FFI_TYPE_POINTER: - ZEND_ASSERT(opcode == ZEND_ADD || opcode == ZEND_SUB); - ZEND_ASSERT(ZEND_FFI_TYPE(el_type->pointer.type)->size != 0); - type = IR_ADDR; - if (op2_info == MAY_BE_LONG) { - op2 = ir_MUL_A(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ZEND_FFI_TYPE(el_type->pointer.type)->size)); - } else { - ZEND_UNREACHABLE(); - return 0; - } - break; - default: - ZEND_UNREACHABLE(); - return 0; - } - - ir_STORE(op1, ir_BINARY_OP(op, type, ir_LOAD(type, op1), op2)); - - return 1; -} - -static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - uint32_t op1_def_info, - zend_jit_addr op1_addr, - bool op1_indirect, - uint32_t op2_info, - zend_jit_addr op2_addr, - zend_ssa_range *op2_range, - uint32_t op1_data_info, - zend_jit_addr op3_addr, - zend_ssa_range *op1_data_range, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } - - if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { - return 0; - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - - if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - cdata_ref = ir_LOAD_A(cdata_ref); - } - - ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); - - if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, - el_type, ptr, op1_data_info, op3_addr)) { - return 0; - } - - jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, op1_data_info, opline); - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} -#endif - static int zend_jit_fe_reset(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info) { zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); @@ -15267,149 +14013,49 @@ static int zend_jit_fetch_this(zend_jit_ctx *jit, const zend_op *opline, const z if (!exit_addr) { return 0; - } - - jit_guard_Z_TYPE(jit, this_addr, IS_OBJECT, exit_addr); - - if (JIT_G(current_frame)) { - TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame)); - } - } - } else { - zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); - ir_ref if_object = jit_if_Z_TYPE(jit, this_addr, IS_OBJECT); - - ir_IF_FALSE_cold(if_object); - jit_SET_EX_OPLINE(jit, opline); - ir_IJMP(jit_STUB_ADDR(jit, jit_stub_invalid_this)); - - ir_IF_TRUE(if_object); - } - } - - if (!check_only) { - if (!zend_jit_load_this(jit, opline->result.var)) { - return 0; - } - } - - return 1; -} - -static int zend_jit_class_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_class_entry *ce) -{ - int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); - const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); - - if (!exit_addr) { - return 0; - } - - ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_object, ce))), ir_CONST_ADDR(ce)), - ir_CONST_ADDR(exit_addr)); - - return 1; -} - -#ifdef HAVE_FFI -static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - bool op1_avoid_refcounting, - zend_ffi_field *field, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - uint32_t res_info = RES_INFO(); - zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + } - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } + jit_guard_Z_TYPE(jit, this_addr, IS_OBJECT, exit_addr); - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + if (JIT_G(current_frame)) { + TRACE_FRAME_SET_THIS_CHECKED(JIT_G(current_frame)); + } + } + } else { + zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); + ir_ref if_object = jit_if_Z_TYPE(jit, this_addr, IS_OBJECT); - if (opline->opcode == ZEND_FETCH_OBJ_W) { - jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), - ptr, ir_CONST_ADDR(field_type))); - jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); - } else { - if (!zend_jit_ffi_read(jit, field_type, ptr, res_addr)) { - return 0; - } - } + ir_IF_FALSE_cold(if_object); + jit_SET_EX_OPLINE(jit, opline); + ir_IJMP(jit_STUB_ADDR(jit, jit_stub_invalid_this)); - if (res_info & MAY_BE_GUARD) { - // TODO: ??? - ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + ir_IF_TRUE(if_object); + } } - if (!op1_avoid_refcounting && !op1_indirect) { - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + if (!check_only) { + if (!zend_jit_load_this(jit, opline->result.var)) { + return 0; } } return 1; } -static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - bool op1_avoid_refcounting, - zend_ffi_symbol *sym, - zend_jit_addr res_addr, - HashTable *op1_ffi_symbols, - zend_jit_ffi_info *ffi_info) +static int zend_jit_class_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_class_entry *ce) { - uint32_t res_info = RES_INFO(); - zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + if (!exit_addr) { return 0; } - ir_ref ptr = ir_CONST_ADDR(sym->addr); - - if (opline->opcode == ZEND_FETCH_OBJ_W) { - jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), - ptr, ir_CONST_ADDR(sym_type))); - jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); - } else { - if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { - return 0; - } - } - - if (res_info & MAY_BE_GUARD) { - // TODO: ??? - ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; - } - - if (!op1_avoid_refcounting && !op1_indirect) { - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - } + ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_object, ce))), ir_CONST_ADDR(ce)), + ir_CONST_ADDR(exit_addr)); return 1; } -#endif static int zend_jit_fetch_obj(zend_jit_ctx *jit, const zend_op *opline, @@ -15930,107 +14576,6 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit, return 1; } -#ifdef HAVE_FFI -static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - zend_ffi_field *field, - uint32_t val_info, - zend_jit_addr val_addr, - zend_jit_addr val_def_addr, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_ffi_type *val_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } - - if (val_addr != val_def_addr && val_def_addr) { - if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { - return 0; - } - if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { - val_addr = val_def_addr; - } - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); - - ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); - - if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { - return 0; - } - - jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} - -static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - zend_ffi_symbol *sym, - uint32_t val_info, - zend_jit_addr val_addr, - zend_jit_addr val_def_addr, - zend_jit_addr res_addr, - HashTable *op1_ffi_symbols, - zend_ffi_type *val_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { - return 0; - } - - if (val_addr != val_def_addr && val_def_addr) { - if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { - return 0; - } - if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { - val_addr = val_def_addr; - } - } - - ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); - - ir_ref ptr = ir_CONST_ADDR(sym->addr); - if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { - return 0; - } - - jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} -#endif - static int zend_jit_assign_obj(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -16390,81 +14935,6 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit, return 1; } -#ifdef HAVE_FFI -static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - zend_ffi_field *field, - uint32_t val_info, - zend_jit_addr val_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); - - if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, - field_type, ptr, val_info, val_addr)) { - return 0; - } - - jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} - -static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - zend_ffi_symbol *sym, - uint32_t val_info, - zend_jit_addr val_addr, - HashTable *op1_ffi_symbols, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { - return 0; - } - - ir_ref ptr = ir_CONST_ADDR(sym->addr); - if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, - sym_type, ptr, val_info, val_addr)) { - return 0; - } - - jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} -#endif - static int zend_jit_assign_obj_op(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -16892,275 +15362,6 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit, return 1; } -#ifdef HAVE_FFI -static int zend_jit_ffi_incdec_helper(zend_jit_ctx *jit, - const zend_op *opline, - uint8_t opcode, - zend_ffi_type *el_type, - ir_ref op1, - zend_jit_addr res_addr) -{ - ir_op op; - ir_type type; - ir_ref op2; - ir_ref ref = IR_UNUSED; - - switch (opcode) { - case ZEND_PRE_INC_OBJ: - case ZEND_POST_INC_OBJ: - op = IR_ADD; - break; - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_DEC_OBJ: - op = IR_SUB; - break; - default: - ZEND_UNREACHABLE(); - return 0; - } - - switch (el_type->kind) { - case ZEND_FFI_TYPE_FLOAT: - type = IR_FLOAT; - op2 = ir_CONST_FLOAT(1.0); - break; - case ZEND_FFI_TYPE_DOUBLE: - type = IR_DOUBLE; - op2 = ir_CONST_DOUBLE(1.0); - break; - case ZEND_FFI_TYPE_UINT8: - type = IR_U8; - op2 = ir_CONST_U8(1); - break; - case ZEND_FFI_TYPE_SINT8: - case ZEND_FFI_TYPE_CHAR: - type = IR_I8; - op2 = ir_CONST_I8(1); - break; - case ZEND_FFI_TYPE_UINT16: - type = IR_U16; - op2 = ir_CONST_U16(1); - break; - case ZEND_FFI_TYPE_SINT16: - type = IR_I16; - op2 = ir_CONST_I16(1); - break; - case ZEND_FFI_TYPE_UINT32: - type = IR_U32; - op2 = ir_CONST_U32(1); - break; - case ZEND_FFI_TYPE_SINT32: - type = IR_I32; - op2 = ir_CONST_I32(1); - break; - case ZEND_FFI_TYPE_UINT64: - type = IR_U64; - op2 = ir_CONST_U64(1); - break; - case ZEND_FFI_TYPE_SINT64: - type = IR_I64; - op2 = ir_CONST_I64(1); - break; - case ZEND_FFI_TYPE_POINTER: - ZEND_ASSERT(ZEND_FFI_TYPE(el_type->pointer.type)->size != 0); - type = IR_ADDR; - op2 = ir_CONST_LONG(ZEND_FFI_TYPE(el_type->pointer.type)->size); - break; - default: - ZEND_UNREACHABLE(); - return 0; - } - - ref = ir_LOAD(type, op1); - - if (res_addr && (opcode == ZEND_POST_INC_OBJ || opcode == ZEND_POST_DEC_OBJ)) { - uint32_t res_type = IS_UNDEF; - - switch (type) { - case IR_FLOAT: - jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); - res_type = IS_DOUBLE; - break; - case IR_DOUBLE: - jit_set_Z_DVAL(jit, res_addr, ref); - res_type = IS_DOUBLE; - break; - case IR_I8: - case IR_I16: -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case IR_I32: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); - res_type = IS_LONG; - break; - case IR_I64: -#else - case IR_I32: -#endif - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; - case IR_U8: - case IR_U16: -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case IR_U32: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); - res_type = IS_LONG; - break; - case IR_U64: -#else - case IR_U32: -#endif - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; - case IR_ADDR: - if ((el_type->attr & ZEND_FFI_ATTR_CONST) - && ZEND_FFI_TYPE(el_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); - } else { - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), - jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(el_type), ref); - } - break; - default: - ZEND_UNREACHABLE(); - return 0; - } - if (res_type && Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, res_type); - } - } - - ref = ir_BINARY_OP(op, type, ref, op2); - ir_STORE(op1, ref); - - if (res_addr && (opcode == ZEND_PRE_INC_OBJ || opcode == ZEND_PRE_DEC_OBJ)) { - uint32_t res_type = IS_UNDEF; - - switch (type) { - case IR_FLOAT: - jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); - res_type = IS_DOUBLE; - break; - case IR_DOUBLE: - jit_set_Z_DVAL(jit, res_addr, ref); - res_type = IS_DOUBLE; - break; - case IR_I8: - case IR_I16: -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case IR_I32: - jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); - res_type = IS_LONG; - break; - case IR_I64: -#else - case IR_I32: -#endif - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; - case IR_U8: - case IR_U16: -#ifdef ZEND_ENABLE_ZVAL_LONG64 - case IR_U32: - jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); - res_type = IS_LONG; - break; - case IR_U64: -#else - case IR_U32: -#endif - jit_set_Z_LVAL(jit, res_addr, ref); - res_type = IS_LONG; - break; - case IR_ADDR: - if ((el_type->attr & ZEND_FFI_ATTR_CONST) - && ZEND_FFI_TYPE(el_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); - } else { - ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), - jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(el_type), ref); - } - break; - default: - ZEND_UNREACHABLE(); - return 0; - } - if (res_type && Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, res_type); - } - } - - return 1; -} - -static int zend_jit_ffi_incdec_obj(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - zend_ffi_field *field, - zend_jit_addr res_addr, - zend_ffi_type *op1_ffi_type, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); - - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { - return 0; - } - - ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); - ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); - - if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, field_type, ptr, res_addr)) { - return 0; - } - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} - -static int zend_jit_ffi_incdec_sym(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - uint32_t op1_info, - zend_jit_addr op1_addr, - bool op1_indirect, - zend_ffi_symbol *sym, - zend_jit_addr res_addr, - HashTable *op1_ffi_symbols, - zend_jit_ffi_info *ffi_info) -{ - zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { - return 0; - } - - ir_ref ptr = ir_CONST_ADDR(sym->addr); - if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, sym_type, ptr, res_addr)) { - return 0; - } - - if (!op1_indirect) { - jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); - } - - return 1; -} -#endif - static int zend_jit_incdec_obj(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c new file mode 100644 index 0000000000000..f84056abddc86 --- /dev/null +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -0,0 +1,1806 @@ +/* + * +----------------------------------------------------------------------+ + * | Zend JIT | + * +----------------------------------------------------------------------+ + * | Copyright (c) The PHP Group | + * +----------------------------------------------------------------------+ + * | This source file is subject to version 3.01 of the PHP license, | + * | that is bundled with this package in the file LICENSE, and is | + * | available through the world-wide-web at the following url: | + * | https://www.php.net/license/3_01.txt | + * | If you did not receive a copy of the PHP license and are unable to | + * | obtain it through the world-wide-web, please send a note to | + * | license@php.net so we can mail you a copy immediately. | + * +----------------------------------------------------------------------+ + * | Authors: Dmitry Stogov | + * +----------------------------------------------------------------------+ + */ + +static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) +{ + return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); +} + +static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + zend_jit_addr addr, + HashTable *ffi_symbols, + zend_jit_ffi_info *ffi_info); + +static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + zend_ffi_symbol *sym, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + return 1; +} + +static int zend_jit_ffi_send_val(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + zend_jit_addr op1_def_addr, + zend_ffi_type *op1_ffi_type) +{ + zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; + zend_jit_trace_stack *stack = call->stack; + zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; + zend_ffi_type *type; + ir_ref ref = IR_UNUSED; + + ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); + type = ZEND_FFI_TYPE(sym->type); + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); + if (type->attr & ZEND_FFI_ATTR_VARIADIC) { + ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) >= zend_hash_num_elements(type->func.args)); + } else { + ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) == zend_hash_num_elements(type->func.args)); + } + ZEND_ASSERT(opline->op2.num > 0 && opline->op2.num <= TRACE_FRAME_NUM_ARGS(call)); + + if (opline->op2.num - 1 < zend_hash_num_elements(type->func.args)) { + type = zend_hash_index_find_ptr(type->func.args, opline->op2.num - 1); + type = ZEND_FFI_TYPE(type); + + switch (type->kind) { + case ZEND_FFI_TYPE_FLOAT: + if (op1_info == MAY_BE_LONG) { + ref = ir_INT2F((jit_Z_LVAL(jit, op1_addr))); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = ir_D2F(jit_Z_DVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_DOUBLE: + if (op1_info == MAY_BE_LONG) { + ref = ir_INT2D((jit_Z_LVAL(jit, op1_addr))); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = jit_Z_DVAL(jit, op1_addr); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT8: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_U8(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT8: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_I8(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT16: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_U16(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT16: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_I16(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_U32(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_SINT32: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_I32(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else { + ZEND_UNREACHABLE(); + } + break; +#else + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else { + ZEND_UNREACHABLE(); + } + break; +#endif + case ZEND_FFI_TYPE_BOOL: + if (op1_info == MAY_BE_NULL || op1_info == MAY_BE_FALSE) { + ref = IR_FALSE; + } else if (op1_info == MAY_BE_TRUE) { + ref = IR_TRUE; + } else if (op1_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + ref = ir_SUB_U8(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_FALSE)); + } else if (op1_info == MAY_BE_LONG) { + ref = ir_NE(jit_Z_LVAL(jit, op1_addr), ir_CONST_LONG(0)); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = ir_NE(jit_Z_DVAL(jit, op1_addr), ir_CONST_DOUBLE(0.0)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_CHAR: + if (op1_info == MAY_BE_LONG) { + ref = ir_TRUNC_C(jit_Z_LVAL(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + case ZEND_FFI_TYPE_POINTER: + if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING + && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); + } else if (op1_ffi_type + && op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type)) { + ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); + } else if (op1_ffi_type + && op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY + && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type)) { + ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); + } else { + ZEND_UNREACHABLE(); + } + break; + default: + ZEND_UNREACHABLE(); + } + } else { + if (op1_info == MAY_BE_NULL) { + ref = IR_NULL; + } else if (op1_info == MAY_BE_FALSE) { + ref = IR_FALSE; + } else if (op1_info == MAY_BE_TRUE) { + ref = IR_TRUE; + } else if (op1_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + ref = ir_SUB_U8(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_FALSE)); + } else if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else if (op1_info == MAY_BE_DOUBLE) { + ref = jit_Z_DVAL(jit, op1_addr); + } else if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) { + ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); + } else { + ZEND_UNREACHABLE(); + } + } + + SET_STACK_REF(stack, opline->op2.num - 1, ref); + + return 1; +} + +static int zend_jit_ffi_do_call_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + zend_jit_addr res_addr) +{ + zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; + zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; + uint32_t i, num_args; + zend_ffi_type *type; + ir_type ret_type = IR_VOID; + ir_ref ref = IR_UNUSED; + + ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); + type = ZEND_FFI_TYPE(sym->type); + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); + + switch (ZEND_FFI_TYPE(type->func.ret_type)->kind) { + case ZEND_FFI_TYPE_VOID: + ret_type = IR_VOID; + break; + case ZEND_FFI_TYPE_FLOAT: + ret_type = IR_FLOAT; + break; + case ZEND_FFI_TYPE_DOUBLE: + ret_type = IR_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + ret_type = IR_U8; + break; + case ZEND_FFI_TYPE_SINT8: + ret_type = IR_I8; + break; + case ZEND_FFI_TYPE_UINT16: + ret_type = IR_U16; + break; + case ZEND_FFI_TYPE_SINT16: + ret_type = IR_I16; + break; + case ZEND_FFI_TYPE_UINT32: + ret_type = IR_U32; + break; + case ZEND_FFI_TYPE_SINT32: + ret_type = IR_I32; + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT64: + ret_type = IR_U64; + break; + case ZEND_FFI_TYPE_SINT64: + ret_type = IR_I64; + break; +#endif + case ZEND_FFI_TYPE_BOOL: + ret_type = IR_BOOL; + break; + case ZEND_FFI_TYPE_CHAR: + ret_type = IR_CHAR; + break; + case ZEND_FFI_TYPE_POINTER: + ret_type = IR_ADDR; + break; + default: + ZEND_UNREACHABLE(); + } + + num_args = TRACE_FRAME_NUM_ARGS(call); + if (num_args) { + ir_ref *args = alloca(sizeof(ir_ref) * num_args); + zend_jit_trace_stack *stack = call->stack; + + for (i = 0; i < num_args; i++) { + args[i] = STACK_REF(stack, i); + } + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + ref = ir_CALL_N(ret_type, ir_CONST_FC_FUNC(sym->addr), num_args, args); + } else { + ref = ir_CALL_N(ret_type, ir_CONST_FUNC(sym->addr), num_args, args); + } + } else { + ZEND_ASSERT(!type->func.args); + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + ref = ir_CALL(ret_type, ir_CONST_FC_FUNC(sym->addr)); + } else { + ref = ir_CALL(ret_type, ir_CONST_FUNC(sym->addr)); + } + } + + if (RETURN_VALUE_USED(opline)) { + zend_ffi_type *ret_type = ZEND_FFI_TYPE(type->func.ret_type); + uint32_t res_type = IS_UNDEF; + + switch (ret_type->kind) { + case ZEND_FFI_TYPE_VOID: + res_type = IS_NULL; + break; + case ZEND_FFI_TYPE_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ref); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT8: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT16: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT16: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT64: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT64: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; +#else + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; +#endif + case ZEND_FFI_TYPE_BOOL: + jit_set_Z_TYPE_INFO(jit, res_addr, + ir_ADD_U32(ir_ZEXT_U32(ref), ir_CONST_U32(IS_FALSE))); + return 1; + case ZEND_FFI_TYPE_CHAR: + jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( + ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), + ir_MUL_L(ir_ZEXT_L(ref), ir_CONST_LONG(sizeof(void*)))))); + res_type = IS_STRING; + break; + case ZEND_FFI_TYPE_POINTER: + if ((ret_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(ret_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); + return 1; + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ret_type), ref); + return 1; + } + break; + default: + ZEND_UNREACHABLE(); + } + + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + } + + return 1; +} + +static int zend_jit_ffi_type_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref obj_ref, zend_ffi_type *ffi_type) +{ + int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, type))), ir_CONST_ADDR(ffi_type)), + ir_CONST_ADDR(exit_addr)); + + return 1; +} + +static int zend_jit_ffi_abc(zend_jit_ctx *jit, + const zend_op *opline, + zend_ffi_type *ffi_type, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range) +{ + int32_t exit_point; + const void *exit_addr; + + ZEND_ASSERT(op2_info == MAY_BE_LONG); + if (ffi_type->kind == ZEND_FFI_TYPE_ARRAY + && !(ffi_type->attr & (ZEND_FFI_ATTR_VLA|ZEND_FFI_ATTR_INCOMPLETE_ARRAY))) { + if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { + zval *zv = Z_ZV(op2_addr); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_LONG); + if (Z_LVAL_P(zv) < 0 || Z_LVAL_P(zv) >= ffi_type->array.length) { + /* Always out of range */ + exit_point = zend_jit_trace_get_exit_point(opline, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); + } + } else if (!op2_range || op2_range->min < 0 || op2_range->max >= ffi_type->array.length) { + if (op2_range && (op2_range->max < 0 || op2_range->min >= ffi_type->array.length)) { + /* Always out of range */ + exit_point = zend_jit_trace_get_exit_point(opline, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); + } else { + /* Array Bounds Check */ + exit_point = zend_jit_trace_get_exit_point(opline, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + ir_GUARD(ir_ULT(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ffi_type->array.length)), + ir_CONST_ADDR(exit_addr)); + } + } + } + + return 1; +} + +static int zend_jit_ffi_read(zend_jit_ctx *jit, + zend_ffi_type *ffi_type, + ir_ref ptr, + zend_jit_addr res_addr) +{ + uint32_t res_type; + + switch (ffi_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ir_LOAD_F(ptr))); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ir_LOAD_D(ptr)); + res_type = IS_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U8(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT8: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I8(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT16: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U16(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT16: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I16(ptr))); + res_type = IS_LONG; + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ir_LOAD_U32(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ir_LOAD_I32(ptr))); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_UINT64: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U64(ptr)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT64: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I64(ptr)); + res_type = IS_LONG; + break; +#else + case ZEND_FFI_TYPE_UINT32: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_U32(ptr)); + res_type = IS_LONG; + break; + case ZEND_FFI_TYPE_SINT32: + jit_set_Z_LVAL(jit, res_addr, ir_LOAD_I32(ptr)); + res_type = IS_LONG; + break; +#endif + case ZEND_FFI_TYPE_BOOL: + jit_set_Z_TYPE_INFO(jit, res_addr, + ir_ADD_U32(ir_ZEXT_U32(ir_LOAD_U8(ptr)), ir_CONST_U32(IS_FALSE))); + return 1; + case ZEND_FFI_TYPE_CHAR: + jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( + ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), + ir_MUL_L(ir_ZEXT_L(ir_LOAD_U8(ptr)), ir_CONST_LONG(sizeof(void*)))))); + res_type = IS_STRING; + break; + case ZEND_FFI_TYPE_ARRAY: + case ZEND_FFI_TYPE_STRUCT: + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_obj), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ptr); + return 1; + case ZEND_FFI_TYPE_POINTER: + if ((ffi_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ptr); + return 1; + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ir_LOAD_A(ptr)); + return 1; + } + break; + break; + default: + ZEND_UNREACHABLE(); + } + + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + + return 1; +} + +static int zend_jit_ffi_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + ir_ref ref, + zend_ffi_type *ffi_type, + zend_jit_ffi_info *ffi_info) +{ + if (ssa->var_info + && use >= 0 + && ssa->var_info[use].ce != zend_ffi_cdata_ce) { + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_cdata_ce)) { + return 0; + } + ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[use].ce = zend_ffi_cdata_ce; + ssa->var_info[use].is_instanceof = 0; + if (def >= 0) { + ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[def].ce = zend_ffi_cdata_ce; + ssa->var_info[def].is_instanceof = 0; + } + } + + if (ffi_info + && use >= 0 + && (ffi_info[use].type != ffi_type + || (ffi_info[use].info & FFI_TYPE_GUARD))) { + if (!zend_jit_ffi_type_guard(jit, opline, ref, ffi_type)) { + return 0; + } + ffi_info[use].info &= ~FFI_TYPE_GUARD; + ffi_info[use].type = ffi_type; + if (def >= 0) { + ffi_info[def].info &= ~FFI_TYPE_GUARD; + ffi_info[def].type = ffi_type; + } + } + + return 1; +} + +static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + zend_jit_addr addr, + HashTable *ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + ir_ref ref = IR_UNUSED; + + if (ssa->var_info + && use >= 0 + && ssa->var_info[use].ce != zend_ffi_ce) { + ref = jit_Z_PTR(jit, addr); + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_ce)) { + return 0; + } + ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[use].ce = zend_ffi_ce; + ssa->var_info[use].is_instanceof = 0; + if (def >= 0) { + ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[def].ce = zend_ffi_ce; + ssa->var_info[def].is_instanceof = 0; + } + } + + if (ffi_info + && use >= 0 + && (ffi_info[use].symbols != ffi_symbols + || (ffi_info[use].info & FFI_SYMBOLS_GUARD))) { + if (!ref) { + ref = jit_Z_PTR(jit, addr); + } + + int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(ref, offsetof(zend_ffi, symbols))), ir_CONST_ADDR(ffi_symbols)), + ir_CONST_ADDR(exit_addr)); + + ffi_info[use].info &= ~FFI_SYMBOLS_GUARD; + ffi_info[use].symbols = ffi_symbols; + if (def >= 0) { + ffi_info[def].info &= ~FFI_SYMBOLS_GUARD; + ffi_info[def].symbols = ffi_symbols; + } + } + + return 1; +} + +static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool op1_avoid_refcounting, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t res_info, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; + } + + ir_ref cdata_ref = jit_FFI_CDATA_PTR(jit, obj_ref); +// ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + if (opline->opcode == ZEND_FETCH_DIM_W || opline->opcode == ZEND_FETCH_DIM_RW) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(el_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, el_type, ptr, res_addr)) { + return 0; + } + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + if (opline->opcode != ZEND_FETCH_LIST_R && !op1_avoid_refcounting && !op1_indirect) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + + return 1; +} + +static int zend_jit_ffi_write(zend_jit_ctx *jit, + zend_ffi_type *ffi_type, + ir_ref ptr, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *val_ffi_type, + zend_jit_addr res_addr) +{ + ir_ref ref = IR_UNUSED; + + switch (ffi_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + if (val_info == MAY_BE_LONG) { + ref = ir_INT2F(jit_Z_LVAL(jit, val_addr)); + } else if (val_info == MAY_BE_DOUBLE) { + ref = ir_D2F(jit_Z_DVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_F(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_F2D(ref); + jit_set_Z_DVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_DOUBLE); + } + } + break; + case ZEND_FFI_TYPE_DOUBLE: + if (val_info == MAY_BE_LONG) { + ref = ir_INT2D(jit_Z_LVAL(jit, val_addr)); + } else if (val_info == MAY_BE_DOUBLE) { + ref = jit_Z_DVAL(jit, val_addr); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_D(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + jit_set_Z_DVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_DOUBLE); + } + } + break; + case ZEND_FFI_TYPE_BOOL: + if (val_info == MAY_BE_FALSE) { + ir_STORE(ptr, IR_FALSE); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_FALSE); + } + return 1; + } else if (val_info == MAY_BE_TRUE) { + ir_STORE(ptr, IR_TRUE); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_TRUE); + } + return 1; + } else if (val_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + if (res_addr) { + ref = jit_Z_TYPE_INFO(jit, val_addr); + jit_set_Z_TYPE_INFO_ex(jit, res_addr, ref); + ref = ir_TRUNC_U8(ref); + } else { + ref = jit_Z_TYPE(jit, val_addr); + } + ir_STORE(ptr, ir_SUB_U8(ref, ir_CONST_U8(IS_FALSE))); + return 1; + } + ZEND_FALLTHROUGH; + case ZEND_FFI_TYPE_UINT8: + if (val_info == MAY_BE_LONG) { + ref = ir_TRUNC_U8(jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_U8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_ZEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; + case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_CHAR: + if (val_info == MAY_BE_LONG) { + ref = ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_I8(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_SEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; + case ZEND_FFI_TYPE_UINT16: + if (val_info == MAY_BE_LONG) { + ref = ir_TRUNC_U16(jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_U16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_ZEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; + case ZEND_FFI_TYPE_SINT16: + if (val_info == MAY_BE_LONG) { + ref = ir_TRUNC_I16(jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_I16(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_SEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + if (val_info == MAY_BE_LONG) { + ref = ir_TRUNC_U32(jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_U32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_ZEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; + case ZEND_FFI_TYPE_SINT32: + if (val_info == MAY_BE_LONG) { + ref = ir_TRUNC_I32(jit_Z_LVAL(jit, val_addr)); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ref = ir_SEXT_L(ref); + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + if (val_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, val_addr); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_I64(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; +#else + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + if (val_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, val_addr); + } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { + ref = ir_LOAD_I32(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + jit_set_Z_LVAL(jit, res_addr, ref); + if (Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } + break; +#endif + case ZEND_FFI_TYPE_POINTER: + if (val_info == MAY_BE_NULL) { + ref = IR_NULL; + } else if (val_ffi_type + && val_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && (val_ffi_type == ffi_type + || ZEND_FFI_TYPE(val_ffi_type->pointer.type) == ZEND_FFI_TYPE(ffi_type->pointer.type) + || ZEND_FFI_TYPE(val_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID + || ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID)) { + ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else { + ZEND_UNREACHABLE(); + } + ir_STORE(ptr, ref); + if (res_addr) { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ref); + } + break; + default: + ZEND_UNREACHABLE(); + } + + return 1; +} + +static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_ffi_type *val_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + + if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, + const zend_op *opline, + uint8_t opcode, + zend_ffi_type *el_type, + ir_ref op1, + uint32_t op2_info, + zend_jit_addr op2_addr) +{ + ir_op op; + ir_type type; + ir_ref op2; + + switch (opcode) { + case ZEND_ADD: + op = IR_ADD; + break; + case ZEND_SUB: + op = IR_SUB; + break; + case ZEND_MUL: + op = IR_MUL; + break; + case ZEND_BW_OR: + op = IR_OR; + break; + case ZEND_BW_AND: + op = IR_AND; + break; + case ZEND_BW_XOR: + op = IR_XOR; + break; + case ZEND_SL: + // TODO: negative shift + op = IR_SHL; + break; + case ZEND_SR: + // TODO: negative shift + op = IR_SAR; /* TODO: SAR or SHR */ + break; + case ZEND_MOD: + // TODO: mod by zero and -1 + op = IR_MOD; + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + + switch (el_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); + type = IR_FLOAT; + if (op2_info == MAY_BE_LONG) { + op2 = ir_INT2F(jit_Z_LVAL(jit, op2_addr)); + } else if (op2_info == MAY_BE_DOUBLE) { + op2 = ir_D2F(jit_Z_DVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_DOUBLE: + ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); + type = IR_DOUBLE; + if (op2_info == MAY_BE_LONG) { + op2 = ir_INT2D(jit_Z_LVAL(jit, op2_addr)); + } else if (op2_info == MAY_BE_DOUBLE) { + op2 = jit_Z_DVAL(jit, op2_addr); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_BOOL: + type = IR_U8; + ZEND_ASSERT(opcode == ZEND_BW_AND || opcode == ZEND_BW_OR); + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U8(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_UINT8: + type = IR_U8; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U8(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_CHAR: + type = IR_I8; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_I8(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_UINT16: + type = IR_U16; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U16(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_SINT16: + type = IR_I16; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_I16(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case ZEND_FFI_TYPE_UINT32: + type = IR_U32; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_U32(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_SINT32: + type = IR_I32; + if (op2_info == MAY_BE_LONG) { + op2 = ir_TRUNC_I32(jit_Z_LVAL(jit, op2_addr)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + type = IR_I64; + if (op2_info == MAY_BE_LONG) { + op2 = jit_Z_LVAL(jit, op2_addr); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; +#else + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + type = IR_I32; + if (op2_info == MAY_BE_LONG) { + op2 = jit_Z_LVAL(jit, op2_addr); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; +#endif + case ZEND_FFI_TYPE_POINTER: + ZEND_ASSERT(opcode == ZEND_ADD || opcode == ZEND_SUB); + ZEND_ASSERT(ZEND_FFI_TYPE(el_type->pointer.type)->size != 0); + type = IR_ADDR; + if (op2_info == MAY_BE_LONG) { + op2 = ir_MUL_A(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ZEND_FFI_TYPE(el_type->pointer.type)->size)); + } else { + ZEND_UNREACHABLE(); + return 0; + } + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + + ir_STORE(op1, ir_BINARY_OP(op, type, ir_LOAD(type, op1), op2)); + + return 1; +} + +static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + uint32_t op1_def_info, + zend_jit_addr op1_addr, + bool op1_indirect, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + uint32_t op1_data_info, + zend_jit_addr op3_addr, + zend_ssa_range *op1_data_range, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (!zend_jit_ffi_abc(jit, opline, op1_ffi_type, op2_info, op2_addr, op2_range)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + cdata_ref = ir_LOAD_A(cdata_ref); + } + + ir_ref ptr = ir_ADD_A(cdata_ref, ir_MUL_L(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(el_type->size))); + + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + el_type, ptr, op1_data_info, op3_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, op1_data_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool op1_avoid_refcounting, + zend_ffi_field *field, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + uint32_t res_info = RES_INFO(); + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (opline->opcode == ZEND_FETCH_OBJ_W) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(field_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, field_type, ptr, res_addr)) { + return 0; + } + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + if (!op1_avoid_refcounting && !op1_indirect) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + + return 1; +} + +static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool op1_avoid_refcounting, + zend_ffi_symbol *sym, + zend_jit_addr res_addr, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + uint32_t res_info = RES_INFO(); + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + + if (opline->opcode == ZEND_FETCH_OBJ_W) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(sym_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { + return 0; + } + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + if (!op1_avoid_refcounting && !op1_indirect) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + + return 1; +} + +static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_field *field, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_ffi_type *val_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + + if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_symbol *sym, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + HashTable *op1_ffi_symbols, + zend_ffi_type *val_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_field *field, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + field_type, ptr, val_info, val_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_symbol *sym, + uint32_t val_info, + zend_jit_addr val_addr, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + sym_type, ptr, val_info, val_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_incdec_helper(zend_jit_ctx *jit, + const zend_op *opline, + uint8_t opcode, + zend_ffi_type *el_type, + ir_ref op1, + zend_jit_addr res_addr) +{ + ir_op op; + ir_type type; + ir_ref op2; + ir_ref ref = IR_UNUSED; + + switch (opcode) { + case ZEND_PRE_INC_OBJ: + case ZEND_POST_INC_OBJ: + op = IR_ADD; + break; + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_DEC_OBJ: + op = IR_SUB; + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + + switch (el_type->kind) { + case ZEND_FFI_TYPE_FLOAT: + type = IR_FLOAT; + op2 = ir_CONST_FLOAT(1.0); + break; + case ZEND_FFI_TYPE_DOUBLE: + type = IR_DOUBLE; + op2 = ir_CONST_DOUBLE(1.0); + break; + case ZEND_FFI_TYPE_UINT8: + type = IR_U8; + op2 = ir_CONST_U8(1); + break; + case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_CHAR: + type = IR_I8; + op2 = ir_CONST_I8(1); + break; + case ZEND_FFI_TYPE_UINT16: + type = IR_U16; + op2 = ir_CONST_U16(1); + break; + case ZEND_FFI_TYPE_SINT16: + type = IR_I16; + op2 = ir_CONST_I16(1); + break; + case ZEND_FFI_TYPE_UINT32: + type = IR_U32; + op2 = ir_CONST_U32(1); + break; + case ZEND_FFI_TYPE_SINT32: + type = IR_I32; + op2 = ir_CONST_I32(1); + break; + case ZEND_FFI_TYPE_UINT64: + type = IR_U64; + op2 = ir_CONST_U64(1); + break; + case ZEND_FFI_TYPE_SINT64: + type = IR_I64; + op2 = ir_CONST_I64(1); + break; + case ZEND_FFI_TYPE_POINTER: + ZEND_ASSERT(ZEND_FFI_TYPE(el_type->pointer.type)->size != 0); + type = IR_ADDR; + op2 = ir_CONST_LONG(ZEND_FFI_TYPE(el_type->pointer.type)->size); + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + + ref = ir_LOAD(type, op1); + + if (res_addr && (opcode == ZEND_POST_INC_OBJ || opcode == ZEND_POST_DEC_OBJ)) { + uint32_t res_type = IS_UNDEF; + + switch (type) { + case IR_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); + res_type = IS_DOUBLE; + break; + case IR_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ref); + res_type = IS_DOUBLE; + break; + case IR_I8: + case IR_I16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_I32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_I64: +#else + case IR_I32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_U8: + case IR_U16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_U32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_U64: +#else + case IR_U32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_ADDR: + if ((el_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(el_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(el_type), ref); + } + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + if (res_type && Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + } + + ref = ir_BINARY_OP(op, type, ref, op2); + ir_STORE(op1, ref); + + if (res_addr && (opcode == ZEND_PRE_INC_OBJ || opcode == ZEND_PRE_DEC_OBJ)) { + uint32_t res_type = IS_UNDEF; + + switch (type) { + case IR_FLOAT: + jit_set_Z_DVAL(jit, res_addr, ir_F2D(ref)); + res_type = IS_DOUBLE; + break; + case IR_DOUBLE: + jit_set_Z_DVAL(jit, res_addr, ref); + res_type = IS_DOUBLE; + break; + case IR_I8: + case IR_I16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_I32: + jit_set_Z_LVAL(jit, res_addr, ir_SEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_I64: +#else + case IR_I32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_U8: + case IR_U16: +#ifdef ZEND_ENABLE_ZVAL_LONG64 + case IR_U32: + jit_set_Z_LVAL(jit, res_addr, ir_ZEXT_L(ref)); + res_type = IS_LONG; + break; + case IR_U64: +#else + case IR_U32: +#endif + jit_set_Z_LVAL(jit, res_addr, ref); + res_type = IS_LONG; + break; + case IR_ADDR: + if ((el_type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(el_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); + } else { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(el_type), ref); + } + break; + default: + ZEND_UNREACHABLE(); + return 0; + } + if (res_type && Z_MODE(res_addr) != IS_REG) { + jit_set_Z_TYPE_INFO(jit, res_addr, res_type); + } + } + + return 1; +} + +static int zend_jit_ffi_incdec_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_field *field, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref cdata_ref = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + ir_ref ptr = ir_ADD_A(cdata_ref, ir_CONST_LONG(field->offset)); + + if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, field_type, ptr, res_addr)) { + return 0; + } + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +static int zend_jit_ffi_incdec_sym(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_ffi_symbol *sym, + zend_jit_addr res_addr, + HashTable *op1_ffi_symbols, + zend_jit_ffi_info *ffi_info) +{ + zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); + + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_CONST_ADDR(sym->addr); + if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, sym_type, ptr, res_addr)) { + return 0; + } + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ From 3c9b7bc2d36d557a49215ea571a287402d7eee5a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 26 Jul 2024 13:21:42 +0300 Subject: [PATCH 049/101] Better JIT support for FFI function calls --- ext/opcache/jit/zend_jit_ir_ffi.c | 1 + ext/opcache/jit/zend_jit_trace.c | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index f84056abddc86..f94d04df950ca 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -188,6 +188,7 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, && op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type)) { ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); + ref = ir_LOAD_A(ref); // TODO: is this always necessary ??? } else if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type)) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 84233e846836c..f2f9e65cc4a9b 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5620,22 +5620,25 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto done; case ZEND_CHECK_FUNC_ARG: if (!JIT_G(current_frame) - || !JIT_G(current_frame)->call - || !JIT_G(current_frame)->call->func) { - break; - } - if (opline->op2_type == IS_CONST - || opline->op2.num > MAX_ARG_FLAG_NUM) { - /* Named parameters not supported in JIT */ - TRACE_FRAME_SET_LAST_SEND_UNKNOWN(JIT_G(current_frame)->call); + || !JIT_G(current_frame)->call) { break; } #ifdef HAVE_FFI if (TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { /* FFI arguments alwyas sent by value ??? */ + TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call); goto done; } #endif + if (!JIT_G(current_frame)->call->func) { + break; + } + if (opline->op2_type == IS_CONST + || opline->op2.num > MAX_ARG_FLAG_NUM) { + /* Named parameters not supported in JIT */ + TRACE_FRAME_SET_LAST_SEND_UNKNOWN(JIT_G(current_frame)->call); + break; + } if (!zend_jit_check_func_arg(&ctx, opline)) { goto jit_failure; } @@ -6073,7 +6076,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_FETCH_DIM_FUNC_ARG: if (!JIT_G(current_frame) || !JIT_G(current_frame)->call - || !JIT_G(current_frame)->call->func +//??? || !JIT_G(current_frame)->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { break; } @@ -6313,7 +6316,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_FETCH_OBJ_FUNC_ARG: if (!JIT_G(current_frame) || !JIT_G(current_frame)->call - || !JIT_G(current_frame)->call->func +//??? || !JIT_G(current_frame)->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { break; } From 9c44a2674beefe175d004e61cb2b4fbfad382fb2 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 30 Jul 2024 13:03:47 +0300 Subject: [PATCH 050/101] JIT/FFI support for "cdata" property of scalar ffi objects --- ext/opcache/jit/zend_jit_ir_ffi.c | 182 +++++++++++++++++++++++++++++- ext/opcache/jit/zend_jit_trace.c | 67 +++++++++++ 2 files changed, 248 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index f94d04df950ca..66820a31500ca 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -819,8 +819,27 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, } } break; - case ZEND_FFI_TYPE_SINT8: case ZEND_FFI_TYPE_CHAR: + if ((val_info & (MAY_BE_GUARD|MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) { + ir_ref str = jit_Z_PTR(jit, val_addr); + + // TODO: ZSTR_LEN() == 1 ??? + ref = ir_LOAD_C(ir_ADD_OFFSET(str, offsetof(zend_string, val))); + ir_STORE(ptr, ref); + if (res_addr) { + ir_ref type_info = jit_Z_TYPE_INFO(jit, val_addr); + ir_ref if_refcounted = ir_IF(ir_AND_U32(type_info, ir_CONST_U32(0xff00))); + + ir_IF_TRUE(if_refcounted); + jit_GC_ADDREF(jit, str); + ir_MERGE_WITH_EMPTY_FALSE(if_refcounted); + jit_set_Z_PTR(jit, res_addr, str); + jit_set_Z_TYPE_INFO_ex(jit, res_addr, type_info); + } + return 1; + } + ZEND_FALLTHROUGH; + case ZEND_FFI_TYPE_SINT8: if (val_info == MAY_BE_LONG) { ref = ir_TRUNC_I8(jit_Z_LVAL(jit, val_addr)); } else if (val_ffi_type && val_ffi_type->kind == ffi_type->kind) { @@ -1311,6 +1330,53 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, return 1; } +static int zend_jit_ffi_fetch_val(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + bool op1_avoid_refcounting, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + uint32_t res_info = RES_INFO(); + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (opline->opcode == ZEND_FETCH_OBJ_W) { + jit_set_Z_PTR(jit, res_addr, + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ptr, ir_CONST_ADDR(op1_ffi_type))); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + if (!zend_jit_ffi_read(jit, op1_ffi_type, ptr, res_addr)) { + return 0; + } + } + + if (res_info & MAY_BE_GUARD) { + // TODO: ??? + ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD; + } + + if (!op1_avoid_refcounting && !op1_indirect) { + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + } + + return 1; +} + static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -1410,6 +1476,54 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, return 1; } +static int zend_jit_ffi_assign_val(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + uint32_t val_info, + zend_jit_addr val_addr, + zend_jit_addr val_def_addr, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_ffi_type *val_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + if (val_addr != val_def_addr && val_def_addr) { + if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) { + return 0; + } + if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) { + val_addr = val_def_addr; + } + } + + ir_ref ptr = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); + + if (!zend_jit_ffi_write(jit, op1_ffi_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -1496,6 +1610,41 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, return 1; } +static int zend_jit_ffi_assign_val_op(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (!zend_jit_ffi_assign_op_helper(jit, opline, opline->extended_value, + op1_ffi_type, ptr, val_info, val_addr)) { + return 0; + } + + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, opline); + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -1767,6 +1916,37 @@ static int zend_jit_ffi_incdec_obj(zend_jit_ctx *jit, return 1; } +static int zend_jit_ffi_incdec_val(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + bool op1_indirect, + zend_jit_addr res_addr, + zend_ffi_type *op1_ffi_type, + zend_jit_ffi_info *ffi_info) +{ + ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + return 0; + } + + ir_ref ptr = ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); + + if (!zend_jit_ffi_incdec_helper(jit, opline, opline->opcode, op1_ffi_type, ptr, res_addr)) { + return 0; + } + + if (!op1_indirect) { + jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); + } + + return 1; +} + static int zend_jit_ffi_incdec_sym(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index f2f9e65cc4a9b..fd614085d5a9c 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4931,6 +4931,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (op1_ffi_type + && op1_ffi_type->kind < ZEND_FFI_TYPE_POINTER + && op1_ffi_type->kind != ZEND_FFI_TYPE_VOID + && zend_string_equals_literal(Z_STR_P(RT_CONSTANT(opline, opline->op2)), "cdata") + && zend_jit_ffi_supported_type(op1_ffi_type)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_incdec_val(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + goto done; } else if (op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, Z_STR_P(RT_CONSTANT(opline, opline->op2))); @@ -5071,6 +5086,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (op1_ffi_type + && op1_ffi_type->kind < ZEND_FFI_TYPE_POINTER + && op1_ffi_type->kind != ZEND_FFI_TYPE_VOID + && zend_string_equals_literal(Z_STR_P(RT_CONSTANT(opline, opline->op2)), "cdata") + && zend_jit_ffi_supported_type(op1_ffi_type)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_assign_val_op(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, + op1_data_info, OP1_DATA_REG_ADDR(), + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + goto done; } else if (op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, Z_STR_P(RT_CONSTANT(opline, opline->op2))); @@ -5201,6 +5231,27 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (op1_ffi_type + && op1_ffi_type->kind < ZEND_FFI_TYPE_POINTER + && op1_ffi_type->kind != ZEND_FFI_TYPE_VOID + && zend_string_equals_literal(Z_STR_P(RT_CONSTANT(opline, opline->op2)), "cdata") + && zend_jit_ffi_supported_type(op1_ffi_type)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_assign_val(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, + op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, + op1_ffi_type, op3_ffi_type, ffi_info)) { + goto jit_failure; + } + if ((opline+1)->op1_type == IS_CV + && (ssa_op+1)->op1_def >= 0 + && ssa->vars[(ssa_op+1)->op1_def].alias == NO_ALIAS) { + ssa->var_info[(ssa_op+1)->op1_def].guarded_reference = ssa->var_info[(ssa_op+1)->op1_use].guarded_reference; + } + goto done; } else if (op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, Z_STR_P(RT_CONSTANT(opline, opline->op2))); @@ -6421,6 +6472,22 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (op1_ffi_type + && op1_ffi_type->kind < ZEND_FFI_TYPE_POINTER + && op1_ffi_type->kind != ZEND_FFI_TYPE_VOID + && zend_string_equals_literal(Z_STR_P(RT_CONSTANT(opline, opline->op2)), "cdata") + && zend_jit_ffi_supported_type(op1_ffi_type)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_fetch_val(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_indirect, + avoid_refcounting, + RES_REG_ADDR(), + op1_ffi_type, ffi_info)) { + goto jit_failure; + } + goto done; } else if ((opline->opcode == ZEND_FETCH_OBJ_R || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || opline->opcode == ZEND_FETCH_OBJ_W) From ec6f8b1e2b878ac0e8d890b8ae4a22ca4ba9d842 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 30 Jul 2024 19:42:12 +0300 Subject: [PATCH 051/101] Avoid modification of "const" CData --- ext/opcache/jit/zend_jit_trace.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index fd614085d5a9c..e458c0702db9b 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6457,6 +6457,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (field + && (!field->is_const || opline->opcode != ZEND_FETCH_OBJ_W) && !field->bits && ZEND_FFI_TYPE(field->type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(field->type))) { @@ -6488,10 +6489,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } goto done; - } else if ((opline->opcode == ZEND_FETCH_OBJ_R - || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG - || opline->opcode == ZEND_FETCH_OBJ_W) - && op1_ffi_symbols) { + } else if (op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym From 21b3db4f563af4364e503c6b2dc4a46942e72052 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 30 Jul 2024 19:46:25 +0300 Subject: [PATCH 052/101] Add JIT/FFI tests --- ext/ffi/tests/jit/001_var_read_scalar.phpt | 27 +++++++++ ext/ffi/tests/jit/002_var_read_array.phpt | 44 ++++++++++++++ ext/ffi/tests/jit/003_var_read_struct.phpt | 33 +++++++++++ .../tests/jit/004_var_read_struct_ptr.phpt | 32 ++++++++++ ext/ffi/tests/jit/005_var_read_func.phpt | 32 ++++++++++ ext/ffi/tests/jit/006_var_write_scalar.phpt | 26 ++++++++ .../tests/jit/007_var_write_scalar_cdata.phpt | 32 ++++++++++ .../jit/008_var_write_struct_ptr_null.phpt | 27 +++++++++ .../jit/009_var_write_struct_ptr_cdata.phpt | 43 ++++++++++++++ ext/ffi/tests/jit/010_var_modify_scalar.phpt | 27 +++++++++ .../tests/jit/011_var_modify_struct_ptr.phpt | 31 ++++++++++ .../tests/jit/012_var_write_scalar_ret.phpt | 27 +++++++++ ext/ffi/tests/jit/013_array_read_scalar.phpt | 29 +++++++++ .../jit/014_array_read_nested_array.phpt | 29 +++++++++ .../jit/015_array_read_nested_struct.phpt | 29 +++++++++ ext/ffi/tests/jit/016_struct_read_scalar.phpt | 25 ++++++++ .../jit/017_struct_read_nested_array.phpt | 29 +++++++++ ext/ffi/tests/jit/018_array_write_scalar.phpt | 35 +++++++++++ .../jit/019_array_write_nested_array.phpt | 59 +++++++++++++++++++ .../jit/020_array_write_nested_struct.phpt | 59 +++++++++++++++++++ .../tests/jit/021_struct_write_scalar.phpt | 28 +++++++++ .../jit/022_struct_write_nested_array.phpt | 39 ++++++++++++ .../jit/023_struct_write_nested_struct.phpt | 36 +++++++++++ .../tests/jit/024_array_modify_scalar.phpt | 35 +++++++++++ .../jit/025_array_modify_nested_array.phpt | 59 +++++++++++++++++++ .../jit/026_array_modify_nested_struct.phpt | 59 +++++++++++++++++++ .../tests/jit/027_struct_modify_scalar.phpt | 28 +++++++++ .../jit/028_struct_modify_nested_array.phpt | 39 ++++++++++++ .../jit/029_struct_modify_nested_struct.phpt | 36 +++++++++++ ext/ffi/tests/jit/030_var_inc_scalar.phpt | 27 +++++++++ ext/ffi/tests/jit/031_var_dec_ptr.phpt | 31 ++++++++++ .../tests/jit/032_struct_preinc_scalar.phpt | 29 +++++++++ .../tests/jit/033_struct_postdec_scalar.phpt | 30 ++++++++++ ext/ffi/tests/jit/034_struct_postinc_ptr.phpt | 32 ++++++++++ ext/ffi/tests/jit/035_struct_predec_ptr.phpt | 32 ++++++++++ ext/ffi/tests/jit/036_scalar_cdata_read.phpt | 25 ++++++++ ext/ffi/tests/jit/037_scalar_cdata_write.phpt | 27 +++++++++ .../tests/jit/038_scalar_cdata_modify.phpt | 26 ++++++++ .../tests/jit/039_scalar_cdata_postinc.phpt | 27 +++++++++ ext/ffi/tests/jit/040_call.phpt | 28 +++++++++ ext/ffi/tests/jit/041_call_tmp_scalar.phpt | 29 +++++++++ ext/ffi/tests/jit/042_call_tmp_cdata.phpt | 30 ++++++++++ ext/ffi/tests/jit/050_isNull.phpt | 26 ++++++++ ext/ffi/tests/jit/051_string.phpt | 26 ++++++++ ext/ffi/tests/jit/052_sizeof.phpt | 25 ++++++++ ext/ffi/tests/jit/053_alignof.phpt | 25 ++++++++ ext/ffi/tests/jit/054_typeof.phpt | 26 ++++++++ ext/ffi/tests/jit/055_memset.phpt | 24 ++++++++ ext/ffi/tests/jit/056_memcpy_string.phpt | 24 ++++++++ ext/ffi/tests/jit/057_memcpy_cdata.phpt | 27 +++++++++ .../tests/jit/058_memcmp_cdata_string.phpt | 25 ++++++++ .../tests/jit/059_memcmp_cdata_string.phpt | 25 ++++++++ ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt | 27 +++++++++ ext/ffi/tests/jit/061_new_string.phpt | 32 ++++++++++ ext/ffi/tests/jit/062_new_ctype.phpt | 33 +++++++++++ ext/ffi/tests/jit/063_free.phpt | 24 ++++++++ ext/ffi/tests/jit/064_cast_string.phpt | 34 +++++++++++ ext/ffi/tests/jit/065_cast_ctype.phpt | 35 +++++++++++ ext/ffi/tests/jit/066_cast_scalar.phpt | 26 ++++++++ ext/ffi/tests/jit/067_cast_ptr_int.phpt | 26 ++++++++ ext/ffi/tests/jit/068_cast_ptr_null.phpt | 26 ++++++++ ext/ffi/tests/jit/069_addr.phpt | 28 +++++++++ 62 files changed, 1951 insertions(+) create mode 100644 ext/ffi/tests/jit/001_var_read_scalar.phpt create mode 100644 ext/ffi/tests/jit/002_var_read_array.phpt create mode 100644 ext/ffi/tests/jit/003_var_read_struct.phpt create mode 100644 ext/ffi/tests/jit/004_var_read_struct_ptr.phpt create mode 100644 ext/ffi/tests/jit/005_var_read_func.phpt create mode 100644 ext/ffi/tests/jit/006_var_write_scalar.phpt create mode 100644 ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt create mode 100644 ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt create mode 100644 ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt create mode 100644 ext/ffi/tests/jit/010_var_modify_scalar.phpt create mode 100644 ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt create mode 100644 ext/ffi/tests/jit/012_var_write_scalar_ret.phpt create mode 100644 ext/ffi/tests/jit/013_array_read_scalar.phpt create mode 100644 ext/ffi/tests/jit/014_array_read_nested_array.phpt create mode 100644 ext/ffi/tests/jit/015_array_read_nested_struct.phpt create mode 100644 ext/ffi/tests/jit/016_struct_read_scalar.phpt create mode 100644 ext/ffi/tests/jit/017_struct_read_nested_array.phpt create mode 100644 ext/ffi/tests/jit/018_array_write_scalar.phpt create mode 100644 ext/ffi/tests/jit/019_array_write_nested_array.phpt create mode 100644 ext/ffi/tests/jit/020_array_write_nested_struct.phpt create mode 100644 ext/ffi/tests/jit/021_struct_write_scalar.phpt create mode 100644 ext/ffi/tests/jit/022_struct_write_nested_array.phpt create mode 100644 ext/ffi/tests/jit/023_struct_write_nested_struct.phpt create mode 100644 ext/ffi/tests/jit/024_array_modify_scalar.phpt create mode 100644 ext/ffi/tests/jit/025_array_modify_nested_array.phpt create mode 100644 ext/ffi/tests/jit/026_array_modify_nested_struct.phpt create mode 100644 ext/ffi/tests/jit/027_struct_modify_scalar.phpt create mode 100644 ext/ffi/tests/jit/028_struct_modify_nested_array.phpt create mode 100644 ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt create mode 100644 ext/ffi/tests/jit/030_var_inc_scalar.phpt create mode 100644 ext/ffi/tests/jit/031_var_dec_ptr.phpt create mode 100644 ext/ffi/tests/jit/032_struct_preinc_scalar.phpt create mode 100644 ext/ffi/tests/jit/033_struct_postdec_scalar.phpt create mode 100644 ext/ffi/tests/jit/034_struct_postinc_ptr.phpt create mode 100644 ext/ffi/tests/jit/035_struct_predec_ptr.phpt create mode 100644 ext/ffi/tests/jit/036_scalar_cdata_read.phpt create mode 100644 ext/ffi/tests/jit/037_scalar_cdata_write.phpt create mode 100644 ext/ffi/tests/jit/038_scalar_cdata_modify.phpt create mode 100644 ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt create mode 100644 ext/ffi/tests/jit/040_call.phpt create mode 100644 ext/ffi/tests/jit/041_call_tmp_scalar.phpt create mode 100644 ext/ffi/tests/jit/042_call_tmp_cdata.phpt create mode 100644 ext/ffi/tests/jit/050_isNull.phpt create mode 100644 ext/ffi/tests/jit/051_string.phpt create mode 100644 ext/ffi/tests/jit/052_sizeof.phpt create mode 100644 ext/ffi/tests/jit/053_alignof.phpt create mode 100644 ext/ffi/tests/jit/054_typeof.phpt create mode 100644 ext/ffi/tests/jit/055_memset.phpt create mode 100644 ext/ffi/tests/jit/056_memcpy_string.phpt create mode 100644 ext/ffi/tests/jit/057_memcpy_cdata.phpt create mode 100644 ext/ffi/tests/jit/058_memcmp_cdata_string.phpt create mode 100644 ext/ffi/tests/jit/059_memcmp_cdata_string.phpt create mode 100644 ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt create mode 100644 ext/ffi/tests/jit/061_new_string.phpt create mode 100644 ext/ffi/tests/jit/062_new_ctype.phpt create mode 100644 ext/ffi/tests/jit/063_free.phpt create mode 100644 ext/ffi/tests/jit/064_cast_string.phpt create mode 100644 ext/ffi/tests/jit/065_cast_ctype.phpt create mode 100644 ext/ffi/tests/jit/066_cast_scalar.phpt create mode 100644 ext/ffi/tests/jit/067_cast_ptr_int.phpt create mode 100644 ext/ffi/tests/jit/068_cast_ptr_null.phpt create mode 100644 ext/ffi/tests/jit/069_addr.phpt diff --git a/ext/ffi/tests/jit/001_var_read_scalar.phpt b/ext/ffi/tests/jit/001_var_read_scalar.phpt new file mode 100644 index 0000000000000..76103be8426eb --- /dev/null +++ b/ext/ffi/tests/jit/001_var_read_scalar.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 001: Read Variable (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +int(%i) diff --git a/ext/ffi/tests/jit/002_var_read_array.phpt b/ext/ffi/tests/jit/002_var_read_array.phpt new file mode 100644 index 0000000000000..6207e5a6386ad --- /dev/null +++ b/ext/ffi/tests/jit/002_var_read_array.phpt @@ -0,0 +1,44 @@ +--TEST-- +FFI/JIT 002: Read Variable (array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int8_t[8])#%d (8) { + [0]=> + int(%i) + [1]=> + int(%i) + [2]=> + int(%i) + [3]=> + int(%i) + [4]=> + int(%i) + [5]=> + int(%i) + [6]=> + int(%i) + [7]=> + int(%i) +} diff --git a/ext/ffi/tests/jit/003_var_read_struct.phpt b/ext/ffi/tests/jit/003_var_read_struct.phpt new file mode 100644 index 0000000000000..666b20eb5ae2c --- /dev/null +++ b/ext/ffi/tests/jit/003_var_read_struct.phpt @@ -0,0 +1,33 @@ +--TEST-- +FFI/JIT 003: Read Variable (struct) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct _IO_FILE)#%d (2) { + ["a"]=> + int(%i) + ["b"]=> + int(%i) +} diff --git a/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt b/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt new file mode 100644 index 0000000000000..e7328cd477849 --- /dev/null +++ b/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt @@ -0,0 +1,32 @@ +--TEST-- +FFI/JIT 004: Read Variable (struct ptr) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct _IO_FILE*)#%d (1) { + [0]=> + object(FFI\CData:struct _IO_FILE)#%d (0) { + } +} diff --git a/ext/ffi/tests/jit/005_var_read_func.phpt b/ext/ffi/tests/jit/005_var_read_func.phpt new file mode 100644 index 0000000000000..9d380bcc090c2 --- /dev/null +++ b/ext/ffi/tests/jit/005_var_read_func.phpt @@ -0,0 +1,32 @@ +--TEST-- +FFI/JIT 005: Read Variable (func) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t(*)())#%d (1) { + [0]=> + object(FFI\CData:int32_t())#%d (0) { + } +} diff --git a/ext/ffi/tests/jit/006_var_write_scalar.phpt b/ext/ffi/tests/jit/006_var_write_scalar.phpt new file mode 100644 index 0000000000000..b0f67c3632624 --- /dev/null +++ b/ext/ffi/tests/jit/006_var_write_scalar.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 006: Write Variable (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = $i; + } + var_dump($ffi->stdout); +} +test(); +?> +--EXPECT-- +int(4) diff --git a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt new file mode 100644 index 0000000000000..d4c466ec5a368 --- /dev/null +++ b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt @@ -0,0 +1,32 @@ +--TEST-- +FFI/JIT 007: Write Variable (scalar cdata) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +cast('int64_t', 42); + var_dump($x); + for ($i = 0; $i < 5; $i++) { + $ffi->stdout = $x; + } + var_dump($ffi->stdout); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int64_t)#%d (1) { + ["cdata"]=> + int(42) +} +int(42) diff --git a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt new file mode 100644 index 0000000000000..1d42a475cd859 --- /dev/null +++ b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 008: Write Variable (struct ptr null) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = null; + } + var_dump($ffi->stdout); +} +test(); +?> +--EXPECT-- +NULL diff --git a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt new file mode 100644 index 0000000000000..f147bae0a4810 --- /dev/null +++ b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt @@ -0,0 +1,43 @@ +--TEST-- +FFI/JIT 009: Write Variable (struct ptr cdata) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +cast('FILE*', 42); + var_dump($x); + for ($i = 0; $i < 5; $i++) { + $ffi->stdout = $x; + } + var_dump($ffi->stdout); + var_dump($ffi->cast('intptr_t', $ffi->stdout)); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct _IO_FILE*)#%d (1) { + [0]=> + object(FFI\CData:struct _IO_FILE)#%d (0) { + } +} +object(FFI\CData:struct _IO_FILE*)#%d (1) { + [0]=> + object(FFI\CData:struct _IO_FILE)#%d (0) { + } +} +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(42) +} diff --git a/ext/ffi/tests/jit/010_var_modify_scalar.phpt b/ext/ffi/tests/jit/010_var_modify_scalar.phpt new file mode 100644 index 0000000000000..3edfac67fbfb0 --- /dev/null +++ b/ext/ffi/tests/jit/010_var_modify_scalar.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 010: Modify Variable (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = null; + for ($i = 0; $i < 5; $i++) { + $ffi->stdout += $i; + } + var_dump($ffi->stdout); +} +test(); +?> +--EXPECT-- +int(10) diff --git a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt new file mode 100644 index 0000000000000..33ffa66b94d9a --- /dev/null +++ b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt @@ -0,0 +1,31 @@ +--TEST-- +FFI/JIT 011: Modify Variable (struct ptr) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = $ffi->cast('FILE*', 42); + for ($i = 0; $i < 5; $i++) { + $ffi->stdout += $i; + } + var_dump($ffi->cast('intptr_t', $ffi->stdout)); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(82) +} diff --git a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt new file mode 100644 index 0000000000000..ce56da2a9f50f --- /dev/null +++ b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 012: Write Variable (scalar + ret) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = $i; + } + var_dump($ffi->stdout, $ret); +} +test(); +?> +--EXPECT-- +int(4) +int(4) diff --git a/ext/ffi/tests/jit/013_array_read_scalar.phpt b/ext/ffi/tests/jit/013_array_read_scalar.phpt new file mode 100644 index 0000000000000..be2e00c7dce17 --- /dev/null +++ b/ext/ffi/tests/jit/013_array_read_scalar.phpt @@ -0,0 +1,29 @@ +--TEST-- +FFI/JIT 013: Read Array (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int32_t[5]"); + $a[0] = 0; + $a[1] = 2; + $a[2] = 4; + $a[3] = 6; + $a[4] = 8; + $x = 0; + for ($i = 0; $i < 5; $i++) { + $x += $a[$i]; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +int(20) diff --git a/ext/ffi/tests/jit/014_array_read_nested_array.phpt b/ext/ffi/tests/jit/014_array_read_nested_array.phpt new file mode 100644 index 0000000000000..67928c803fdd7 --- /dev/null +++ b/ext/ffi/tests/jit/014_array_read_nested_array.phpt @@ -0,0 +1,29 @@ +--TEST-- +FFI/JIT 014: Read Array (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int32_t[5][2]"); + $a[0][1] = 0; + $a[1][1] = 2; + $a[2][1] = 4; + $a[3][1] = 6; + $a[4][1] = 8; + $x = 0; + for ($i = 0; $i < 5; $i++) { + $x += $a[$i][1]; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +int(20) diff --git a/ext/ffi/tests/jit/015_array_read_nested_struct.phpt b/ext/ffi/tests/jit/015_array_read_nested_struct.phpt new file mode 100644 index 0000000000000..0bc4a9557b8ae --- /dev/null +++ b/ext/ffi/tests/jit/015_array_read_nested_struct.phpt @@ -0,0 +1,29 @@ +--TEST-- +FFI/JIT 015: Read Array (nested struct) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}[5]"); + $a[0]->a = 0; + $a[1]->a = 2; + $a[2]->a = 4; + $a[3]->a = 6; + $a[4]->a = 8; + $x = 0; + for ($i = 0; $i < 5; $i++) { + $x += $a[$i]->a; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +int(20) diff --git a/ext/ffi/tests/jit/016_struct_read_scalar.phpt b/ext/ffi/tests/jit/016_struct_read_scalar.phpt new file mode 100644 index 0000000000000..2f4cf6b8ce9e7 --- /dev/null +++ b/ext/ffi/tests/jit/016_struct_read_scalar.phpt @@ -0,0 +1,25 @@ +--TEST-- +FFI/JIT 016: Read Struct (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}"); + $a->a = 12; + $x = 0; + for ($i = 0; $i < 5; $i++) { + $x += $a->a; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +int(60) diff --git a/ext/ffi/tests/jit/017_struct_read_nested_array.phpt b/ext/ffi/tests/jit/017_struct_read_nested_array.phpt new file mode 100644 index 0000000000000..7216bd2997bf7 --- /dev/null +++ b/ext/ffi/tests/jit/017_struct_read_nested_array.phpt @@ -0,0 +1,29 @@ +--TEST-- +FFI/JIT 017: Read Struct (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a[5]; int32_t b;}"); + $a->a[0] = 0; + $a->a[1] = 2; + $a->a[2] = 4; + $a->a[3] = 6; + $a->a[4] = 8; + $x = 0; + for ($i = 0; $i < 5; $i++) { + $x += $a->a[$i]; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +int(20) diff --git a/ext/ffi/tests/jit/018_array_write_scalar.phpt b/ext/ffi/tests/jit/018_array_write_scalar.phpt new file mode 100644 index 0000000000000..920e81e171096 --- /dev/null +++ b/ext/ffi/tests/jit/018_array_write_scalar.phpt @@ -0,0 +1,35 @@ +--TEST-- +FFI/JIT 018: Array Write (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int32_t[5]"); + for ($i = 0; $i < 5; $i++) { + $a[$i] = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + [3]=> + int(3) + [4]=> + int(4) +} + diff --git a/ext/ffi/tests/jit/019_array_write_nested_array.phpt b/ext/ffi/tests/jit/019_array_write_nested_array.phpt new file mode 100644 index 0000000000000..5d58bdce7401e --- /dev/null +++ b/ext/ffi/tests/jit/019_array_write_nested_array.phpt @@ -0,0 +1,59 @@ +--TEST-- +FFI/JIT 019: Array Write (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int32_t[5][2]"); + for ($i = 0; $i < 5; $i++) { + $a[$i][1] = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[5][2])#%d (5) { + [0]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(0) + } + [1]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(1) + } + [2]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(2) + } + [3]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(3) + } + [4]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(4) + } +} diff --git a/ext/ffi/tests/jit/020_array_write_nested_struct.phpt b/ext/ffi/tests/jit/020_array_write_nested_struct.phpt new file mode 100644 index 0000000000000..6d3a15f224a13 --- /dev/null +++ b/ext/ffi/tests/jit/020_array_write_nested_struct.phpt @@ -0,0 +1,59 @@ +--TEST-- +FFI/JIT 020: Array Write (nested struct) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}[5]"); + for ($i = 0; $i < 5; $i++) { + $a[$i]->a = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct [5])#%d (5) { + [0]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(0) + ["b"]=> + int(0) + } + [1]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(1) + ["b"]=> + int(0) + } + [2]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(2) + ["b"]=> + int(0) + } + [3]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(3) + ["b"]=> + int(0) + } + [4]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(4) + ["b"]=> + int(0) + } +} diff --git a/ext/ffi/tests/jit/021_struct_write_scalar.phpt b/ext/ffi/tests/jit/021_struct_write_scalar.phpt new file mode 100644 index 0000000000000..1bcf7d5350e0d --- /dev/null +++ b/ext/ffi/tests/jit/021_struct_write_scalar.phpt @@ -0,0 +1,28 @@ +--TEST-- +FFI/JIT 021: Write Struct (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(4) + ["b"]=> + int(0) +} diff --git a/ext/ffi/tests/jit/022_struct_write_nested_array.phpt b/ext/ffi/tests/jit/022_struct_write_nested_array.phpt new file mode 100644 index 0000000000000..8e5581973eef5 --- /dev/null +++ b/ext/ffi/tests/jit/022_struct_write_nested_array.phpt @@ -0,0 +1,39 @@ +--TEST-- +FFI/JIT 022: Write Struct (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a[5]; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a[$i] = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + [3]=> + int(3) + [4]=> + int(4) + } + ["b"]=> + int(0) +} diff --git a/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt b/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt new file mode 100644 index 0000000000000..c1785b180e823 --- /dev/null +++ b/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt @@ -0,0 +1,36 @@ +--TEST-- +FFI/JIT 022: Write Struct (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct { struct { struct {int32_t x; int32_t y} a; } a; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a->a->x = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#2 (2) { + ["a"]=> + object(FFI\CData:struct )#3 (1) { + ["a"]=> + object(FFI\CData:struct )#1 (2) { + ["x"]=> + int(4) + ["y"]=> + int(0) + } + } + ["b"]=> + int(0) +} diff --git a/ext/ffi/tests/jit/024_array_modify_scalar.phpt b/ext/ffi/tests/jit/024_array_modify_scalar.phpt new file mode 100644 index 0000000000000..5825edbfd843e --- /dev/null +++ b/ext/ffi/tests/jit/024_array_modify_scalar.phpt @@ -0,0 +1,35 @@ +--TEST-- +FFI/JIT 024: Array Modification (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int32_t[5]"); + for ($i = 0; $i < 5; $i++) { + $a[$i] += $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + [3]=> + int(3) + [4]=> + int(4) +} + diff --git a/ext/ffi/tests/jit/025_array_modify_nested_array.phpt b/ext/ffi/tests/jit/025_array_modify_nested_array.phpt new file mode 100644 index 0000000000000..afc668a317d6e --- /dev/null +++ b/ext/ffi/tests/jit/025_array_modify_nested_array.phpt @@ -0,0 +1,59 @@ +--TEST-- +FFI/JIT 025: Array Modification (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int32_t[5][2]"); + for ($i = 0; $i < 5; $i++) { + $a[$i][1] += $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[5][2])#%d (5) { + [0]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(0) + } + [1]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(1) + } + [2]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(2) + } + [3]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(3) + } + [4]=> + object(FFI\CData:int32_t[2])#%d (2) { + [0]=> + int(0) + [1]=> + int(4) + } +} diff --git a/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt b/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt new file mode 100644 index 0000000000000..814cba1f8c2f1 --- /dev/null +++ b/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt @@ -0,0 +1,59 @@ +--TEST-- +FFI/JIT 026: Array Modification (nested struct) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}[5]"); + for ($i = 0; $i < 5; $i++) { + $a[$i]->a += $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct [5])#%d (5) { + [0]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(0) + ["b"]=> + int(0) + } + [1]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(1) + ["b"]=> + int(0) + } + [2]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(2) + ["b"]=> + int(0) + } + [3]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(3) + ["b"]=> + int(0) + } + [4]=> + object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(4) + ["b"]=> + int(0) + } +} diff --git a/ext/ffi/tests/jit/027_struct_modify_scalar.phpt b/ext/ffi/tests/jit/027_struct_modify_scalar.phpt new file mode 100644 index 0000000000000..80dedb4cd992a --- /dev/null +++ b/ext/ffi/tests/jit/027_struct_modify_scalar.phpt @@ -0,0 +1,28 @@ +--TEST-- +FFI/JIT 027: Modify Struct (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a += $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(10) + ["b"]=> + int(0) +} diff --git a/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt b/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt new file mode 100644 index 0000000000000..7635d32d5140e --- /dev/null +++ b/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt @@ -0,0 +1,39 @@ +--TEST-- +FFI/JIT 028: Modify Struct (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a[5]; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a[$i] += $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + [3]=> + int(3) + [4]=> + int(4) + } + ["b"]=> + int(0) +} diff --git a/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt b/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt new file mode 100644 index 0000000000000..cfdedd585c3d7 --- /dev/null +++ b/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt @@ -0,0 +1,36 @@ +--TEST-- +FFI/JIT 029: Modify Struct (nested array) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct { struct { struct {int32_t x; int32_t y} a; } a; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a->a->x += $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#2 (2) { + ["a"]=> + object(FFI\CData:struct )#3 (1) { + ["a"]=> + object(FFI\CData:struct )#1 (2) { + ["x"]=> + int(10) + ["y"]=> + int(0) + } + } + ["b"]=> + int(0) +} diff --git a/ext/ffi/tests/jit/030_var_inc_scalar.phpt b/ext/ffi/tests/jit/030_var_inc_scalar.phpt new file mode 100644 index 0000000000000..24f440d6545fc --- /dev/null +++ b/ext/ffi/tests/jit/030_var_inc_scalar.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 030: Increment Variable (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = null; + for ($i = 0; $i < 5; $i++) { + $ffi->stdout++; + } + var_dump($ffi->stdout); +} +test(); +?> +--EXPECT-- +int(5) diff --git a/ext/ffi/tests/jit/031_var_dec_ptr.phpt b/ext/ffi/tests/jit/031_var_dec_ptr.phpt new file mode 100644 index 0000000000000..47df85b8d2344 --- /dev/null +++ b/ext/ffi/tests/jit/031_var_dec_ptr.phpt @@ -0,0 +1,31 @@ +--TEST-- +FFI/JIT 031: Decrement Variable (ptr) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout = $ffi->cast('FILE*', 42); + for ($i = 0; $i < 5; $i++) { + $ffi->stdout--; + } + var_dump($ffi->cast('intptr_t', $ffi->stdout)); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(22) +} diff --git a/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt b/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt new file mode 100644 index 0000000000000..7ae5e0f301cb1 --- /dev/null +++ b/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt @@ -0,0 +1,29 @@ +--TEST-- +FFI/JIT 032: PREINC Struct (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $x = ++$a->a; + } + var_dump($a, $x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(5) + ["b"]=> + int(0) +} +int(5) diff --git a/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt b/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt new file mode 100644 index 0000000000000..1575a893e128e --- /dev/null +++ b/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt @@ -0,0 +1,30 @@ +--TEST-- +FFI/JIT 033: POSTDEC Struct (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a; int32_t b;}"); + $a->a = 42; + for ($i = 0; $i < 5; $i++) { + $x = $a->a--; + } + var_dump($a, $x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + int(37) + ["b"]=> + int(0) +} +int(38) diff --git a/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt b/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt new file mode 100644 index 0000000000000..1a15c7f1deb74 --- /dev/null +++ b/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt @@ -0,0 +1,32 @@ +--TEST-- +FFI/JIT 034: POSTINC Struct (ptr) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t *a; int32_t b;}"); + $a->a = $ffi->cast('int32_t*', 42); + for ($i = 0; $i < 5; $i++) { + $x = $a->a++; + } + var_dump($ffi->cast('intptr_t', $a->a), $ffi->cast('intptr_t', $x)); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(62) +} +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(58) +} diff --git a/ext/ffi/tests/jit/035_struct_predec_ptr.phpt b/ext/ffi/tests/jit/035_struct_predec_ptr.phpt new file mode 100644 index 0000000000000..611b0b9708115 --- /dev/null +++ b/ext/ffi/tests/jit/035_struct_predec_ptr.phpt @@ -0,0 +1,32 @@ +--TEST-- +FFI/JIT 035: PREDEC Struct (ptr) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t *a; int32_t b;}"); + $a->a = $ffi->cast('int32_t*', 42); + for ($i = 0; $i < 5; $i++) { + $x = --$a->a; + } + var_dump($ffi->cast('intptr_t', $a->a), $ffi->cast('intptr_t', $x)); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(22) +} +object(FFI\CData:int%d_t)#%d (1) { + ["cdata"]=> + int(22) +} diff --git a/ext/ffi/tests/jit/036_scalar_cdata_read.phpt b/ext/ffi/tests/jit/036_scalar_cdata_read.phpt new file mode 100644 index 0000000000000..183d7af747f57 --- /dev/null +++ b/ext/ffi/tests/jit/036_scalar_cdata_read.phpt @@ -0,0 +1,25 @@ +--TEST-- +FFI/JIT 036: Scalar CData Read +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int"); + $a->cdata = 42; + $x = 0; + for ($i = 0; $i < 5; $i++) { + $x += $a->cdata; + } + var_dump($x); +} +test(); +?> +--EXPECT-- +int(210) diff --git a/ext/ffi/tests/jit/037_scalar_cdata_write.phpt b/ext/ffi/tests/jit/037_scalar_cdata_write.phpt new file mode 100644 index 0000000000000..6c371edcb195a --- /dev/null +++ b/ext/ffi/tests/jit/037_scalar_cdata_write.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 037: Scalar CData Write +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int"); + for ($i = 0; $i < 5; $i++) { + $ret = $x->cdata = 42; + } + var_dump($ret, $x); +} +test(); +?> +--EXPECTF-- +int(42) +object(FFI\CData:int32_t)#%d (1) { + ["cdata"]=> + int(42) +} diff --git a/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt b/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt new file mode 100644 index 0000000000000..25df988389d6c --- /dev/null +++ b/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 038: Scalar CData Modification +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int"); + for ($i = 0; $i < 5; $i++) { + $x->cdata += 42; + } + var_dump($x); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t)#%d (1) { + ["cdata"]=> + int(210) +} diff --git a/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt b/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt new file mode 100644 index 0000000000000..fe606842c7310 --- /dev/null +++ b/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 039: Scalar CData post increment +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int"); + for ($i = 0; $i < 5; $i++) { + $ret = $x->cdata++; + } + var_dump($ret, $x); +} +test(); +?> +--EXPECTF-- +int(4) +object(FFI\CData:int32_t)#%d (1) { + ["cdata"]=> + int(5) +} diff --git a/ext/ffi/tests/jit/040_call.phpt b/ext/ffi/tests/jit/040_call.phpt new file mode 100644 index 0000000000000..54278bdec0fa1 --- /dev/null +++ b/ext/ffi/tests/jit/040_call.phpt @@ -0,0 +1,28 @@ +--TEST-- +FFI/JIT 040: Function call +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +printf("Hello %s\n", $name); + } +} +test("FFI"); +?> +--EXPECT-- +Hello FFI +Hello FFI +Hello FFI +Hello FFI +Hello FFI diff --git a/ext/ffi/tests/jit/041_call_tmp_scalar.phpt b/ext/ffi/tests/jit/041_call_tmp_scalar.phpt new file mode 100644 index 0000000000000..9f063e0fcdc02 --- /dev/null +++ b/ext/ffi/tests/jit/041_call_tmp_scalar.phpt @@ -0,0 +1,29 @@ +--TEST-- +FFI/JIT 041: Function call (tmp scalar arg) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +fprintf($ffi->stdout, "Hello %s\n", $name); + } +} +test("FFI"); +?> +--EXPECT-- +Hello FFI +Hello FFI +Hello FFI +Hello FFI +Hello FFI diff --git a/ext/ffi/tests/jit/042_call_tmp_cdata.phpt b/ext/ffi/tests/jit/042_call_tmp_cdata.phpt new file mode 100644 index 0000000000000..5d32b8147f522 --- /dev/null +++ b/ext/ffi/tests/jit/042_call_tmp_cdata.phpt @@ -0,0 +1,30 @@ +--TEST-- +FFI/JIT 042: Function call (tmp CData arg) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +fprintf($ffi->stdout, "Hello %s\n", $name); + } +} +test("FFI"); +?> +--EXPECT-- +Hello FFI +Hello FFI +Hello FFI +Hello FFI +Hello FFI diff --git a/ext/ffi/tests/jit/050_isNull.phpt b/ext/ffi/tests/jit/050_isNull.phpt new file mode 100644 index 0000000000000..c30f871fa9a32 --- /dev/null +++ b/ext/ffi/tests/jit/050_isNull.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 050: FFI::isNull() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +stdout); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +bool(false) diff --git a/ext/ffi/tests/jit/051_string.phpt b/ext/ffi/tests/jit/051_string.phpt new file mode 100644 index 0000000000000..d29edb360b593 --- /dev/null +++ b/ext/ffi/tests/jit/051_string.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 051: FFI::string() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + $x[0] = "X"; + for ($i = 0; $i < 5; $i++) { + $ret = FFI::string($x, 1); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +string(1) "X" + diff --git a/ext/ffi/tests/jit/052_sizeof.phpt b/ext/ffi/tests/jit/052_sizeof.phpt new file mode 100644 index 0000000000000..969f64634d96d --- /dev/null +++ b/ext/ffi/tests/jit/052_sizeof.phpt @@ -0,0 +1,25 @@ +--TEST-- +FFI/JIT 052: FFI::sizeof() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int[10]"); + $x[0] = 42; + for ($i = 0; $i < 5; $i++) { + $ret = FFI::sizeof($x); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +int(40) diff --git a/ext/ffi/tests/jit/053_alignof.phpt b/ext/ffi/tests/jit/053_alignof.phpt new file mode 100644 index 0000000000000..618111595ceb1 --- /dev/null +++ b/ext/ffi/tests/jit/053_alignof.phpt @@ -0,0 +1,25 @@ +--TEST-- +FFI/JIT 053: FFI::alignof() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int[10]"); + $x[0] = 42; + for ($i = 0; $i < 5; $i++) { + $ret = FFI::alignof($x); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +int(4) diff --git a/ext/ffi/tests/jit/054_typeof.phpt b/ext/ffi/tests/jit/054_typeof.phpt new file mode 100644 index 0000000000000..6fd386ab3e1cc --- /dev/null +++ b/ext/ffi/tests/jit/054_typeof.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 054: FFI::typeof() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int[10]"); + $x[0] = 42; + for ($i = 0; $i < 5; $i++) { + $ret = FFI::typeof($x); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +object(FFI\CType:int32_t[10])#3 (0) { +} diff --git a/ext/ffi/tests/jit/055_memset.phpt b/ext/ffi/tests/jit/055_memset.phpt new file mode 100644 index 0000000000000..403708be4ef40 --- /dev/null +++ b/ext/ffi/tests/jit/055_memset.phpt @@ -0,0 +1,24 @@ +--TEST-- +FFI/JIT 055: FFI::memset() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + for ($i = 0; $i < 5; $i++) { + FFI::memset($x, ord("a"), 9); + } + var_dump(FFI::string($x)); +} +test(); +?> +--EXPECT-- +string(9) "aaaaaaaaa" diff --git a/ext/ffi/tests/jit/056_memcpy_string.phpt b/ext/ffi/tests/jit/056_memcpy_string.phpt new file mode 100644 index 0000000000000..e053f02e1cc91 --- /dev/null +++ b/ext/ffi/tests/jit/056_memcpy_string.phpt @@ -0,0 +1,24 @@ +--TEST-- +FFI/JIT 056: FFI::memcpy() (string) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + for ($i = 0; $i < 5; $i++) { + FFI::memcpy($x, "12345678", 8); + } + var_dump(FFI::string($x)); +} +test(); +?> +--EXPECT-- +string(8) "12345678" diff --git a/ext/ffi/tests/jit/057_memcpy_cdata.phpt b/ext/ffi/tests/jit/057_memcpy_cdata.phpt new file mode 100644 index 0000000000000..1654c279b6fd6 --- /dev/null +++ b/ext/ffi/tests/jit/057_memcpy_cdata.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 057: FFI::memcpy() (CData) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + $y = $ffi->new("char[10]"); + FFI::memcpy($y, "123456789", 9); + for ($i = 0; $i < 5; $i++) { + FFI::memcpy($x, $y, 8); + } + var_dump(FFI::string($x), FFI::string($y)); +} +test(); +?> +--EXPECT-- +string(8) "12345678" +string(9) "123456789" diff --git a/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt b/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt new file mode 100644 index 0000000000000..367d66dc66b64 --- /dev/null +++ b/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt @@ -0,0 +1,25 @@ +--TEST-- +FFI/JIT 058: FFI::memcmp() (CData + string) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + FFI::memcpy($x, "123456789", 9); + for ($i = 0; $i < 5; $i++) { + $ret = FFI::memcmp($x, "1234", 4); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +int(0) diff --git a/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt b/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt new file mode 100644 index 0000000000000..e39506dcfcba1 --- /dev/null +++ b/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt @@ -0,0 +1,25 @@ +--TEST-- +FFI/JIT 059: FFI::memcmp() (string + CData) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + FFI::memcpy($x, "123456789", 9); + for ($i = 0; $i < 5; $i++) { + $ret = FFI::memcmp("124", $x, 3); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +int(1) diff --git a/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt b/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt new file mode 100644 index 0000000000000..4b5024840ff44 --- /dev/null +++ b/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt @@ -0,0 +1,27 @@ +--TEST-- +FFI/JIT 060: FFI::memcmp() (CData + CData) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("char[10]"); + $y = $ffi->new("char[10]"); + FFI::memcpy($x, "123456789", 9); + FFI::memcpy($y, "124456789", 9); + for ($i = 0; $i < 5; $i++) { + $ret = FFI::memcmp($x, $y, 9); + } + var_dump($ret); +} +test(); +?> +--EXPECT-- +int(-1) diff --git a/ext/ffi/tests/jit/061_new_string.phpt b/ext/ffi/tests/jit/061_new_string.phpt new file mode 100644 index 0000000000000..66e535511b740 --- /dev/null +++ b/ext/ffi/tests/jit/061_new_string.phpt @@ -0,0 +1,32 @@ +--TEST-- +FFI/JIT 061: FFI::new() (string) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int[4]"); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[4])#%d (4) { + [0]=> + int(0) + [1]=> + int(0) + [2]=> + int(0) + [3]=> + int(0) +} diff --git a/ext/ffi/tests/jit/062_new_ctype.phpt b/ext/ffi/tests/jit/062_new_ctype.phpt new file mode 100644 index 0000000000000..f98ae44ef9a91 --- /dev/null +++ b/ext/ffi/tests/jit/062_new_ctype.phpt @@ -0,0 +1,33 @@ +--TEST-- +FFI/JIT 062: FFI::new() (CType) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +type("int[4]"); + for ($i = 0; $i < 5; $i++) { + $ret = $ffi->new($type); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[4])#%d (4) { + [0]=> + int(0) + [1]=> + int(0) + [2]=> + int(0) + [3]=> + int(0) +} diff --git a/ext/ffi/tests/jit/063_free.phpt b/ext/ffi/tests/jit/063_free.phpt new file mode 100644 index 0000000000000..02a337bd28eb1 --- /dev/null +++ b/ext/ffi/tests/jit/063_free.phpt @@ -0,0 +1,24 @@ +--TEST-- +FFI/JIT 063: FFI::free() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("int", false); + FFI::free($ret); + } +} +test(); +?> +DONE +--EXPECT-- +DONE diff --git a/ext/ffi/tests/jit/064_cast_string.phpt b/ext/ffi/tests/jit/064_cast_string.phpt new file mode 100644 index 0000000000000..cc88b5cc0b339 --- /dev/null +++ b/ext/ffi/tests/jit/064_cast_string.phpt @@ -0,0 +1,34 @@ +--TEST-- +FFI/JIT 064: FFI::cast() (string) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("unsigned int[4]"); + $x[1] = 42; + for ($i = 0; $i < 5; $i++) { + $ret = $ffi->cast("int[4]", $x); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[4])#%d (4) { + [0]=> + int(0) + [1]=> + int(42) + [2]=> + int(0) + [3]=> + int(0) +} diff --git a/ext/ffi/tests/jit/065_cast_ctype.phpt b/ext/ffi/tests/jit/065_cast_ctype.phpt new file mode 100644 index 0000000000000..fb3b4d71d5104 --- /dev/null +++ b/ext/ffi/tests/jit/065_cast_ctype.phpt @@ -0,0 +1,35 @@ +--TEST-- +FFI/JIT 065: FFI::cast() (CType) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("unsigned int[4]"); + $x[1] = 42; + $type = $ffi->type("int[4]"); + for ($i = 0; $i < 5; $i++) { + $ret = $ffi->cast($type, $x); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t[4])#%d (4) { + [0]=> + int(0) + [1]=> + int(42) + [2]=> + int(0) + [3]=> + int(0) +} diff --git a/ext/ffi/tests/jit/066_cast_scalar.phpt b/ext/ffi/tests/jit/066_cast_scalar.phpt new file mode 100644 index 0000000000000..7f715240a5012 --- /dev/null +++ b/ext/ffi/tests/jit/066_cast_scalar.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 066: FFI::cast() (scalar) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +cast("double", 42); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:double)#%d (1) { + ["cdata"]=> + float(42) +} diff --git a/ext/ffi/tests/jit/067_cast_ptr_int.phpt b/ext/ffi/tests/jit/067_cast_ptr_int.phpt new file mode 100644 index 0000000000000..b2d31a4b4ca4f --- /dev/null +++ b/ext/ffi/tests/jit/067_cast_ptr_int.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 067: FFI::cast() (ptr/int) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +cast("int*", 0); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t*)#%d (1) { + [0]=> + NULL +} diff --git a/ext/ffi/tests/jit/068_cast_ptr_null.phpt b/ext/ffi/tests/jit/068_cast_ptr_null.phpt new file mode 100644 index 0000000000000..0084072b79b92 --- /dev/null +++ b/ext/ffi/tests/jit/068_cast_ptr_null.phpt @@ -0,0 +1,26 @@ +--TEST-- +FFI/JIT 068: FFI::cast() (ptr/null) +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +cast("int*", null); + } + var_dump($ret); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:int32_t*)#2 (1) { + [0]=> + NULL +} diff --git a/ext/ffi/tests/jit/069_addr.phpt b/ext/ffi/tests/jit/069_addr.phpt new file mode 100644 index 0000000000000..7801b22998c39 --- /dev/null +++ b/ext/ffi/tests/jit/069_addr.phpt @@ -0,0 +1,28 @@ +--TEST-- +FFI/JIT 069: Function call with FFI::addr() +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("BUF"); + for ($i = 0; $i < 5; $i++) { + $ret = $ffi->sprintf(FFI::addr($x), "Hello %s", $name); + } + var_dump($ret, FFI::string($x->buf)); +} +test("FFI"); +?> +--EXPECT-- +int(9) +string(9) "Hello FFI" From 7cb6c2ee798fa822177962643d8d27706521370a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 30 Jul 2024 21:07:04 +0300 Subject: [PATCH 053/101] Add SKIPIF sectionis and "stdout" recovery code --- ext/ffi/tests/jit/001_var_read_scalar.phpt | 8 ++++++++ ext/ffi/tests/jit/002_var_read_array.phpt | 8 ++++++++ ext/ffi/tests/jit/003_var_read_struct.phpt | 8 ++++++++ ext/ffi/tests/jit/004_var_read_struct_ptr.phpt | 8 ++++++++ ext/ffi/tests/jit/005_var_read_func.phpt | 8 ++++++++ ext/ffi/tests/jit/006_var_write_scalar.phpt | 10 ++++++++++ ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt | 10 ++++++++++ ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt | 10 ++++++++++ .../tests/jit/009_var_write_struct_ptr_cdata.phpt | 10 ++++++++++ ext/ffi/tests/jit/010_var_modify_scalar.phpt | 10 ++++++++++ ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt | 10 ++++++++++ ext/ffi/tests/jit/012_var_write_scalar_ret.phpt | 10 ++++++++++ ext/ffi/tests/jit/030_var_inc_scalar.phpt | 12 +++++++++++- ext/ffi/tests/jit/031_var_dec_ptr.phpt | 11 +++++++++++ ext/ffi/tests/jit/040_call.phpt | 8 ++++++++ ext/ffi/tests/jit/041_call_tmp_scalar.phpt | 8 ++++++++ ext/ffi/tests/jit/042_call_tmp_cdata.phpt | 8 ++++++++ ext/ffi/tests/jit/050_isNull.phpt | 8 ++++++++ ext/ffi/tests/jit/069_addr.phpt | 8 ++++++++ 19 files changed, 172 insertions(+), 1 deletion(-) diff --git a/ext/ffi/tests/jit/001_var_read_scalar.phpt b/ext/ffi/tests/jit/001_var_read_scalar.phpt index 76103be8426eb..4a0c2042d7ffc 100644 --- a/ext/ffi/tests/jit/001_var_read_scalar.phpt +++ b/ext/ffi/tests/jit/001_var_read_scalar.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- stdout; for ($i = 0; $i < 5; $i++) { $ffi->stdout = $i; } var_dump($ffi->stdout); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt index d4c466ec5a368..e418fa0c93ad0 100644 --- a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt +++ b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; $x = $ffi->cast('int64_t', 42); var_dump($x); for ($i = 0; $i < 5; $i++) { $ffi->stdout = $x; } var_dump($ffi->stdout); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt index 1d42a475cd859..e81218b6ef2dd 100644 --- a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt +++ b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; for ($i = 0; $i < 5; $i++) { $ffi->stdout = null; } var_dump($ffi->stdout); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt index f147bae0a4810..bf17e206f3073 100644 --- a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt +++ b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; $x = $ffi->cast('FILE*', 42); var_dump($x); for ($i = 0; $i < 5; $i++) { @@ -23,6 +32,7 @@ function test() { } var_dump($ffi->stdout); var_dump($ffi->cast('intptr_t', $ffi->stdout)); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/010_var_modify_scalar.phpt b/ext/ffi/tests/jit/010_var_modify_scalar.phpt index 3edfac67fbfb0..dc34f342395d5 100644 --- a/ext/ffi/tests/jit/010_var_modify_scalar.phpt +++ b/ext/ffi/tests/jit/010_var_modify_scalar.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; $ffi->stdout = null; for ($i = 0; $i < 5; $i++) { $ffi->stdout += $i; } var_dump($ffi->stdout); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt index 33ffa66b94d9a..fe60238e16990 100644 --- a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt +++ b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; $ffi->stdout = $ffi->cast('FILE*', 42); for ($i = 0; $i < 5; $i++) { $ffi->stdout += $i; } var_dump($ffi->cast('intptr_t', $ffi->stdout)); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt index ce56da2a9f50f..807e0ada03d1e 100644 --- a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt +++ b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; for ($i = 0; $i < 5; $i++) { $ret = $ffi->stdout = $i; } var_dump($ffi->stdout, $ret); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/030_var_inc_scalar.phpt b/ext/ffi/tests/jit/030_var_inc_scalar.phpt index 24f440d6545fc..7fd2b461508e3 100644 --- a/ext/ffi/tests/jit/030_var_inc_scalar.phpt +++ b/ext/ffi/tests/jit/030_var_inc_scalar.phpt @@ -8,18 +8,28 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; $ffi->stdout = null; for ($i = 0; $i < 5; $i++) { $ffi->stdout++; } var_dump($ffi->stdout); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/031_var_dec_ptr.phpt b/ext/ffi/tests/jit/031_var_dec_ptr.phpt index 47df85b8d2344..711ed56c7762d 100644 --- a/ext/ffi/tests/jit/031_var_dec_ptr.phpt +++ b/ext/ffi/tests/jit/031_var_dec_ptr.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- stdout; + $ffi->stdout = $old; $ffi->stdout = $ffi->cast('FILE*', 42); for ($i = 0; $i < 5; $i++) { $ffi->stdout--; } var_dump($ffi->cast('intptr_t', $ffi->stdout)); + $ffi->stdout = $old; } test(); ?> diff --git a/ext/ffi/tests/jit/040_call.phpt b/ext/ffi/tests/jit/040_call.phpt index 54278bdec0fa1..d2737dd30db87 100644 --- a/ext/ffi/tests/jit/040_call.phpt +++ b/ext/ffi/tests/jit/040_call.phpt @@ -8,6 +8,14 @@ opcache.jit_hot_func=0 opcache.jit_hot_return=0 opcache.jit_hot_side_exit=0 ;opcache.jit_debug=0x180005 +--SKIPIF-- + --FILE-- --FILE-- --FILE-- --FILE-- --FILE-- Date: Tue, 30 Jul 2024 22:19:33 +0300 Subject: [PATCH 054/101] Reciver "stdout" before calling var_dump() --- ext/ffi/tests/jit/006_var_write_scalar.phpt | 3 ++- ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt | 3 ++- ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt | 3 ++- ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt | 10 ++++------ ext/ffi/tests/jit/010_var_modify_scalar.phpt | 3 ++- ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt | 10 ++++------ ext/ffi/tests/jit/012_var_write_scalar_ret.phpt | 3 ++- ext/ffi/tests/jit/030_var_inc_scalar.phpt | 3 ++- ext/ffi/tests/jit/031_var_dec_ptr.phpt | 10 ++++------ 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ext/ffi/tests/jit/006_var_write_scalar.phpt b/ext/ffi/tests/jit/006_var_write_scalar.phpt index c454a049f2109..d627d1fef6a19 100644 --- a/ext/ffi/tests/jit/006_var_write_scalar.phpt +++ b/ext/ffi/tests/jit/006_var_write_scalar.phpt @@ -27,8 +27,9 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout = $i; } - var_dump($ffi->stdout); + $out = $ffi->stdout; $ffi->stdout = $old; + var_dump($out); } test(); ?> diff --git a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt index e418fa0c93ad0..3b5cd234d072d 100644 --- a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt +++ b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt @@ -29,8 +29,9 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout = $x; } - var_dump($ffi->stdout); + $out = $ffi->stdout; $ffi->stdout = $old; + var_dump($out); } test(); ?> diff --git a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt index e81218b6ef2dd..0fdfdfdd39e8b 100644 --- a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt +++ b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt @@ -28,8 +28,9 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout = null; } - var_dump($ffi->stdout); + $out = $ffi->stdout; $ffi->stdout = $old; + var_dump($out); } test(); ?> diff --git a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt index bf17e206f3073..54001e941cd13 100644 --- a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt +++ b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt @@ -30,9 +30,10 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout = $x; } - var_dump($ffi->stdout); - var_dump($ffi->cast('intptr_t', $ffi->stdout)); + $out1 = $ffi->stdout; + $out2 = $ffi->cast('intptr_t', $ffi->stdout)->cdata; $ffi->stdout = $old; + var_dump($out1, $out2); } test(); ?> @@ -47,7 +48,4 @@ object(FFI\CData:struct _IO_FILE*)#%d (1) { object(FFI\CData:struct _IO_FILE)#%d (0) { } } -object(FFI\CData:int%d_t)#%d (1) { - ["cdata"]=> - int(42) -} +int(42) diff --git a/ext/ffi/tests/jit/010_var_modify_scalar.phpt b/ext/ffi/tests/jit/010_var_modify_scalar.phpt index dc34f342395d5..51a4d07f78e3a 100644 --- a/ext/ffi/tests/jit/010_var_modify_scalar.phpt +++ b/ext/ffi/tests/jit/010_var_modify_scalar.phpt @@ -28,8 +28,9 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout += $i; } - var_dump($ffi->stdout); + $out = $ffi->stdout; $ffi->stdout = $old; + var_dump($out); } test(); ?> diff --git a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt index fe60238e16990..cac525958755d 100644 --- a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt +++ b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt @@ -29,13 +29,11 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout += $i; } - var_dump($ffi->cast('intptr_t', $ffi->stdout)); + $out = $ffi->cast('intptr_t', $ffi->stdout)->cdata; $ffi->stdout = $old; + var_dump($out); } test(); ?> ---EXPECTF-- -object(FFI\CData:int%d_t)#%d (1) { - ["cdata"]=> - int(82) -} +--EXPECT-- +int(82) diff --git a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt index 807e0ada03d1e..26c2bbb8e859e 100644 --- a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt +++ b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt @@ -27,8 +27,9 @@ function test() { for ($i = 0; $i < 5; $i++) { $ret = $ffi->stdout = $i; } - var_dump($ffi->stdout, $ret); + $out = $ffi->stdout; $ffi->stdout = $old; + var_dump($out, $ret); } test(); ?> diff --git a/ext/ffi/tests/jit/030_var_inc_scalar.phpt b/ext/ffi/tests/jit/030_var_inc_scalar.phpt index 7fd2b461508e3..979e8059e3b8b 100644 --- a/ext/ffi/tests/jit/030_var_inc_scalar.phpt +++ b/ext/ffi/tests/jit/030_var_inc_scalar.phpt @@ -28,8 +28,9 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout++; } - var_dump($ffi->stdout); + $out = $ffi->stdout; $ffi->stdout = $old; + var_dump($out); } test(); ?> diff --git a/ext/ffi/tests/jit/031_var_dec_ptr.phpt b/ext/ffi/tests/jit/031_var_dec_ptr.phpt index 711ed56c7762d..9d22c9b7031f8 100644 --- a/ext/ffi/tests/jit/031_var_dec_ptr.phpt +++ b/ext/ffi/tests/jit/031_var_dec_ptr.phpt @@ -30,13 +30,11 @@ function test() { for ($i = 0; $i < 5; $i++) { $ffi->stdout--; } - var_dump($ffi->cast('intptr_t', $ffi->stdout)); + $out = $ffi->cast('intptr_t', $ffi->stdout)->cdata; $ffi->stdout = $old; + var_dump($out); } test(); ?> ---EXPECTF-- -object(FFI\CData:int%d_t)#%d (1) { - ["cdata"]=> - int(22) -} +--EXPECT-- +int(22) From 65db269d4cfde0734b5e38db73975633521421ef Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 30 Jul 2024 22:26:26 +0300 Subject: [PATCH 055/101] Add EXTENSIONS section --- ext/ffi/tests/jit/001_var_read_scalar.phpt | 2 ++ ext/ffi/tests/jit/002_var_read_array.phpt | 2 ++ ext/ffi/tests/jit/003_var_read_struct.phpt | 2 ++ ext/ffi/tests/jit/004_var_read_struct_ptr.phpt | 2 ++ ext/ffi/tests/jit/005_var_read_func.phpt | 2 ++ ext/ffi/tests/jit/006_var_write_scalar.phpt | 2 ++ ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt | 2 ++ ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt | 2 ++ ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt | 2 ++ ext/ffi/tests/jit/010_var_modify_scalar.phpt | 2 ++ ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt | 2 ++ ext/ffi/tests/jit/012_var_write_scalar_ret.phpt | 2 ++ ext/ffi/tests/jit/013_array_read_scalar.phpt | 2 ++ ext/ffi/tests/jit/014_array_read_nested_array.phpt | 2 ++ ext/ffi/tests/jit/015_array_read_nested_struct.phpt | 2 ++ ext/ffi/tests/jit/016_struct_read_scalar.phpt | 2 ++ ext/ffi/tests/jit/017_struct_read_nested_array.phpt | 2 ++ ext/ffi/tests/jit/018_array_write_scalar.phpt | 2 ++ ext/ffi/tests/jit/019_array_write_nested_array.phpt | 2 ++ ext/ffi/tests/jit/020_array_write_nested_struct.phpt | 2 ++ ext/ffi/tests/jit/021_struct_write_scalar.phpt | 2 ++ ext/ffi/tests/jit/022_struct_write_nested_array.phpt | 2 ++ ext/ffi/tests/jit/023_struct_write_nested_struct.phpt | 2 ++ ext/ffi/tests/jit/024_array_modify_scalar.phpt | 2 ++ ext/ffi/tests/jit/025_array_modify_nested_array.phpt | 2 ++ ext/ffi/tests/jit/026_array_modify_nested_struct.phpt | 2 ++ ext/ffi/tests/jit/027_struct_modify_scalar.phpt | 2 ++ ext/ffi/tests/jit/028_struct_modify_nested_array.phpt | 2 ++ ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt | 2 ++ ext/ffi/tests/jit/030_var_inc_scalar.phpt | 2 ++ ext/ffi/tests/jit/031_var_dec_ptr.phpt | 2 ++ ext/ffi/tests/jit/032_struct_preinc_scalar.phpt | 2 ++ ext/ffi/tests/jit/033_struct_postdec_scalar.phpt | 2 ++ ext/ffi/tests/jit/034_struct_postinc_ptr.phpt | 2 ++ ext/ffi/tests/jit/035_struct_predec_ptr.phpt | 2 ++ ext/ffi/tests/jit/036_scalar_cdata_read.phpt | 2 ++ ext/ffi/tests/jit/037_scalar_cdata_write.phpt | 2 ++ ext/ffi/tests/jit/038_scalar_cdata_modify.phpt | 2 ++ ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt | 2 ++ ext/ffi/tests/jit/040_call.phpt | 2 ++ ext/ffi/tests/jit/041_call_tmp_scalar.phpt | 2 ++ ext/ffi/tests/jit/042_call_tmp_cdata.phpt | 2 ++ ext/ffi/tests/jit/050_isNull.phpt | 2 ++ ext/ffi/tests/jit/051_string.phpt | 2 ++ ext/ffi/tests/jit/052_sizeof.phpt | 2 ++ ext/ffi/tests/jit/053_alignof.phpt | 2 ++ ext/ffi/tests/jit/054_typeof.phpt | 2 ++ ext/ffi/tests/jit/055_memset.phpt | 2 ++ ext/ffi/tests/jit/056_memcpy_string.phpt | 2 ++ ext/ffi/tests/jit/057_memcpy_cdata.phpt | 2 ++ ext/ffi/tests/jit/058_memcmp_cdata_string.phpt | 2 ++ ext/ffi/tests/jit/059_memcmp_cdata_string.phpt | 2 ++ ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt | 2 ++ ext/ffi/tests/jit/061_new_string.phpt | 2 ++ ext/ffi/tests/jit/062_new_ctype.phpt | 2 ++ ext/ffi/tests/jit/063_free.phpt | 2 ++ ext/ffi/tests/jit/064_cast_string.phpt | 2 ++ ext/ffi/tests/jit/065_cast_ctype.phpt | 2 ++ ext/ffi/tests/jit/066_cast_scalar.phpt | 2 ++ ext/ffi/tests/jit/067_cast_ptr_int.phpt | 2 ++ ext/ffi/tests/jit/068_cast_ptr_null.phpt | 2 ++ ext/ffi/tests/jit/069_addr.phpt | 2 ++ 62 files changed, 124 insertions(+) diff --git a/ext/ffi/tests/jit/001_var_read_scalar.phpt b/ext/ffi/tests/jit/001_var_read_scalar.phpt index 4a0c2042d7ffc..8ca42b29f85f9 100644 --- a/ext/ffi/tests/jit/001_var_read_scalar.phpt +++ b/ext/ffi/tests/jit/001_var_read_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 001: Read Variable (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/002_var_read_array.phpt b/ext/ffi/tests/jit/002_var_read_array.phpt index 4fd2113f5929a..e1d4936a4a685 100644 --- a/ext/ffi/tests/jit/002_var_read_array.phpt +++ b/ext/ffi/tests/jit/002_var_read_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 002: Read Variable (array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/003_var_read_struct.phpt b/ext/ffi/tests/jit/003_var_read_struct.phpt index 9cf3b950e8085..5803ce92e983c 100644 --- a/ext/ffi/tests/jit/003_var_read_struct.phpt +++ b/ext/ffi/tests/jit/003_var_read_struct.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 003: Read Variable (struct) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt b/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt index 578d738c12c48..5fb4ceefaef93 100644 --- a/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt +++ b/ext/ffi/tests/jit/004_var_read_struct_ptr.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 004: Read Variable (struct ptr) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/005_var_read_func.phpt b/ext/ffi/tests/jit/005_var_read_func.phpt index 7f90932b4ca23..a30b69dacf8fd 100644 --- a/ext/ffi/tests/jit/005_var_read_func.phpt +++ b/ext/ffi/tests/jit/005_var_read_func.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 005: Read Variable (func) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/006_var_write_scalar.phpt b/ext/ffi/tests/jit/006_var_write_scalar.phpt index d627d1fef6a19..642fc5ee44aa5 100644 --- a/ext/ffi/tests/jit/006_var_write_scalar.phpt +++ b/ext/ffi/tests/jit/006_var_write_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 006: Write Variable (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt index 3b5cd234d072d..a84801096f352 100644 --- a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt +++ b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 007: Write Variable (scalar cdata) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt index 0fdfdfdd39e8b..ba15c2abfab54 100644 --- a/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt +++ b/ext/ffi/tests/jit/008_var_write_struct_ptr_null.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 008: Write Variable (struct ptr null) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt index 54001e941cd13..af290f57704cf 100644 --- a/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt +++ b/ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 009: Write Variable (struct ptr cdata) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/010_var_modify_scalar.phpt b/ext/ffi/tests/jit/010_var_modify_scalar.phpt index 51a4d07f78e3a..068760779ff4c 100644 --- a/ext/ffi/tests/jit/010_var_modify_scalar.phpt +++ b/ext/ffi/tests/jit/010_var_modify_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 010: Modify Variable (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt index cac525958755d..5e3d993b5ea53 100644 --- a/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt +++ b/ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 011: Modify Variable (struct ptr) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt index 26c2bbb8e859e..625f6f8b2f28b 100644 --- a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt +++ b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 012: Write Variable (scalar + ret) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/013_array_read_scalar.phpt b/ext/ffi/tests/jit/013_array_read_scalar.phpt index be2e00c7dce17..560f509ebb8fc 100644 --- a/ext/ffi/tests/jit/013_array_read_scalar.phpt +++ b/ext/ffi/tests/jit/013_array_read_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 013: Read Array (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/014_array_read_nested_array.phpt b/ext/ffi/tests/jit/014_array_read_nested_array.phpt index 67928c803fdd7..9aea8b4004b1c 100644 --- a/ext/ffi/tests/jit/014_array_read_nested_array.phpt +++ b/ext/ffi/tests/jit/014_array_read_nested_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 014: Read Array (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/015_array_read_nested_struct.phpt b/ext/ffi/tests/jit/015_array_read_nested_struct.phpt index 0bc4a9557b8ae..89978b74726de 100644 --- a/ext/ffi/tests/jit/015_array_read_nested_struct.phpt +++ b/ext/ffi/tests/jit/015_array_read_nested_struct.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 015: Read Array (nested struct) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/016_struct_read_scalar.phpt b/ext/ffi/tests/jit/016_struct_read_scalar.phpt index 2f4cf6b8ce9e7..fbe417dd79121 100644 --- a/ext/ffi/tests/jit/016_struct_read_scalar.phpt +++ b/ext/ffi/tests/jit/016_struct_read_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 016: Read Struct (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/017_struct_read_nested_array.phpt b/ext/ffi/tests/jit/017_struct_read_nested_array.phpt index 7216bd2997bf7..ff1bbf9e57748 100644 --- a/ext/ffi/tests/jit/017_struct_read_nested_array.phpt +++ b/ext/ffi/tests/jit/017_struct_read_nested_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 017: Read Struct (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/018_array_write_scalar.phpt b/ext/ffi/tests/jit/018_array_write_scalar.phpt index 920e81e171096..94af6e0bef7bd 100644 --- a/ext/ffi/tests/jit/018_array_write_scalar.phpt +++ b/ext/ffi/tests/jit/018_array_write_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 018: Array Write (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/019_array_write_nested_array.phpt b/ext/ffi/tests/jit/019_array_write_nested_array.phpt index 5d58bdce7401e..d68b756fb565c 100644 --- a/ext/ffi/tests/jit/019_array_write_nested_array.phpt +++ b/ext/ffi/tests/jit/019_array_write_nested_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 019: Array Write (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/020_array_write_nested_struct.phpt b/ext/ffi/tests/jit/020_array_write_nested_struct.phpt index 6d3a15f224a13..4b4becc15cf24 100644 --- a/ext/ffi/tests/jit/020_array_write_nested_struct.phpt +++ b/ext/ffi/tests/jit/020_array_write_nested_struct.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 020: Array Write (nested struct) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/021_struct_write_scalar.phpt b/ext/ffi/tests/jit/021_struct_write_scalar.phpt index 1bcf7d5350e0d..96912f2caabbd 100644 --- a/ext/ffi/tests/jit/021_struct_write_scalar.phpt +++ b/ext/ffi/tests/jit/021_struct_write_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 021: Write Struct (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/022_struct_write_nested_array.phpt b/ext/ffi/tests/jit/022_struct_write_nested_array.phpt index 8e5581973eef5..07e5b09393205 100644 --- a/ext/ffi/tests/jit/022_struct_write_nested_array.phpt +++ b/ext/ffi/tests/jit/022_struct_write_nested_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 022: Write Struct (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt b/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt index c1785b180e823..7fea436e988f2 100644 --- a/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt +++ b/ext/ffi/tests/jit/023_struct_write_nested_struct.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 022: Write Struct (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/024_array_modify_scalar.phpt b/ext/ffi/tests/jit/024_array_modify_scalar.phpt index 5825edbfd843e..8cd48ce9ab923 100644 --- a/ext/ffi/tests/jit/024_array_modify_scalar.phpt +++ b/ext/ffi/tests/jit/024_array_modify_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 024: Array Modification (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/025_array_modify_nested_array.phpt b/ext/ffi/tests/jit/025_array_modify_nested_array.phpt index afc668a317d6e..b0a4df12e3246 100644 --- a/ext/ffi/tests/jit/025_array_modify_nested_array.phpt +++ b/ext/ffi/tests/jit/025_array_modify_nested_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 025: Array Modification (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt b/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt index 814cba1f8c2f1..ebee45e98c7bd 100644 --- a/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt +++ b/ext/ffi/tests/jit/026_array_modify_nested_struct.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 026: Array Modification (nested struct) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/027_struct_modify_scalar.phpt b/ext/ffi/tests/jit/027_struct_modify_scalar.phpt index 80dedb4cd992a..191e0cbee934d 100644 --- a/ext/ffi/tests/jit/027_struct_modify_scalar.phpt +++ b/ext/ffi/tests/jit/027_struct_modify_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 027: Modify Struct (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt b/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt index 7635d32d5140e..8675589a7a570 100644 --- a/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt +++ b/ext/ffi/tests/jit/028_struct_modify_nested_array.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 028: Modify Struct (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt b/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt index cfdedd585c3d7..16d15f3e95f2f 100644 --- a/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt +++ b/ext/ffi/tests/jit/029_struct_modify_nested_struct.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 029: Modify Struct (nested array) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/030_var_inc_scalar.phpt b/ext/ffi/tests/jit/030_var_inc_scalar.phpt index 979e8059e3b8b..6e967999cd250 100644 --- a/ext/ffi/tests/jit/030_var_inc_scalar.phpt +++ b/ext/ffi/tests/jit/030_var_inc_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 030: Increment Variable (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/031_var_dec_ptr.phpt b/ext/ffi/tests/jit/031_var_dec_ptr.phpt index 9d22c9b7031f8..4389393118041 100644 --- a/ext/ffi/tests/jit/031_var_dec_ptr.phpt +++ b/ext/ffi/tests/jit/031_var_dec_ptr.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 031: Decrement Variable (ptr) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt b/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt index 7ae5e0f301cb1..a831fc8ea1564 100644 --- a/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt +++ b/ext/ffi/tests/jit/032_struct_preinc_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 032: PREINC Struct (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt b/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt index 1575a893e128e..073143d1fd9d0 100644 --- a/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt +++ b/ext/ffi/tests/jit/033_struct_postdec_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 033: POSTDEC Struct (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt b/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt index 1a15c7f1deb74..5ef5f402efc8e 100644 --- a/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt +++ b/ext/ffi/tests/jit/034_struct_postinc_ptr.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 034: POSTINC Struct (ptr) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/035_struct_predec_ptr.phpt b/ext/ffi/tests/jit/035_struct_predec_ptr.phpt index 611b0b9708115..6e756d4ba8ee4 100644 --- a/ext/ffi/tests/jit/035_struct_predec_ptr.phpt +++ b/ext/ffi/tests/jit/035_struct_predec_ptr.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 035: PREDEC Struct (ptr) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/036_scalar_cdata_read.phpt b/ext/ffi/tests/jit/036_scalar_cdata_read.phpt index 183d7af747f57..59659d5219b2a 100644 --- a/ext/ffi/tests/jit/036_scalar_cdata_read.phpt +++ b/ext/ffi/tests/jit/036_scalar_cdata_read.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 036: Scalar CData Read +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/037_scalar_cdata_write.phpt b/ext/ffi/tests/jit/037_scalar_cdata_write.phpt index 6c371edcb195a..55ba8a7b7052f 100644 --- a/ext/ffi/tests/jit/037_scalar_cdata_write.phpt +++ b/ext/ffi/tests/jit/037_scalar_cdata_write.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 037: Scalar CData Write +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt b/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt index 25df988389d6c..c9e069ab4d415 100644 --- a/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt +++ b/ext/ffi/tests/jit/038_scalar_cdata_modify.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 038: Scalar CData Modification +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt b/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt index fe606842c7310..92a4279bbbb15 100644 --- a/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt +++ b/ext/ffi/tests/jit/039_scalar_cdata_postinc.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 039: Scalar CData post increment +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/040_call.phpt b/ext/ffi/tests/jit/040_call.phpt index d2737dd30db87..2e3c5b919cff9 100644 --- a/ext/ffi/tests/jit/040_call.phpt +++ b/ext/ffi/tests/jit/040_call.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 040: Function call +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/041_call_tmp_scalar.phpt b/ext/ffi/tests/jit/041_call_tmp_scalar.phpt index cf9761acb28e4..49b41e3855284 100644 --- a/ext/ffi/tests/jit/041_call_tmp_scalar.phpt +++ b/ext/ffi/tests/jit/041_call_tmp_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 041: Function call (tmp scalar arg) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/042_call_tmp_cdata.phpt b/ext/ffi/tests/jit/042_call_tmp_cdata.phpt index 6b0b1e90548a9..b9771b468a536 100644 --- a/ext/ffi/tests/jit/042_call_tmp_cdata.phpt +++ b/ext/ffi/tests/jit/042_call_tmp_cdata.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 042: Function call (tmp CData arg) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/050_isNull.phpt b/ext/ffi/tests/jit/050_isNull.phpt index 3f8a9f56a5c97..145f999d4690e 100644 --- a/ext/ffi/tests/jit/050_isNull.phpt +++ b/ext/ffi/tests/jit/050_isNull.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 050: FFI::isNull() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/051_string.phpt b/ext/ffi/tests/jit/051_string.phpt index d29edb360b593..bad524688748b 100644 --- a/ext/ffi/tests/jit/051_string.phpt +++ b/ext/ffi/tests/jit/051_string.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 051: FFI::string() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/052_sizeof.phpt b/ext/ffi/tests/jit/052_sizeof.phpt index 969f64634d96d..52a3c42131abe 100644 --- a/ext/ffi/tests/jit/052_sizeof.phpt +++ b/ext/ffi/tests/jit/052_sizeof.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 052: FFI::sizeof() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/053_alignof.phpt b/ext/ffi/tests/jit/053_alignof.phpt index 618111595ceb1..5797124d38eb9 100644 --- a/ext/ffi/tests/jit/053_alignof.phpt +++ b/ext/ffi/tests/jit/053_alignof.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 053: FFI::alignof() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/054_typeof.phpt b/ext/ffi/tests/jit/054_typeof.phpt index 6fd386ab3e1cc..186a63576d5e3 100644 --- a/ext/ffi/tests/jit/054_typeof.phpt +++ b/ext/ffi/tests/jit/054_typeof.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 054: FFI::typeof() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/055_memset.phpt b/ext/ffi/tests/jit/055_memset.phpt index 403708be4ef40..7258a9a677d63 100644 --- a/ext/ffi/tests/jit/055_memset.phpt +++ b/ext/ffi/tests/jit/055_memset.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 055: FFI::memset() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/056_memcpy_string.phpt b/ext/ffi/tests/jit/056_memcpy_string.phpt index e053f02e1cc91..61bbf22da84f7 100644 --- a/ext/ffi/tests/jit/056_memcpy_string.phpt +++ b/ext/ffi/tests/jit/056_memcpy_string.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 056: FFI::memcpy() (string) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/057_memcpy_cdata.phpt b/ext/ffi/tests/jit/057_memcpy_cdata.phpt index 1654c279b6fd6..192987aa09c79 100644 --- a/ext/ffi/tests/jit/057_memcpy_cdata.phpt +++ b/ext/ffi/tests/jit/057_memcpy_cdata.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 057: FFI::memcpy() (CData) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt b/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt index 367d66dc66b64..5d17fd286446a 100644 --- a/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt +++ b/ext/ffi/tests/jit/058_memcmp_cdata_string.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 058: FFI::memcmp() (CData + string) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt b/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt index e39506dcfcba1..45e152822b9c9 100644 --- a/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt +++ b/ext/ffi/tests/jit/059_memcmp_cdata_string.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 059: FFI::memcmp() (string + CData) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt b/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt index 4b5024840ff44..edfbc55eb602b 100644 --- a/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt +++ b/ext/ffi/tests/jit/060_memcmp_cdata_cdata.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 060: FFI::memcmp() (CData + CData) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/061_new_string.phpt b/ext/ffi/tests/jit/061_new_string.phpt index 66e535511b740..f0584da7c152a 100644 --- a/ext/ffi/tests/jit/061_new_string.phpt +++ b/ext/ffi/tests/jit/061_new_string.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 061: FFI::new() (string) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/062_new_ctype.phpt b/ext/ffi/tests/jit/062_new_ctype.phpt index f98ae44ef9a91..ae87d42ab5840 100644 --- a/ext/ffi/tests/jit/062_new_ctype.phpt +++ b/ext/ffi/tests/jit/062_new_ctype.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 062: FFI::new() (CType) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/063_free.phpt b/ext/ffi/tests/jit/063_free.phpt index 02a337bd28eb1..77c756f0e2b0c 100644 --- a/ext/ffi/tests/jit/063_free.phpt +++ b/ext/ffi/tests/jit/063_free.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 063: FFI::free() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/064_cast_string.phpt b/ext/ffi/tests/jit/064_cast_string.phpt index cc88b5cc0b339..20b1f4ddab468 100644 --- a/ext/ffi/tests/jit/064_cast_string.phpt +++ b/ext/ffi/tests/jit/064_cast_string.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 064: FFI::cast() (string) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/065_cast_ctype.phpt b/ext/ffi/tests/jit/065_cast_ctype.phpt index fb3b4d71d5104..611cdcc0f89a6 100644 --- a/ext/ffi/tests/jit/065_cast_ctype.phpt +++ b/ext/ffi/tests/jit/065_cast_ctype.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 065: FFI::cast() (CType) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/066_cast_scalar.phpt b/ext/ffi/tests/jit/066_cast_scalar.phpt index 7f715240a5012..f8569be5e85b4 100644 --- a/ext/ffi/tests/jit/066_cast_scalar.phpt +++ b/ext/ffi/tests/jit/066_cast_scalar.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 066: FFI::cast() (scalar) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/067_cast_ptr_int.phpt b/ext/ffi/tests/jit/067_cast_ptr_int.phpt index b2d31a4b4ca4f..0cb9346111cc2 100644 --- a/ext/ffi/tests/jit/067_cast_ptr_int.phpt +++ b/ext/ffi/tests/jit/067_cast_ptr_int.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 067: FFI::cast() (ptr/int) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/068_cast_ptr_null.phpt b/ext/ffi/tests/jit/068_cast_ptr_null.phpt index 0084072b79b92..8d50367706730 100644 --- a/ext/ffi/tests/jit/068_cast_ptr_null.phpt +++ b/ext/ffi/tests/jit/068_cast_ptr_null.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 068: FFI::cast() (ptr/null) +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing diff --git a/ext/ffi/tests/jit/069_addr.phpt b/ext/ffi/tests/jit/069_addr.phpt index 2949128a22763..b7d1aea367b32 100644 --- a/ext/ffi/tests/jit/069_addr.phpt +++ b/ext/ffi/tests/jit/069_addr.phpt @@ -1,5 +1,7 @@ --TEST-- FFI/JIT 069: Function call with FFI::addr() +--EXTENSIONS-- +ffi --INI-- ffi.enable=1 ;opcache.jit=tracing From 739825488480501dacd73bf927408f356eaf3f3e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 30 Jul 2024 22:34:58 +0300 Subject: [PATCH 056/101] Fix SKIPIF sections --- ext/ffi/tests/jit/001_var_read_scalar.phpt | 2 +- ext/ffi/tests/jit/002_var_read_array.phpt | 2 +- ext/ffi/tests/jit/003_var_read_struct.phpt | 2 +- ext/ffi/tests/jit/004_var_read_struct_ptr.phpt | 2 +- ext/ffi/tests/jit/005_var_read_func.phpt | 2 +- ext/ffi/tests/jit/006_var_write_scalar.phpt | 2 +- ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt | 2 +- ext/ffi/tests/jit/009_var_write_struct_ptr_cdata.phpt | 2 +- ext/ffi/tests/jit/010_var_modify_scalar.phpt | 2 +- ext/ffi/tests/jit/011_var_modify_struct_ptr.phpt | 2 +- ext/ffi/tests/jit/012_var_write_scalar_ret.phpt | 2 +- ext/ffi/tests/jit/030_var_inc_scalar.phpt | 2 +- ext/ffi/tests/jit/031_var_dec_ptr.phpt | 2 +- ext/ffi/tests/jit/040_call.phpt | 2 +- ext/ffi/tests/jit/050_isNull.phpt | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/ffi/tests/jit/001_var_read_scalar.phpt b/ext/ffi/tests/jit/001_var_read_scalar.phpt index 8ca42b29f85f9..750514fcd9ea7 100644 --- a/ext/ffi/tests/jit/001_var_read_scalar.phpt +++ b/ext/ffi/tests/jit/001_var_read_scalar.phpt @@ -13,7 +13,7 @@ opcache.jit_hot_side_exit=0 --SKIPIF-- Date: Mon, 5 Aug 2024 16:51:51 +0300 Subject: [PATCH 057/101] JIT/FFI support for closure calls (pointers to functions) --- ext/opcache/jit/zend_jit_ir_ffi.c | 90 ++++++++++++++++++++++--------- ext/opcache/jit/zend_jit_trace.c | 49 ++++++++++++++--- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 66820a31500ca..3ad87e7136e16 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -30,6 +30,15 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, HashTable *ffi_symbols, zend_jit_ffi_info *ffi_info); +static int zend_jit_ffi_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + ir_ref ref, + zend_ffi_type *ffi_type, + zend_jit_ffi_info *ffi_info); + static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -39,12 +48,56 @@ static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, zend_jit_addr op1_addr, zend_ffi_symbol *sym, HashTable *op1_ffi_symbols, - zend_jit_ffi_info *ffi_info) + zend_jit_ffi_info *ffi_info, + ir_ref *ffi_func_ref) { + zend_ffi_type *type; + + ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); + type = ZEND_FFI_TYPE(sym->type); + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { return 0; } + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + *ffi_func_ref = ir_CONST_FC_FUNC(sym->addr); + } else { + *ffi_func_ref = ir_CONST_FUNC(sym->addr); + } + return 1; +} + +static int zend_jit_ffi_init_call_obj(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + uint32_t op1_info, + zend_jit_addr op1_addr, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ffi_type *op2_ffi_type, + zend_jit_ffi_info *ffi_info, + ir_ref *ffi_func_ref) +{ + ir_ref obj_ref = jit_Z_PTR(jit, op2_addr); + zend_ffi_type *type; + + ZEND_ASSERT(op2_ffi_type->kind == ZEND_FFI_TYPE_POINTER); + type = ZEND_FFI_TYPE(op2_ffi_type->pointer.type); + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); + + if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op2_use, -1, obj_ref, op2_ffi_type, ffi_info)) { + return 0; + } + + *ffi_func_ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, obj_ref)); + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + *ffi_func_ref = ir_CAST_FC_FUNC(*ffi_func_ref); + } + return 1; } @@ -60,12 +113,9 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, { zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; zend_jit_trace_stack *stack = call->stack; - zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; - zend_ffi_type *type; + zend_ffi_type *type = (zend_ffi_type*)(void*)call->call_opline; ir_ref ref = IR_UNUSED; - ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); - type = ZEND_FFI_TYPE(sym->type); ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); if (type->attr & ZEND_FFI_ATTR_VARIADIC) { ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) >= zend_hash_num_elements(type->func.args)); @@ -225,22 +275,20 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, return 1; } -static int zend_jit_ffi_do_call_sym(zend_jit_ctx *jit, - const zend_op *opline, - const zend_op_array *op_array, - zend_ssa *ssa, - const zend_ssa_op *ssa_op, - zend_jit_addr res_addr) +static int zend_jit_ffi_do_call(zend_jit_ctx *jit, + const zend_op *opline, + const zend_op_array *op_array, + zend_ssa *ssa, + const zend_ssa_op *ssa_op, + zend_jit_addr res_addr) { zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; - zend_ffi_symbol *sym = (zend_ffi_symbol*)(void*)call->call_opline; + zend_ffi_type *type = (zend_ffi_type*)(void*)call->call_opline; + ir_ref func_ref = (intptr_t)(void*)call->ce; uint32_t i, num_args; - zend_ffi_type *type; ir_type ret_type = IR_VOID; ir_ref ref = IR_UNUSED; - ZEND_ASSERT(sym->kind == ZEND_FFI_SYM_FUNC); - type = ZEND_FFI_TYPE(sym->type); ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); switch (ZEND_FFI_TYPE(type->func.ret_type)->kind) { @@ -300,18 +348,10 @@ static int zend_jit_ffi_do_call_sym(zend_jit_ctx *jit, for (i = 0; i < num_args; i++) { args[i] = STACK_REF(stack, i); } - if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { - ref = ir_CALL_N(ret_type, ir_CONST_FC_FUNC(sym->addr), num_args, args); - } else { - ref = ir_CALL_N(ret_type, ir_CONST_FUNC(sym->addr), num_args, args); - } + ref = ir_CALL_N(ret_type, func_ref, num_args, args); } else { ZEND_ASSERT(!type->func.args); - if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { - ref = ir_CALL(ret_type, ir_CONST_FC_FUNC(sym->addr)); - } else { - ref = ir_CALL(ret_type, ir_CONST_FUNC(sym->addr)); - } + ref = ir_CALL(ret_type, func_ref); } if (RETURN_VALUE_USED(opline)) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index e458c0702db9b..087a417034e59 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4126,7 +4126,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par uint32_t frame_flags = 0; #ifdef HAVE_FFI zend_jit_ffi_info *ffi_info = NULL; - zend_ffi_symbol *frame_ffi_sym = NULL; + zend_ffi_type *frame_ffi_func_type = NULL; + ir_ref frame_ffi_func_ref = IR_UNUSED; #endif JIT_G(current_trace) = trace_buffer; @@ -4463,7 +4464,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_flags = 0; #ifdef HAVE_FFI - frame_ffi_sym = NULL; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; #endif switch (opline->opcode) { @@ -5711,7 +5713,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (JIT_G(current_frame) && JIT_G(current_frame)->call && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { - if (!zend_jit_ffi_do_call_sym(&ctx, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) { + if (!zend_jit_ffi_do_call(&ctx, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) { goto jit_failure; } goto done; @@ -6807,17 +6809,20 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (sym && sym->kind == ZEND_FFI_SYM_FUNC && zend_jit_ffi_supported_func(ZEND_FFI_TYPE(sym->type))) { + ir_ref ffi_func_ref; + if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } if (!zend_jit_ffi_init_call_sym(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, sym, - op1_ffi_symbols, ffi_info)) { + op1_ffi_symbols, ffi_info, &ffi_func_ref)) { goto jit_failure; } frame_flags = TRACE_FRAME_MASK_FFI; - frame_ffi_sym = sym; + frame_ffi_func_type = ZEND_FFI_TYPE(sym->type); + frame_ffi_func_ref = ffi_func_ref; goto done; } } @@ -6850,12 +6855,41 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; case ZEND_INIT_DYNAMIC_CALL: +#ifdef HAVE_FFI + if (orig_op2_type != IS_OBJECT + || (op2_ce != zend_ce_closure && !op2_ffi_type)) { + break; + } +#else if (orig_op2_type != IS_OBJECT || op2_ce != zend_ce_closure) { break; } +#endif op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); frame_flags = TRACE_FRAME_MASK_NESTED; +#ifdef HAVE_FFI + if (op2_ffi_type + && op2_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(op2_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC + && zend_jit_ffi_supported_func(ZEND_FFI_TYPE(op2_ffi_type->pointer.type))) { + ir_ref ffi_func_ref; + + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_init_call_obj(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, + op2_info, OP2_REG_ADDR(), + op2_ffi_type, ffi_info, &ffi_func_ref)) { + goto jit_failure; + } + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_func_type = ZEND_FFI_TYPE(op2_ffi_type->pointer.type); + frame_ffi_func_ref = ffi_func_ref; + goto done; + } +#endif if (!zend_jit_init_closure_call(&ctx, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, p + 1, peek_checked_stack - checked_stack)) { goto jit_failure; } @@ -7453,8 +7487,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par TRACE_FRAME_INIT(call, p->func, frame_flags, num_args); #ifdef HAVE_FFI if (TRACE_FRAME_FFI(call)) { - ZEND_ASSERT(frame_ffi_sym != NULL); - call->call_opline = (const zend_op*)(void*)frame_ffi_sym; + ZEND_ASSERT(frame_ffi_func_type != NULL); + call->call_opline = (const zend_op*)(void*)frame_ffi_func_type; + call->ce = (zend_class_entry*)(intptr_t)frame_ffi_func_ref; } #endif call->prev = frame->call; From 23158ff9adf81510da5644189ac8c6a530623c0e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 5 Aug 2024 18:23:28 +0300 Subject: [PATCH 058/101] JIT/FFI cleanup arguments --- ext/opcache/jit/zend_jit.h | 3 + ext/opcache/jit/zend_jit_ir_ffi.c | 94 +++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index 0ce6c1a4409a2..1ad4e8927e176 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -182,6 +182,9 @@ void zend_jit_restart(void); #define ZREG_ZVAL_ADDREF (4<<4) #define ZREG_THIS (5<<4) +#define ZREG_FFI_PTR_LOAD (1<<3) +#define ZREG_FFI_ZVAL_DTOR (1<<4) + #define ZREG_NONE -1 #endif /* HAVE_JIT_H */ diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 3ad87e7136e16..f3a522328abda 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -93,10 +93,7 @@ static int zend_jit_ffi_init_call_obj(zend_jit_ctx *jit, return 0; } - *ffi_func_ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, obj_ref)); - if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { - *ffi_func_ref = ir_CAST_FC_FUNC(*ffi_func_ref); - } + *ffi_func_ref = obj_ref; return 1; } @@ -115,6 +112,8 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, zend_jit_trace_stack *stack = call->stack; zend_ffi_type *type = (zend_ffi_type*)(void*)call->call_opline; ir_ref ref = IR_UNUSED; + uint8_t arg_type = IS_UNDEF; + uint8_t arg_flags = 0; ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); if (type->attr & ZEND_FFI_ATTR_VARIADIC) { @@ -233,16 +232,28 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, case ZEND_FFI_TYPE_POINTER: if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); + arg_type = IS_STRING; + ref = jit_Z_PTR(jit, op1_addr); + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } } else if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type)) { - ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); - ref = ir_LOAD_A(ref); // TODO: is this always necessary ??? + arg_type = IS_OBJECT; + ref = jit_Z_PTR(jit, op1_addr); + arg_flags |= ZREG_FFI_PTR_LOAD; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } } else if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type)) { - ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, op1_addr)); + arg_type = IS_OBJECT; + ref = jit_Z_PTR(jit, op1_addr); + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } } else { ZEND_UNREACHABLE(); } @@ -264,13 +275,18 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, } else if (op1_info == MAY_BE_DOUBLE) { ref = jit_Z_DVAL(jit, op1_addr); } else if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) { - ref = ir_ADD_OFFSET(jit_Z_PTR(jit, op1_addr), offsetof(zend_string, val)); + arg_type = IS_STRING; + ref = jit_Z_PTR(jit, op1_addr); + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } } else { ZEND_UNREACHABLE(); } } - SET_STACK_REF(stack, opline->op2.num - 1, ref); + SET_STACK_TYPE(stack, opline->op2.num - 1, arg_type, 0); + SET_STACK_REF_EX(stack, opline->op2.num - 1, ref, arg_flags); return 1; } @@ -340,13 +356,31 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, ZEND_UNREACHABLE(); } + if (!IR_IS_CONST_REF(func_ref)) { + func_ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, func_ref)); + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { + func_ref = ir_CAST_FC_FUNC(func_ref); + } + } + num_args = TRACE_FRAME_NUM_ARGS(call); if (num_args) { ir_ref *args = alloca(sizeof(ir_ref) * num_args); zend_jit_trace_stack *stack = call->stack; for (i = 0; i < num_args; i++) { - args[i] = STACK_REF(stack, i); + uint8_t type = STACK_TYPE(stack, i); + ir_ref ref = STACK_REF(stack, i); + + if (type == IS_STRING) { + ref = ir_ADD_OFFSET(ref, offsetof(zend_string, val)); + } else if (type == IS_OBJECT) { + ref = jit_FFI_CDATA_PTR(jit, ref); + if (STACK_FLAGS(stack, i) & ZREG_FFI_PTR_LOAD) { + ref = ir_LOAD_A(ref); + } + } + args[i] = ref; } ref = ir_CALL_N(ret_type, func_ref, num_args, args); } else { @@ -443,6 +477,44 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } } + if (num_args) { + zend_jit_trace_stack *stack = call->stack; + + for (i = 0; i < num_args; i++) { + if (STACK_FLAGS(stack, i) & ZREG_FFI_ZVAL_DTOR) { + uint8_t type = STACK_TYPE(stack, i); + ir_ref ref = STACK_REF(stack, i); + + if (type == IS_STRING) { + ir_ref if_interned = ir_IF(ir_AND_U32( + ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_refcounted, gc.u.type_info))), + ir_CONST_U32(IS_STR_INTERNED))); + ir_IF_FALSE(if_interned); + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, ref)); + ir_IF_FALSE(if_not_zero); + jit_ZVAL_DTOR(jit, ref, MAY_BE_STRING, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); + ir_MERGE_WITH_EMPTY_TRUE(if_interned); + } else if (type == IS_OBJECT) { + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, ref)); + ir_IF_FALSE(if_not_zero); + jit_ZVAL_DTOR(jit, ref, MAY_BE_OBJECT, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ + } else { + ZEND_ASSERT(0); + } + } + } + } + + func_ref = (intptr_t)(void*)call->ce; + if (!IR_IS_CONST_REF(func_ref)) { + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, func_ref)); + ir_IF_FALSE(if_not_zero); + jit_ZVAL_DTOR(jit, func_ref, MAY_BE_OBJECT, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ + } + return 1; } From f0f1a2263c08e1e837f79af4944a5a78be2c16b3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 5 Aug 2024 18:35:26 +0300 Subject: [PATCH 059/101] FFI/JIT better support for enums --- ext/opcache/jit/zend_jit_ir_ffi.c | 45 ++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index f3a522328abda..46a16fe775c84 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -126,8 +126,12 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, if (opline->op2.num - 1 < zend_hash_num_elements(type->func.args)) { type = zend_hash_index_find_ptr(type->func.args, opline->op2.num - 1); type = ZEND_FFI_TYPE(type); + zend_ffi_type_kind type_kind = type->kind; - switch (type->kind) { + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = type->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_FLOAT: if (op1_info == MAY_BE_LONG) { ref = ir_INT2F((jit_Z_LVAL(jit, op1_addr))); @@ -304,10 +308,15 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, uint32_t i, num_args; ir_type ret_type = IR_VOID; ir_ref ref = IR_UNUSED; + zend_ffi_type_kind type_kind; ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); - switch (ZEND_FFI_TYPE(type->func.ret_type)->kind) { + type_kind = ZEND_FFI_TYPE(type->func.ret_type)->kind; + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = ZEND_FFI_TYPE(type->func.ret_type)->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_VOID: ret_type = IR_VOID; break; @@ -392,7 +401,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, zend_ffi_type *ret_type = ZEND_FFI_TYPE(type->func.ret_type); uint32_t res_type = IS_UNDEF; - switch (ret_type->kind) { + type_kind = ret_type->kind; + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = ret_type->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_VOID: res_type = IS_NULL; break; @@ -589,8 +602,12 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, zend_jit_addr res_addr) { uint32_t res_type; + zend_ffi_type_kind type_kind = ffi_type->kind; - switch (ffi_type->kind) { + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = ffi_type->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_FLOAT: jit_set_Z_DVAL(jit, res_addr, ir_F2D(ir_LOAD_F(ptr))); res_type = IS_DOUBLE; @@ -850,8 +867,12 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, zend_jit_addr res_addr) { ir_ref ref = IR_UNUSED; + zend_ffi_type_kind type_kind = ffi_type->kind; - switch (ffi_type->kind) { + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = ffi_type->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_FLOAT: if (val_info == MAY_BE_LONG) { ref = ir_INT2F(jit_Z_LVAL(jit, val_addr)); @@ -1208,7 +1229,12 @@ static int zend_jit_ffi_assign_op_helper(zend_jit_ctx *jit, return 0; } - switch (el_type->kind) { + zend_ffi_type_kind type_kind = el_type->kind; + + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = el_type->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_FLOAT: ZEND_ASSERT(op == IR_ADD || op == IR_SUB || op == IR_MUL); type = IR_FLOAT; @@ -1818,7 +1844,12 @@ static int zend_jit_ffi_incdec_helper(zend_jit_ctx *jit, return 0; } - switch (el_type->kind) { + zend_ffi_type_kind type_kind = el_type->kind; + + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = el_type->enumeration.kind; + } + switch (type_kind) { case ZEND_FFI_TYPE_FLOAT: type = IR_FLOAT; op2 = ir_CONST_FLOAT(1.0); From fa52d0a33af8828a67934899ca8dc5a75961ae0e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 6 Aug 2024 10:26:12 +0300 Subject: [PATCH 060/101] JIT/FFI add tet for closure call --- ext/ffi/tests/jit/043_call_callback.phpt | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 ext/ffi/tests/jit/043_call_callback.phpt diff --git a/ext/ffi/tests/jit/043_call_callback.phpt b/ext/ffi/tests/jit/043_call_callback.phpt new file mode 100644 index 0000000000000..55380a24cf206 --- /dev/null +++ b/ext/ffi/tests/jit/043_call_callback.phpt @@ -0,0 +1,43 @@ +--TEST-- +FFI/JIT 040: Function call +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--SKIPIF-- + +--FILE-- +new("T"); + $x->f = $ffi->printf; + for ($i = 0; $i < 5; $i++) { + ($x->f)("Hello %s\n", $name); + } +} +test("FFI"); +?> +--EXPECT-- +Hello FFI +Hello FFI +Hello FFI +Hello FFI +Hello FFI From 842202f07e77f568dc6594c1ffa369268873f651 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 6 Aug 2024 13:27:29 +0300 Subject: [PATCH 061/101] JIT/FFI print FFI types in JIT debug traces --- Zend/zend.c | 1 + Zend/zend.h | 2 +- ext/ffi/ffi.c | 12 ++++++++++++ ext/opcache/jit/zend_jit_trace.c | 12 +++++++++--- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 84a346fc97431..e35dee56ccf5c 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -105,6 +105,7 @@ ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *cont ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context) = NULL; ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str) = NULL; ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope) = NULL; +ZEND_API void (*zend_ffi_type_print)(FILE *f, const zend_ffi_type *type) = NULL; /* This callback must be signal handler safe! */ void (*zend_on_timeout)(int seconds); diff --git a/Zend/zend.h b/Zend/zend.h index 980a8619f9b14..6bbe86d7467c3 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -421,7 +421,7 @@ ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, voi ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context); ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str); ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope); - +ZEND_API extern void (*zend_ffi_type_print)(FILE *f, const zend_ffi_type *type); /* If DTrace is available and enabled */ extern ZEND_API bool zend_dtrace_enabled; diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 4a31b60a162f7..6afa6a78b6ea4 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -5314,6 +5314,17 @@ static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */ } /* }}} */ +static void _zend_ffi_type_print(FILE *f, const zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_ctype_name_buf buf; + + buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4); + if (!zend_ffi_ctype_name(&buf, ZEND_FFI_TYPE(type))) { + } else { + fwrite(buf.start, buf.end - buf.start, 1, f); + } +} + ZEND_INI_BEGIN() ZEND_INI_ENTRY_EX("ffi.enable", "preload", ZEND_INI_SYSTEM, OnUpdateFFIEnable, zend_ffi_enable_displayer_cb) STD_ZEND_INI_ENTRY("ffi.preload", NULL, ZEND_INI_SYSTEM, OnUpdateString, preload, zend_ffi_globals, ffi_globals) @@ -5597,6 +5608,7 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc; zend_ffi_cdata_create = _zend_ffi_cdata_create; + zend_ffi_type_print = _zend_ffi_type_print; if (FFI_G(preload)) { return zend_ffi_preload(FFI_G(preload)); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 087a417034e59..b86c250111fec 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -8331,9 +8331,11 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa p++; #ifdef HAVE_FFI if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { - fprintf(stderr, " op1(%sobject of class %s: ffi_type)", ref, + fprintf(stderr, " op1(%sobject of class %s: ", ref, ZSTR_VAL(p->ce->name)); p++; + zend_ffi_type_print(stderr, p->ptr); + fprintf(stderr, ")"); } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { fprintf(stderr, " op1(%sobject of class %s: ffi_symbols)", ref, ZSTR_VAL(p->ce->name)); @@ -8355,9 +8357,11 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa p++; #ifdef HAVE_FFI if ((p+1)->op == ZEND_JIT_TRACE_OP2_FFI_TYPE) { - fprintf(stderr, " op2(%sobject of class %s: ffi_type)", ref, + fprintf(stderr, " op2(%sobject of class %s: ", ref, ZSTR_VAL(p->ce->name)); p++; + zend_ffi_type_print(stderr, p->ptr); + fprintf(stderr, ")"); } else #endif fprintf(stderr, " op2(%sobject of class %s)", ref, @@ -8375,9 +8379,11 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa p++; #ifdef HAVE_FFI if ((p+1)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { - fprintf(stderr, " op3(%sobject of class %s: ffi_type)", ref, + fprintf(stderr, " op3(%sobject of class %s: ", ref, ZSTR_VAL(p->ce->name)); p++; + zend_ffi_type_print(stderr, p->ptr); + fprintf(stderr, ")"); } else #endif fprintf(stderr, " op3(%sobject of class %s)", ref, From 79285ca72f449a625ade5caad570327abc8fd5ea Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 6 Aug 2024 15:50:12 +0300 Subject: [PATCH 062/101] JIT/FFI support for temporary POINTER types (e.g. created by FFI::addr) --- ext/ffi/ffi.c | 3 -- ext/ffi/php_ffi.h | 3 ++ ext/opcache/jit/zend_jit_ir_ffi.c | 17 ++++++++++ ext/opcache/jit/zend_jit_trace.c | 21 +++++++++++-- ext/opcache/jit/zend_jit_vm_helpers.c | 45 +++++++++++++++++++++++---- 5 files changed, 77 insertions(+), 12 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 6afa6a78b6ea4..c00ae76a67004 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -64,9 +64,6 @@ static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"}; #include "ffi_arginfo.h" -#define ZEND_FFI_TYPE_MAKE_OWNED(t) \ - ((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED)) - #define ZEND_FFI_SIZEOF_ARG \ MAX(FFI_SIZEOF_ARG, sizeof(double)) diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 85131056a0baf..f6da1ef9743fa 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -410,6 +410,9 @@ typedef struct _zend_ffi { #define ZEND_FFI_TYPE_IS_OWNED(t) \ (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED) +#define ZEND_FFI_TYPE_MAKE_OWNED(t) \ + ((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED)) + PHP_FFI_API bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); #endif /* PHP_FFI_H */ diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 46a16fe775c84..d35e3ced36b79 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -16,6 +16,17 @@ * +----------------------------------------------------------------------+ */ + +static zend_ffi_type *zend_jit_ffi_type_pointer_to(const zend_ffi_type *type, zend_ffi_type *holder) +{ + holder->kind = ZEND_FFI_TYPE_POINTER; + holder->attr = 0; + holder->size = sizeof(void*); + holder->align = _Alignof(void*); + holder->pointer.type = ZEND_FFI_TYPE(type); + return holder; +} + static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) { return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); @@ -114,6 +125,12 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, ir_ref ref = IR_UNUSED; uint8_t arg_type = IS_UNDEF; uint8_t arg_flags = 0; + zend_ffi_type type_holder; + + if (ZEND_FFI_TYPE_IS_OWNED(op1_ffi_type)) { + /* OWNED flag means POINTER TO */ + op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &type_holder); + } ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); if (type->attr & ZEND_FFI_ATTR_VARIADIC) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index b86c250111fec..779a4780090f6 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -8334,7 +8334,12 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa fprintf(stderr, " op1(%sobject of class %s: ", ref, ZSTR_VAL(p->ce->name)); p++; - zend_ffi_type_print(stderr, p->ptr); + if (ZEND_FFI_TYPE_IS_OWNED(p->ptr)) { + zend_ffi_type holder; + zend_ffi_type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); + } else { + zend_ffi_type_print(stderr, p->ptr); + } fprintf(stderr, ")"); } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { fprintf(stderr, " op1(%sobject of class %s: ffi_symbols)", ref, @@ -8360,7 +8365,12 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa fprintf(stderr, " op2(%sobject of class %s: ", ref, ZSTR_VAL(p->ce->name)); p++; - zend_ffi_type_print(stderr, p->ptr); + if (ZEND_FFI_TYPE_IS_OWNED(p->ptr)) { + zend_ffi_type holder; + zend_ffi_type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); + } else { + zend_ffi_type_print(stderr, p->ptr); + } fprintf(stderr, ")"); } else #endif @@ -8382,7 +8392,12 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa fprintf(stderr, " op3(%sobject of class %s: ", ref, ZSTR_VAL(p->ce->name)); p++; - zend_ffi_type_print(stderr, p->ptr); + if (ZEND_FFI_TYPE_IS_OWNED(p->ptr)) { + zend_ffi_type holder; + zend_ffi_type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); + } else { + zend_ffi_type_print(stderr, p->ptr); + } fprintf(stderr, ")"); } else #endif diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 5791adb630f57..9109f40fe91a0 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -718,11 +718,22 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #ifdef HAVE_FFI if (ce1 == zend_ffi_cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); - if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { - zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); + zend_ffi_type *ffi_type = cdata->type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { op1_ffi_type = ffi_type; } + } else { + ffi_type = ZEND_FFI_TYPE(ffi_type); + if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + ffi_type = ffi_type->pointer.type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { + /* OWNED flag means POINTER TO */ + op1_ffi_type = ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); + } + } + } } } else if (ce1 == zend_ffi_ce) { zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(zv); @@ -783,11 +794,22 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #ifdef HAVE_FFI if (ce2 == zend_ffi_cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); - if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { - zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); + zend_ffi_type *ffi_type = cdata->type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { op2_ffi_type = ffi_type; } + } else { + ffi_type = ZEND_FFI_TYPE(ffi_type); + if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + ffi_type = ffi_type->pointer.type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { + /* OWNED flag means POINTER TO */ + op2_ffi_type = ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); + } + } + } } } #endif @@ -822,11 +844,22 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, #ifdef HAVE_FFI if (ce3 == zend_ffi_cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); - if (!ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { - zend_ffi_type *ffi_type = ZEND_FFI_TYPE(cdata->type); + zend_ffi_type *ffi_type = cdata->type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { op3_ffi_type = ffi_type; } + } else { + ffi_type = ZEND_FFI_TYPE(ffi_type); + if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + ffi_type = ffi_type->pointer.type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { + /* OWNED flag means POINTER TO */ + op3_ffi_type = ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); + } + } + } } } #endif From 469322a956f68b8251daf7d0505fc19af60f6ca0 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 6 Aug 2024 17:06:01 +0300 Subject: [PATCH 063/101] JIT/FFI better support for temporary POINTER types --- ext/opcache/jit/zend_jit.c | 10 +++++++++ ext/opcache/jit/zend_jit_ir.c | 37 +++++++++++++++++++++---------- ext/opcache/jit/zend_jit_ir_ffi.c | 17 -------------- ext/opcache/jit/zend_jit_trace.c | 10 +++++++++ 4 files changed, 45 insertions(+), 29 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 67050f830509b..c1cc4419f72ec 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -56,6 +56,16 @@ typedef struct zend_jit_ffi_info { uint32_t info; } zend_jit_ffi_info; +static zend_ffi_type *zend_jit_ffi_type_pointer_to(const zend_ffi_type *type, zend_ffi_type *holder) +{ + holder->kind = ZEND_FFI_TYPE_POINTER; + holder->attr = 0; + holder->size = sizeof(void*); + holder->align = _Alignof(void*); + holder->pointer.type = ZEND_FFI_TYPE(type); + return holder; +} + static bool zend_jit_ffi_supported_type(zend_ffi_type *type) { if (sizeof(void*) == 4) { diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 13ded51aa222f..54af7cebeba61 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -17368,13 +17368,18 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa && (trace+1)->op == ZEND_JIT_TRACE_OP1_TYPE && (trace+2)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; - if (op1_ffi_type - && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && op2_info == MAY_BE_LONG - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind < ZEND_FFI_TYPE_POINTER - && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID - && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { - return 1; + if (op1_ffi_type) { + zend_ffi_type holder; + if (ZEND_FFI_TYPE_IS_OWNED(op1_ffi_type)) { + op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &holder); + } + if ((op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && op2_info == MAY_BE_LONG + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind < ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { + return 1; + } } } #endif @@ -17431,17 +17436,25 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa zend_ffi_type *op1_ffi_type = (zend_ffi_type*)(trace+2)->ptr; zend_ffi_type *op3_ffi_type = NULL; uint32_t op1_data_info = OP1_DATA_INFO(); + zend_ffi_type holder, holder2; if ((trace+3)->op == ZEND_JIT_TRACE_OP3_TYPE && (trace+4)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { op3_ffi_type = (zend_ffi_type*)(trace+4)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op3_ffi_type)) { + op3_ffi_type = zend_jit_ffi_type_pointer_to(op3_ffi_type, &holder2); + } } - if (op1_ffi_type - && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && op2_info == MAY_BE_LONG - && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { - return 1; + if (op1_ffi_type) { + if (ZEND_FFI_TYPE_IS_OWNED(op1_ffi_type)) { + op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &holder); + } + if ((op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && op2_info == MAY_BE_LONG + && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { + return 1; + } } } #endif diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index d35e3ced36b79..46a16fe775c84 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -16,17 +16,6 @@ * +----------------------------------------------------------------------+ */ - -static zend_ffi_type *zend_jit_ffi_type_pointer_to(const zend_ffi_type *type, zend_ffi_type *holder) -{ - holder->kind = ZEND_FFI_TYPE_POINTER; - holder->attr = 0; - holder->size = sizeof(void*); - holder->align = _Alignof(void*); - holder->pointer.type = ZEND_FFI_TYPE(type); - return holder; -} - static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) { return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); @@ -125,12 +114,6 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, ir_ref ref = IR_UNUSED; uint8_t arg_type = IS_UNDEF; uint8_t arg_flags = 0; - zend_ffi_type type_holder; - - if (ZEND_FFI_TYPE_IS_OWNED(op1_ffi_type)) { - /* OWNED flag means POINTER TO */ - op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &type_holder); - } ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); if (type->attr & ZEND_FFI_ATTR_VARIADIC) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 779a4780090f6..268ef04dc2924 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4405,6 +4405,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par zend_ffi_type *op1_ffi_type = NULL; zend_ffi_type *op2_ffi_type = NULL; zend_ffi_type *op3_ffi_type = NULL; + zend_ffi_type holder1, holder2, holder3; HashTable *op1_ffi_symbols = NULL; (void)op2_ffi_type; #endif @@ -4431,6 +4432,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { op1_ffi_type = (zend_ffi_type*)(p+1)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op1_ffi_type)) { + op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &holder1); + } p++; } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { op1_ffi_symbols = (HashTable*)(p+1)->ptr; @@ -4444,6 +4448,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if ((p+1)->op == ZEND_JIT_TRACE_OP2_FFI_TYPE) { op2_ffi_type = (zend_ffi_type*)(p+1)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op2_ffi_type)) { + op2_ffi_type = zend_jit_ffi_type_pointer_to(op2_ffi_type, &holder2); + } p++; } #endif @@ -4454,6 +4461,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if ((p+1)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { op3_ffi_type = (zend_ffi_type*)(p+1)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op3_ffi_type)) { + op3_ffi_type = zend_jit_ffi_type_pointer_to(op3_ffi_type, &holder3); + } p++; } #endif From 34472abf8e6999f9c00df8310d36cc37d8d1c994 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 10 Sep 2024 10:45:57 +0300 Subject: [PATCH 064/101] Fixed support for lazy objects --- ext/opcache/jit/zend_jit_helpers.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index baba3ff9f8406..00fdf648b14b6 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3382,6 +3382,7 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, v // inlined zend_ffi_object_init() GC_SET_REFCOUNT(&cdata->std, 1); GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + cdata->std.extra_flags = 0; cdata->std.ce = zend_ffi_cdata_ce; cdata->std.handlers = zend_ffi_cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ cdata->std.properties = NULL; @@ -3407,6 +3408,7 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_obj(zval *zv, zend_ffi_type *type, v // inlined zend_ffi_object_init() GC_SET_REFCOUNT(&cdata->std, 1); GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + cdata->std.extra_flags = 0; cdata->std.ce = zend_ffi_cdata_ce; cdata->std.handlers = zend_ffi_cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ cdata->std.properties = NULL; From 9288fcfabfda4ad24c623a085f2657788684a81e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 17 Sep 2024 15:09:47 +0300 Subject: [PATCH 065/101] Fix register allocation --- ext/opcache/jit/zend_jit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index c1cc4419f72ec..039b9cd0b331e 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -112,7 +112,8 @@ static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, dst_type = ZEND_FFI_TYPE(dst_type); if (!zend_jit_ffi_supported_type(dst_type)) { return false; - } else if (src_info == MAY_BE_LONG || src_info == MAY_BE_DOUBLE) { + } else if ((src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG + || (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) { return dst_type->kind < ZEND_FFI_TYPE_POINTER && dst_type->kind != ZEND_FFI_TYPE_VOID; } else if (src_info == MAY_BE_FALSE || src_info == MAY_BE_TRUE || src_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { return dst_type->kind == ZEND_FFI_TYPE_BOOL; From 53f43c5064b5933f2d8dec04e060af239b589bd9 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 19 Sep 2024 15:36:59 +0300 Subject: [PATCH 066/101] Use signed comparison --- ext/opcache/jit/zend_jit_ir_ffi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 46a16fe775c84..c71608690a3a3 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -587,7 +587,7 @@ static int zend_jit_ffi_abc(zend_jit_ctx *jit, if (!exit_addr) { return 0; } - ir_GUARD(ir_ULT(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ffi_type->array.length)), + ir_GUARD(ir_LT(jit_Z_LVAL(jit, op2_addr), ir_CONST_LONG(ffi_type->array.length)), ir_CONST_ADDR(exit_addr)); } } From 2265c3b97cdb345b5e56454f24d447fded7a8005 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 19 Sep 2024 17:51:22 +0300 Subject: [PATCH 067/101] Improve register allocation --- ext/opcache/jit/zend_jit_ir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 54af7cebeba61..b51aca097815f 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -17374,7 +17374,7 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &holder); } if ((op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && op2_info == MAY_BE_LONG + && (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind < ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { @@ -17451,7 +17451,7 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &holder); } if ((op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) - && op2_info == MAY_BE_LONG + && (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { return 1; } From 145f654048819669e88b59f366fd79cf04ee5b27 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 20 Sep 2024 10:44:02 +0300 Subject: [PATCH 068/101] Fix tests for 32-bit systems --- ext/ffi/tests/jit/001_var_read_scalar.phpt | 2 +- ext/ffi/tests/jit/006_var_write_scalar.phpt | 2 +- ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt | 6 +++--- ext/ffi/tests/jit/010_var_modify_scalar.phpt | 2 +- ext/ffi/tests/jit/012_var_write_scalar_ret.phpt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/ffi/tests/jit/001_var_read_scalar.phpt b/ext/ffi/tests/jit/001_var_read_scalar.phpt index 750514fcd9ea7..c7b61f61b83b7 100644 --- a/ext/ffi/tests/jit/001_var_read_scalar.phpt +++ b/ext/ffi/tests/jit/001_var_read_scalar.phpt @@ -22,7 +22,7 @@ try { stdout; diff --git a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt index a2c3a86f06183..b4cfe4a3d7e2d 100644 --- a/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt +++ b/ext/ffi/tests/jit/007_var_write_scalar_cdata.phpt @@ -22,11 +22,11 @@ try { stdout; - $x = $ffi->cast('int64_t', 42); + $x = $ffi->cast('intptr_t', 42); var_dump($x); for ($i = 0; $i < 5; $i++) { $ffi->stdout = $x; @@ -38,7 +38,7 @@ function test() { test(); ?> --EXPECTF-- -object(FFI\CData:int64_t)#%d (1) { +object(FFI\CData:int%d_t)#%d (1) { ["cdata"]=> int(42) } diff --git a/ext/ffi/tests/jit/010_var_modify_scalar.phpt b/ext/ffi/tests/jit/010_var_modify_scalar.phpt index 60116f1945a9e..26802fb1ab068 100644 --- a/ext/ffi/tests/jit/010_var_modify_scalar.phpt +++ b/ext/ffi/tests/jit/010_var_modify_scalar.phpt @@ -22,7 +22,7 @@ try { stdout; diff --git a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt index 32196a3ef1538..923cd5b7b09fd 100644 --- a/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt +++ b/ext/ffi/tests/jit/012_var_write_scalar_ret.phpt @@ -22,7 +22,7 @@ try { stdout; From 96581d123e0ddf02bb871395a49871524324333e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 26 Sep 2024 17:14:28 +0300 Subject: [PATCH 069/101] Redesign JIT/FFI API --- Zend/zend.c | 10 +---- Zend/zend.h | 15 +------ ext/ffi/ffi.c | 64 ++++++++++++++++----------- ext/ffi/php_ffi.h | 14 +++++- ext/opcache/ZendAccelerator.c | 20 +++++---- ext/opcache/jit/zend_jit_helpers.c | 8 ++-- ext/opcache/jit/zend_jit_ir_ffi.c | 24 +++++----- ext/opcache/jit/zend_jit_trace.c | 12 ++--- ext/opcache/jit/zend_jit_vm_helpers.c | 8 ++-- 9 files changed, 92 insertions(+), 83 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index e35dee56ccf5c..b7e94dce51f93 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -98,14 +98,8 @@ ZEND_API void (*zend_accel_schedule_restart_hook)(int reason) = NULL; ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size) = NULL; ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL; -ZEND_API zend_class_entry *zend_ffi_ce = NULL; -ZEND_API zend_class_entry *zend_ffi_cdata_ce = NULL; -ZEND_API zend_ffi_cdata* (*zend_ffi_cdata_create)(void *ptr, zend_ffi_type *type) = NULL; -ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *context) = NULL; -ZEND_API zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context) = NULL; -ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str) = NULL; -ZEND_API zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope) = NULL; -ZEND_API void (*zend_ffi_type_print)(FILE *f, const zend_ffi_type *type) = NULL; +/* FFI/OPCache interopability API */ +ZEND_API struct _zend_ffi_api *zend_ffi_api = NULL; /* This callback must be signal handler safe! */ void (*zend_on_timeout)(int seconds); diff --git a/Zend/zend.h b/Zend/zend.h index 6bbe86d7467c3..3efd723e86151 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -408,20 +408,7 @@ extern ZEND_API zend_class_entry *zend_standard_class_def; extern ZEND_API zend_utility_values zend_uv; /* FFI/OPCache interopability API */ -extern ZEND_API zend_class_entry *zend_ffi_ce; -extern ZEND_API zend_class_entry *zend_ffi_cdata_ce; - -typedef struct _zend_ffi_dcl zend_ffi_dcl; -typedef struct _zend_ffi_scope zend_ffi_scope; -typedef struct _zend_ffi_cdata zend_ffi_cdata; -typedef struct _zend_ffi_type zend_ffi_type; - -ZEND_API extern zend_ffi_cdata* (*zend_ffi_cdata_create)(void *ptr, zend_ffi_type *type); -ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_get)(zend_string *str, void *context); -ZEND_API extern zend_ffi_dcl* (*zend_ffi_cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context); -ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_get)(zend_string *str); -ZEND_API extern zend_ffi_scope* (*zend_ffi_cache_scope_add)(zend_string *str, zend_ffi_scope *scope); -ZEND_API extern void (*zend_ffi_type_print)(FILE *f, const zend_ffi_type *type); +extern ZEND_API struct _zend_ffi_api *zend_ffi_api; /* If DTrace is available and enabled */ extern ZEND_API bool zend_dtrace_enabled; diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index c00ae76a67004..abba4b3f7bc1a 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -67,8 +67,12 @@ static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"}; #define ZEND_FFI_SIZEOF_ARG \ MAX(FFI_SIZEOF_ARG, sizeof(double)) +static struct _zend_ffi_api ffi_api; + static zend_class_entry *zend_ffi_exception_ce; static zend_class_entry *zend_ffi_parser_exception_ce; +static zend_class_entry *zend_ffi_ce; +static zend_class_entry *zend_ffi_cdata_ce; static zend_class_entry *zend_ffi_ctype_ce; static zend_object_handlers zend_ffi_handlers; @@ -91,7 +95,7 @@ static ZEND_FUNCTION(ffi_trampoline); static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type); static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type); -//???static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); +static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); #if FFI_CLOSURES static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value); @@ -177,7 +181,7 @@ static bool zend_ffi_func_ptr_are_compatible(zend_ffi_type *dst_type, zend_ffi_t } /* }}} */ -PHP_FFI_API bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */ +static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */ { while (1) { if (dst_type == src_type) { @@ -352,7 +356,7 @@ static ffi_type *zend_ffi_get_type(zend_ffi_type *type) /* {{{ */ } /* }}} */ -static zend_ffi_cdata* _zend_ffi_cdata_create(void *ptr, zend_ffi_type *type) /* {{{ */ +static zend_ffi_cdata* zend_ffi_cdata_create(void *ptr, zend_ffi_type *type) /* {{{ */ { zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); @@ -2935,8 +2939,8 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ FFI_G(tags) = NULL; if (code && ZSTR_LEN(code)) { - if (zend_ffi_cache_scope_get) { - zend_ffi_scope *scope = zend_ffi_cache_scope_get(code); + if (ffi_api.cache_scope_get) { + zend_ffi_scope *scope = ffi_api.cache_scope_get(code); if (scope) { ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); ffi->lib = handle; @@ -2990,12 +2994,12 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ } ZEND_HASH_FOREACH_END(); } - if (zend_ffi_cache_scope_add) { + if (ffi_api.cache_scope_add) { zend_ffi_scope scope, *cached_scope; scope.symbols = FFI_G(symbols); scope.tags = FFI_G(tags); - cached_scope = zend_ffi_cache_scope_add(code, &scope); + cached_scope = ffi_api.cache_scope_add(code, &scope); if (cached_scope) { if (FFI_G(symbols)) { zend_hash_destroy(FFI_G(symbols)); @@ -3262,8 +3266,8 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ close(fd); ZSTR_VAL(code)[code_size] = 0; - if (!preload && zend_ffi_cache_scope_get) { - zend_ffi_scope *scope = zend_ffi_cache_scope_get(code); + if (!preload && ffi_api.cache_scope_get) { + zend_ffi_scope *scope = ffi_api.cache_scope_get(code); if (scope) { ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); ffi->lib = handle; @@ -3473,12 +3477,12 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ ffi->tags = scope->tags; ffi->persistent = 1; } else { - if (zend_ffi_cache_scope_add) { + if (ffi_api.cache_scope_add) { zend_ffi_scope scope, *cached_scope; scope.symbols = FFI_G(symbols); scope.tags = FFI_G(tags); - cached_scope = zend_ffi_cache_scope_add(code, &scope); + cached_scope = ffi_api.cache_scope_add(code, &scope); if (cached_scope) { if (FFI_G(symbols)) { zend_hash_destroy(FFI_G(symbols)); @@ -3802,8 +3806,8 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(default_type_attr) = 0; - if (zend_ffi_cache_type_get - && (cached_dcl = zend_ffi_cache_type_get(type_def, FFI_G(symbols)))) { + if (ffi_api.cache_type_get + && (cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)))) { memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); @@ -3823,8 +3827,8 @@ ZEND_METHOD(FFI, new) /* {{{ */ zend_ffi_tags_cleanup(&dcl); } - if (zend_ffi_cache_type_add) { - cached_dcl = zend_ffi_cache_type_add(type_def, &dcl, FFI_G(symbols)); + if (zend_ffi_api->cache_type_add) { + cached_dcl = zend_ffi_api->cache_type_add(type_def, &dcl, FFI_G(symbols)); if (cached_dcl) { if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { _zend_ffi_type_dtor(dcl.type); @@ -3967,8 +3971,8 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(default_type_attr) = 0; - if (zend_ffi_cache_type_get - && (cached_dcl = zend_ffi_cache_type_get(type_def, FFI_G(symbols)))) { + if (ffi_api.cache_type_get + && (cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)))) { memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); @@ -3988,8 +3992,8 @@ ZEND_METHOD(FFI, cast) /* {{{ */ zend_ffi_tags_cleanup(&dcl); } - if (zend_ffi_cache_type_add) { - cached_dcl = zend_ffi_cache_type_add(type_def, &dcl, FFI_G(symbols)); + if (ffi_api.cache_type_add) { + cached_dcl = ffi_api.cache_type_add(type_def, &dcl, FFI_G(symbols)); if (cached_dcl) { if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { _zend_ffi_type_dtor(dcl.type); @@ -4154,8 +4158,8 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(default_type_attr) = 0; - if (zend_ffi_cache_type_get - && (cached_dcl = zend_ffi_cache_type_get(type_def, FFI_G(symbols)))) { + if (ffi_api.cache_type_get + && (cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)))) { memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); @@ -4175,8 +4179,8 @@ ZEND_METHOD(FFI, type) /* {{{ */ zend_ffi_tags_cleanup(&dcl); } - if (zend_ffi_cache_type_add) { - cached_dcl = zend_ffi_cache_type_add(type_def, &dcl, FFI_G(symbols)); + if (ffi_api.cache_type_add) { + cached_dcl = ffi_api.cache_type_add(type_def, &dcl, FFI_G(symbols)); if (cached_dcl) { if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { _zend_ffi_type_dtor(dcl.type); @@ -5311,7 +5315,7 @@ static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */ } /* }}} */ -static void _zend_ffi_type_print(FILE *f, const zend_ffi_type *type) /* {{{ */ +static void zend_ffi_type_print(FILE *f, const zend_ffi_type *type) /* {{{ */ { zend_ffi_ctype_name_buf buf; @@ -5604,8 +5608,16 @@ ZEND_MINIT_FUNCTION(ffi) zend_ffi_ctype_handlers.get_properties = zend_fake_get_properties; zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc; - zend_ffi_cdata_create = _zend_ffi_cdata_create; - zend_ffi_type_print = _zend_ffi_type_print; + memset(&ffi_api, 0, sizeof(ffi_api)); + + ffi_api.scope_ce = zend_ffi_ce; + ffi_api.cdata_ce = zend_ffi_cdata_ce; + + ffi_api.cdata_create = zend_ffi_cdata_create; + ffi_api.type_print = zend_ffi_type_print; + ffi_api.is_compatible_type = zend_ffi_is_compatible_type; + + zend_ffi_api = &ffi_api; if (FFI_G(preload)) { return zend_ffi_preload(FFI_G(preload)); diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index f6da1ef9743fa..ee1f95c3c338b 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -413,6 +413,18 @@ typedef struct _zend_ffi { #define ZEND_FFI_TYPE_MAKE_OWNED(t) \ ((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED)) -PHP_FFI_API bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type); +struct _zend_ffi_api { + zend_class_entry *scope_ce; + zend_class_entry *cdata_ce; + + zend_ffi_cdata* (*cdata_create)(void *ptr, zend_ffi_type *type); + void (*type_print)(FILE *f, const zend_ffi_type *type); + bool (*is_compatible_type)(zend_ffi_type *dst_type, zend_ffi_type *src_type); + + zend_ffi_dcl* (*cache_type_get)(zend_string *str, void *context); + zend_ffi_dcl* (*cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context); + zend_ffi_scope* (*cache_scope_get)(zend_string *str); + zend_ffi_scope* (*cache_scope_add)(zend_string *str, zend_ffi_scope *scope); +}; #endif /* PHP_FFI_H */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index e2aa5bba491d8..3269bd7b56858 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3860,10 +3860,12 @@ static zend_result accel_post_startup(void) zend_inheritance_cache_add = zend_accel_inheritance_cache_add; #if HAVE_FFI - zend_ffi_cache_type_get = accel_ffi_cache_type_get; - zend_ffi_cache_type_add = accel_ffi_cache_type_add; - zend_ffi_cache_scope_get = accel_ffi_cache_scope_get; - zend_ffi_cache_scope_add = accel_ffi_cache_scope_add; + if (zend_ffi_api) { + zend_ffi_api->cache_type_get = accel_ffi_cache_type_get; + zend_ffi_api->cache_type_add = accel_ffi_cache_type_add; + zend_ffi_api->cache_scope_get = accel_ffi_cache_scope_get; + zend_ffi_api->cache_scope_add = accel_ffi_cache_scope_add; + } #endif } @@ -3920,10 +3922,12 @@ void accel_shutdown(void) zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add; #if HAVE_FFI - zend_ffi_cache_type_get = NULL; - zend_ffi_cache_type_add = NULL; - zend_ffi_cache_scope_get = NULL; - zend_ffi_cache_scope_add = NULL; + if (zend_ffi_api) { + zend_ffi_api->cache_type_get = NULL; + zend_ffi_api->cache_type_add = NULL; + zend_ffi_api->cache_scope_get = NULL; + zend_ffi_api->cache_scope_add = NULL; + } #endif if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) { diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 00fdf648b14b6..0750dd1ec722d 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3383,8 +3383,8 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, v GC_SET_REFCOUNT(&cdata->std, 1); GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); cdata->std.extra_flags = 0; - cdata->std.ce = zend_ffi_cdata_ce; - cdata->std.handlers = zend_ffi_cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + cdata->std.ce = zend_ffi_api->cdata_ce; + cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ cdata->std.properties = NULL; zend_objects_store_put(&cdata->std); cdata->type = type; @@ -3409,8 +3409,8 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_obj(zval *zv, zend_ffi_type *type, v GC_SET_REFCOUNT(&cdata->std, 1); GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); cdata->std.extra_flags = 0; - cdata->std.ce = zend_ffi_cdata_ce; - cdata->std.handlers = zend_ffi_cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + cdata->std.ce = zend_ffi_api->cdata_ce; + cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ cdata->std.properties = NULL; zend_objects_store_put(&cdata->std); cdata->type = type; diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index c71608690a3a3..12ce62f98df68 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -708,16 +708,16 @@ static int zend_jit_ffi_guard(zend_jit_ctx *jit, { if (ssa->var_info && use >= 0 - && ssa->var_info[use].ce != zend_ffi_cdata_ce) { - if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_cdata_ce)) { + && ssa->var_info[use].ce != zend_ffi_api->cdata_ce) { + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_api->cdata_ce)) { return 0; } ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[use].ce = zend_ffi_cdata_ce; + ssa->var_info[use].ce = zend_ffi_api->cdata_ce; ssa->var_info[use].is_instanceof = 0; if (def >= 0) { ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[def].ce = zend_ffi_cdata_ce; + ssa->var_info[def].ce = zend_ffi_api->cdata_ce; ssa->var_info[def].is_instanceof = 0; } } @@ -753,17 +753,17 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, if (ssa->var_info && use >= 0 - && ssa->var_info[use].ce != zend_ffi_ce) { + && ssa->var_info[use].ce != zend_ffi_api->scope_ce) { ref = jit_Z_PTR(jit, addr); - if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_ce)) { + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_api->scope_ce)) { return 0; } ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[use].ce = zend_ffi_ce; + ssa->var_info[use].ce = zend_ffi_api->scope_ce; ssa->var_info[use].is_instanceof = 0; if (def >= 0) { ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[def].ce = zend_ffi_ce; + ssa->var_info[def].ce = zend_ffi_api->scope_ce; ssa->var_info[def].is_instanceof = 0; } } @@ -835,7 +835,7 @@ static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_DIM_W || opline->opcode == ZEND_FETCH_DIM_RW) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(el_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { @@ -1445,7 +1445,7 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_OBJ_W) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(field_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { @@ -1492,7 +1492,7 @@ static int zend_jit_ffi_fetch_val(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_OBJ_W) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(op1_ffi_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { @@ -1540,7 +1540,7 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, if (opline->opcode == ZEND_FETCH_OBJ_W) { jit_set_Z_PTR(jit, res_addr, - ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_cdata_create), + ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(sym_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 268ef04dc2924..2efdc1c5cc77f 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -8346,9 +8346,9 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa p++; if (ZEND_FFI_TYPE_IS_OWNED(p->ptr)) { zend_ffi_type holder; - zend_ffi_type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); + zend_ffi_api->type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); } else { - zend_ffi_type_print(stderr, p->ptr); + zend_ffi_api->type_print(stderr, p->ptr); } fprintf(stderr, ")"); } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { @@ -8377,9 +8377,9 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa p++; if (ZEND_FFI_TYPE_IS_OWNED(p->ptr)) { zend_ffi_type holder; - zend_ffi_type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); + zend_ffi_api->type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); } else { - zend_ffi_type_print(stderr, p->ptr); + zend_ffi_api->type_print(stderr, p->ptr); } fprintf(stderr, ")"); } else @@ -8404,9 +8404,9 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa p++; if (ZEND_FFI_TYPE_IS_OWNED(p->ptr)) { zend_ffi_type holder; - zend_ffi_type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); + zend_ffi_api->type_print(stderr, zend_jit_ffi_type_pointer_to(p->ptr, &holder)); } else { - zend_ffi_type_print(stderr, p->ptr); + zend_ffi_api->type_print(stderr, p->ptr); } fprintf(stderr, ")"); } else diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 9109f40fe91a0..aefb1fa07b576 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -716,7 +716,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (Z_TYPE_P(zv) == IS_OBJECT) { ce1 = Z_OBJCE_P(zv); #ifdef HAVE_FFI - if (ce1 == zend_ffi_cdata_ce) { + if (zend_ffi_api && ce1 == zend_ffi_api->cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); zend_ffi_type *ffi_type = cdata->type; if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { @@ -735,7 +735,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } } } - } else if (ce1 == zend_ffi_ce) { + } else if (zend_ffi_api && ce1 == zend_ffi_api->scope_ce) { zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(zv); if (ffi->persistent && ffi->symbols) { op1_ffi_symbols = ffi->symbols; @@ -792,7 +792,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (Z_TYPE_P(zv) == IS_OBJECT) { ce2 = Z_OBJCE_P(zv); #ifdef HAVE_FFI - if (ce2 == zend_ffi_cdata_ce) { + if (zend_ffi_api && ce2 == zend_ffi_api->cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); zend_ffi_type *ffi_type = cdata->type; if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { @@ -842,7 +842,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (Z_TYPE_P(zv) == IS_OBJECT) { ce3 = Z_OBJCE_P(zv); #ifdef HAVE_FFI - if (ce3 == zend_ffi_cdata_ce) { + if (zend_ffi_api && ce3 == zend_ffi_api->cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); zend_ffi_type *ffi_type = cdata->type; if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { From 8a7719b83ebb7ab9f270b1f9b8b3926fbf1c3722 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 26 Sep 2024 17:24:02 +0300 Subject: [PATCH 070/101] Fix few problems in passing argumets to FFI calls --- ext/opcache/jit/zend_jit_ir_ffi.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 12ce62f98df68..644bf6cd4690f 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -123,6 +123,14 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, } ZEND_ASSERT(opline->op2.num > 0 && opline->op2.num <= TRACE_FRAME_NUM_ARGS(call)); + if (op1_info & MAY_BE_REF) { + op1_addr = jit_ZVAL_DEREF(jit, op1_addr); + op1_info &= ~MAY_BE_REF; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + // TODO: dtor ??? + } + } + if (opline->op2.num - 1 < zend_hash_num_elements(type->func.args)) { type = zend_hash_index_find_ptr(type->func.args, opline->op2.num - 1); type = ZEND_FFI_TYPE(type); @@ -241,9 +249,14 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { arg_flags |= ZREG_FFI_ZVAL_DTOR; } + } else if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_NULL) { + arg_type = IS_NULL; + ref = IR_NULL; } else if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER - && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type)) { + && (ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->pointer.type) + || zend_ffi_api->is_compatible_type(ZEND_FFI_TYPE(op1_ffi_type->pointer.type), ZEND_FFI_TYPE(type->pointer.type)))) { + // TODO: guards ??? arg_type = IS_OBJECT; ref = jit_Z_PTR(jit, op1_addr); arg_flags |= ZREG_FFI_PTR_LOAD; @@ -252,7 +265,9 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, } } else if (op1_ffi_type && op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY - && ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type)) { + && (ZEND_FFI_TYPE(type->pointer.type) == ZEND_FFI_TYPE(op1_ffi_type->array.type) + || zend_ffi_api->is_compatible_type(ZEND_FFI_TYPE(op1_ffi_type->pointer.type), ZEND_FFI_TYPE(type->pointer.type)))) { + // TODO: guards ??? arg_type = IS_OBJECT; ref = jit_Z_PTR(jit, op1_addr); if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { From c21268f5b27764704b0fcca68357ac49862e1763 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 26 Sep 2024 20:33:14 +0300 Subject: [PATCH 071/101] Regenrate ext/ffi/ffi_arginfo.h --- ext/ffi/ffi_arginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ffi/ffi_arginfo.h b/ext/ffi/ffi_arginfo.h index 1e50ce70da792..563c9f8b8e8b1 100644 --- a/ext/ffi/ffi_arginfo.h +++ b/ext/ffi/ffi_arginfo.h @@ -6,7 +6,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_cdef, 0, 0, FFI, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, lib, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_load, 0, 1, FFI, 0) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_FFI_load, 0, 1, FFI, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) ZEND_END_ARG_INFO() From 1e5fad46121b64d0e82285c4e064c43fb95eb096 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 30 Sep 2024 16:41:40 +0300 Subject: [PATCH 072/101] Fix FFI caching --- ext/ffi/ffi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index abba4b3f7bc1a..9f2eb08914bbb 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -2312,12 +2312,12 @@ static void zend_ffi_free_obj(zend_object *object) /* {{{ */ ffi->lib = NULL; } - if (ffi->symbols) { + if (ffi->symbols && !(GC_FLAGS(ffi->symbols) & IS_ARRAY_IMMUTABLE)) { zend_hash_destroy(ffi->symbols); efree(ffi->symbols); } - if (ffi->tags) { + if (ffi->tags && !(GC_FLAGS(ffi->tags) & IS_ARRAY_IMMUTABLE)) { zend_hash_destroy(ffi->tags); efree(ffi->tags); } @@ -3496,7 +3496,6 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ } FFI_G(symbols) = cached_scope->symbols; FFI_G(tags) = cached_scope->tags; - persistent = true; } } From 2f7ed899be0be7485dbf84e6309b9d355b65701c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 9 Oct 2024 14:53:39 +0300 Subject: [PATCH 073/101] JIT for FFI::addr() --- ext/opcache/jit/zend_jit_helpers.c | 113 ++++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_internal.h | 3 + ext/opcache/jit/zend_jit_ir.c | 2 + ext/opcache/jit/zend_jit_ir_ffi.c | 31 +++++++- ext/opcache/jit/zend_jit_trace.c | 31 +++++++- 5 files changed, 178 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 0750dd1ec722d..28d241199a99f 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3420,4 +3420,117 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_obj(zval *zv, zend_ffi_type *type, v ZVAL_OBJ(zv, &cdata->std); } } + +static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr(zval *zv) +{ + zend_ffi_cdata *cdata, *new_cdata; + zend_ffi_type *type, *new_type; + + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + ZVAL_DEREF(zv); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + + cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); + type = ZEND_FFI_TYPE(cdata->type); + +// if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER +// && cdata->ptr == &cdata->ptr_holder) { +// zend_throw_error(zend_ffi_exception_ce, "FFI::addr() cannot create a reference to a temporary pointer"); +// return NULL; +// } + + new_type = emalloc(sizeof(zend_ffi_type)); + new_type->kind = ZEND_FFI_TYPE_POINTER; + new_type->attr = 0; + new_type->size = sizeof(void*); + new_type->align = _Alignof(void*); + /* life-time (source must relive the resulting pointer) ??? */ + new_type->pointer.type = type; + + new_cdata = emalloc(sizeof(zend_ffi_cdata)); + // inlined zend_ffi_object_init() + GC_SET_REFCOUNT(&new_cdata->std, 1); + GC_TYPE_INFO(&new_cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + new_cdata->std.extra_flags = 0; + new_cdata->std.ce = zend_ffi_api->cdata_ce; + new_cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + new_cdata->std.properties = NULL; + zend_objects_store_put(&new_cdata->std); + new_cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type); + new_cdata->flags = 0; + + new_cdata->ptr = (void*)&new_cdata->ptr_holder; + new_cdata->ptr_holder = cdata->ptr; + + return new_cdata; +} + +static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr_var(zval *zv) +{ + zend_ffi_cdata *cdata, *new_cdata; + zend_ffi_type *type, *new_type; + zval *arg = zv; + + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + } + ZVAL_DEREF(zv); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + + cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); + type = ZEND_FFI_TYPE(cdata->type); + +// if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER +// && cdata->ptr == &cdata->ptr_holder) { +// zend_throw_error(zend_ffi_exception_ce, "FFI::addr() cannot create a reference to a temporary pointer"); +// return NULL; +// } + + new_type = emalloc(sizeof(zend_ffi_type)); + new_type->kind = ZEND_FFI_TYPE_POINTER; + new_type->attr = 0; + new_type->size = sizeof(void*); + new_type->align = _Alignof(void*); + /* life-time (source must relive the resulting pointer) ??? */ + new_type->pointer.type = type; + + new_cdata = emalloc(sizeof(zend_ffi_cdata)); + // inlined zend_ffi_object_init() + GC_SET_REFCOUNT(&new_cdata->std, 1); + GC_TYPE_INFO(&new_cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + new_cdata->std.extra_flags = 0; + new_cdata->std.ce = zend_ffi_api->cdata_ce; + new_cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + new_cdata->std.properties = NULL; + zend_objects_store_put(&new_cdata->std); + new_cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type); + new_cdata->flags = 0; + + new_cdata->ptr = (void*)&new_cdata->ptr_holder; + new_cdata->ptr_holder = cdata->ptr; + + if (Z_REFCOUNTED_P(arg)) { + zend_refcounted *ref = Z_COUNTED_P(arg); + + if (!GC_DELREF(ref)) { + if (ref == (zend_refcounted*)cdata || GC_REFCOUNT(&cdata->std) == 1) { + if (ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { + /* transfer type ownership */ + cdata->type = type; + new_type->pointer.type = ZEND_FFI_TYPE_MAKE_OWNED(type); + } + if (cdata->flags & ZEND_FFI_FLAG_OWNED) { + /* transfer ownership */ + cdata->flags &= ~ZEND_FFI_FLAG_OWNED; + new_cdata->flags |= ZEND_FFI_FLAG_OWNED; + } + } + rc_dtor_func(ref); + } + } + + return new_cdata; +} #endif diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 4329baaca1410..cdd008183e399 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -575,6 +575,7 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS 0x00000400 #define TRACE_FRAME_MASK_FFI 0x00000800 +#define TRACE_FRAME_MASK_FFI_ADDR 0x00001000 #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ @@ -616,6 +617,8 @@ struct _zend_jit_trace_stack_frame { ((frame)->_info & TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS) #define TRACE_FRAME_FFI(frame) \ ((frame)->_info & TRACE_FRAME_MASK_FFI) +#define TRACE_FRAME_FFI_ADDR(frame) \ + ((frame)->_info & TRACE_FRAME_MASK_FFI_ADDR) #define TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(frame) do { \ (frame)->_info |= (0xffffu << TRACE_FRAME_SHIFT_NUM_ARGS); \ diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index b51aca097815f..df2d00a919ae0 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3104,6 +3104,8 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_zval_string); REGISTER_HELPER(zend_jit_zval_ffi_ptr); REGISTER_HELPER(zend_jit_zval_ffi_obj); + REGISTER_HELPER(zend_jit_zval_ffi_addr); + REGISTER_HELPER(zend_jit_zval_ffi_addr_var); #endif #ifndef ZTS diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 644bf6cd4690f..f7425e4345932 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -115,6 +115,24 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, uint8_t arg_type = IS_UNDEF; uint8_t arg_flags = 0; + if (!type) { + ZEND_ASSERT(TRACE_FRAME_FFI_ADDR(call)); + ZEND_ASSERT(opline->op2.num == 1); + ZEND_ASSERT(op1_ffi_type); + + if (opline->op1_type == IS_VAR) { + ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_ffi_addr_var), + jit_ZVAL_ADDR(jit, op1_addr)); + } else { + ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_ffi_addr), + jit_ZVAL_ADDR(jit, op1_addr)); + } + + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, 0); + + return 1; + } ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); if (type->attr & ZEND_FFI_ATTR_VARIADIC) { ZEND_ASSERT(TRACE_FRAME_NUM_ARGS(call) >= zend_hash_num_elements(type->func.args)); @@ -318,6 +336,7 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, zend_jit_addr res_addr) { zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; + zend_jit_trace_stack *stack = call->stack; zend_ffi_type *type = (zend_ffi_type*)(void*)call->call_opline; ir_ref func_ref = (intptr_t)(void*)call->ce; uint32_t i, num_args; @@ -325,6 +344,17 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, ir_ref ref = IR_UNUSED; zend_ffi_type_kind type_kind; + if (!type) { + ZEND_ASSERT(TRACE_FRAME_FFI_ADDR(call)); + + ref = STACK_REF(stack, 0); + + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + + return 1; + } + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); type_kind = ZEND_FFI_TYPE(type->func.ret_type)->kind; @@ -390,7 +420,6 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, num_args = TRACE_FRAME_NUM_ARGS(call); if (num_args) { ir_ref *args = alloca(sizeof(ir_ref) * num_args); - zend_jit_trace_stack *stack = call->stack; for (i = 0; i < num_args; i++) { uint8_t type = STACK_TYPE(stack, i); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 2efdc1c5cc77f..087ba05b197ef 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5601,6 +5601,17 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par break; } op1_info = OP1_INFO(); +#ifdef HAVE_FFI + if (JIT_G(current_frame) + && JIT_G(current_frame)->call + && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + if (!zend_jit_ffi_send_val(&ctx, opline, op_array, ssa, ssa_op, + op1_info, OP1_REG_ADDR(), 0, op1_ffi_type)) { + goto jit_failure; + } + goto done; + } +#endif if (!zend_jit_send_ref(&ctx, opline, op_array, op1_info, 0)) { goto jit_failure; @@ -6857,6 +6868,25 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par || (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT))))) { break; } +#ifdef HAVE_FFI + if (opline->op1_type == IS_CONST + && opline->op2_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, opline->op1); + if (Z_TYPE_P(zv) == IS_STRING + && (zend_string_equals_literal_ci(Z_STR_P(zv), "FFI") + || zend_string_equals_literal_ci(Z_STR_P(zv), "\\FFI"))) { + zval *zv = RT_CONSTANT(opline, opline->op2); + if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal(Z_STR_P(zv), "addr") + && opline->extended_value == 1) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_MASK_FFI_ADDR; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } + } + } +#endif if (!zend_jit_init_static_method_call(&ctx, opline, op_array_ssa->cfg.map ? op_array_ssa->cfg.map[opline - op_array->opcodes] : -1, op_array, ssa, ssa_op, frame->call_level, @@ -7497,7 +7527,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par TRACE_FRAME_INIT(call, p->func, frame_flags, num_args); #ifdef HAVE_FFI if (TRACE_FRAME_FFI(call)) { - ZEND_ASSERT(frame_ffi_func_type != NULL); call->call_opline = (const zend_op*)(void*)frame_ffi_func_type; call->ce = (zend_class_entry*)(intptr_t)frame_ffi_func_ref; } From 11cf0c1103842249cdf2ba5745c01c85ba8292e6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 22 Oct 2024 13:17:18 +0300 Subject: [PATCH 074/101] Passing values to FFI functions won't change their types --- ext/opcache/jit/zend_jit_trace.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 087ba05b197ef..4499081cb9062 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -7185,6 +7185,18 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par || opline->opcode == ZEND_FE_RESET_R) { /* keep old value */ type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)); +#ifdef HAVE_FFI + } else if (JIT_G(current_frame) + && JIT_G(current_frame)->call + && TRACE_FRAME_FFI(JIT_G(current_frame)->call) + && (opline->opcode == ZEND_SEND_VAR_EX + || opline->opcode == ZEND_SEND_VAR_NO_REF + || opline->opcode == ZEND_SEND_VAR_NO_REF_EX + || opline->opcode == ZEND_SEND_FUNC_ARG) + && opline->op2_type != IS_CONST) { + /* keep old value */ + type = STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var)); +#endif } SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), type, (gen_handler || type == IS_UNKNOWN || !ra || From e74f5a3daedbdcaf5293f5cbb4e85082486015a6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 23 Oct 2024 00:30:50 +0300 Subject: [PATCH 075/101] Eliminate some FFI related type guards --- ext/opcache/jit/zend_jit.c | 44 +++++++++++++ ext/opcache/jit/zend_jit_trace.c | 108 ++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 039b9cd0b331e..4058e85aa0876 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -164,6 +164,50 @@ static bool zend_jit_ffi_compatible_addr_op(zend_ffi_type *dst_type, uint32_t sr } return false; } + +static uint32_t zend_jit_ffi_type_info(zend_ffi_type *type) +{ + uint32_t type_kind = type->kind; + uint32_t info = MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN; + + if (type_kind == ZEND_FFI_TYPE_ENUM) { + type_kind = type->enumeration.kind; + } + switch (type_kind) { + case ZEND_FFI_TYPE_VOID: + info = MAY_BE_NULL; + break; + case ZEND_FFI_TYPE_FLOAT: + case ZEND_FFI_TYPE_DOUBLE: + info = MAY_BE_DOUBLE; + break; + case ZEND_FFI_TYPE_UINT8: + case ZEND_FFI_TYPE_SINT8: + case ZEND_FFI_TYPE_UINT16: + case ZEND_FFI_TYPE_SINT16: + case ZEND_FFI_TYPE_UINT32: + case ZEND_FFI_TYPE_SINT32: + case ZEND_FFI_TYPE_UINT64: + case ZEND_FFI_TYPE_SINT64: + info = MAY_BE_LONG; + break; + case ZEND_FFI_TYPE_BOOL: + info = MAY_BE_FALSE|MAY_BE_TRUE; + break; + case ZEND_FFI_TYPE_CHAR: + info = MAY_BE_STRING; + break; + case ZEND_FFI_TYPE_POINTER: + if ((type->attr & ZEND_FFI_ATTR_CONST) + && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { + info = IS_STRING; + } + break; + default: + break; + } + return info; +} #endif #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 4499081cb9062..6d554e47a3b83 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1804,6 +1804,11 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin max_used_stack = used_stack = -1; } +#ifdef HAVE_FFI + uint32_t frame_flags = 0; + zend_ffi_type *frame_ffi_func_type = NULL; +#endif + p = trace_buffer + ZEND_JIT_TRACE_START_REC_SIZE; idx = 0; level = 0; @@ -1814,6 +1819,19 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin uint8_t val_type = IS_UNKNOWN; // zend_class_entry *op1_ce = NULL; zend_class_entry *op2_ce = NULL; +// zend_class_entry *op3_ce = NULL; +#ifdef HAVE_FFI + zend_ffi_type *op1_ffi_type = NULL; + zend_ffi_type *op2_ffi_type = NULL; + zend_ffi_type *op3_ffi_type = NULL; + zend_ffi_type holder1, holder2, holder3; + HashTable *op1_ffi_symbols = NULL; + (void)op2_ffi_type; + (void)op3_ffi_type; + + frame_flags = 0; + frame_ffi_func_type = NULL; +#endif opline = p->opline; @@ -1837,10 +1855,44 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin // op1_ce = (zend_class_entry*)(p+1)->ce; p++; } +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + op1_ffi_type = (zend_ffi_type*)(p+1)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op1_ffi_type)) { + op1_ffi_type = zend_jit_ffi_type_pointer_to(op1_ffi_type, &holder1); + } + p++; + } else if ((p+1)->op == ZEND_JIT_TRACE_OP1_FFI_SYMBOLS) { + op1_ffi_symbols = (HashTable*)(p+1)->ptr; + p++; + } +#endif if ((p+1)->op == ZEND_JIT_TRACE_OP2_TYPE) { op2_ce = (zend_class_entry*)(p+1)->ce; p++; } +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP2_FFI_TYPE) { + op2_ffi_type = (zend_ffi_type*)(p+1)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op2_ffi_type)) { + op2_ffi_type = zend_jit_ffi_type_pointer_to(op2_ffi_type, &holder2); + } + p++; + } +#endif + if ((p+1)->op == ZEND_JIT_TRACE_OP3_TYPE) { +// op3_ce = (zend_class_entry*)(p+1)->ce; + p++; + } +#ifdef HAVE_FFI + if ((p+1)->op == ZEND_JIT_TRACE_OP3_FFI_TYPE) { + op3_ffi_type = (zend_ffi_type*)(p+1)->ptr; + if (ZEND_FFI_TYPE_IS_OWNED(op3_ffi_type)) { + op3_ffi_type = zend_jit_ffi_type_pointer_to(op3_ffi_type, &holder3); + } + p++; + } +#endif if ((p+1)->op == ZEND_JIT_TRACE_VAL_INFO) { val_type = (p+1)->op1_type; p++; @@ -2249,6 +2301,18 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin break; } ADD_OP1_TRACE_GUARD(); +#ifdef HAVE_FFI + if (op1_ffi_symbols) { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, + Z_STR_P(RT_CONSTANT(opline, opline->op2))); + if (sym + && sym->kind == ZEND_FFI_SYM_FUNC + && zend_jit_ffi_supported_func(ZEND_FFI_TYPE(sym->type))) { + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_func_type = ZEND_FFI_TYPE(sym->type); + } + } +#endif break; case ZEND_INIT_DYNAMIC_CALL: if (orig_op2_type == IS_OBJECT && op2_ce == zend_ce_closure) { @@ -2346,6 +2410,11 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin } } } + +#ifdef HAVE_FFI + zend_ssa_op *ssa_op = ssa_ops +idx; +#endif + if (opline->opcode == ZEND_RECV_INIT && !(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { /* RECV_INIT always copy the constant */ @@ -2355,6 +2424,38 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin if (ssa_ops[idx].op2_use >= 0 && ssa_ops[idx].op2_def >= 0) { ssa_var_info[ssa_ops[idx].op2_def] = ssa_var_info[ssa_ops[idx].op2_use]; } +#ifdef HAVE_FFI + } else if (opline->opcode == ZEND_DO_FCALL + && RETURN_VALUE_USED(opline) + && frame + && frame->call + && TRACE_FRAME_FFI(frame->call)) { + zend_ffi_type *type = (zend_ffi_type*)frame->call->call_opline; + + ssa_var_info[ssa_ops[idx].result_def].type = + zend_jit_ffi_type_info(ZEND_FFI_TYPE(type->func.ret_type)); + } else if (op1_ffi_type + && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) + && (opline->opcode == ZEND_FETCH_DIM_R + || opline->opcode == ZEND_FETCH_DIM_IS + || opline->opcode == ZEND_FETCH_LIST_R) + && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_LONG + && ZEND_FFI_TYPE(op1_ffi_type->array.type)->kind != ZEND_FFI_TYPE_VOID + && zend_jit_ffi_supported_type(ZEND_FFI_TYPE(op1_ffi_type->array.type))) { + ssa_var_info[ssa_ops[idx].result_def].type = + zend_jit_ffi_type_info(ZEND_FFI_TYPE(op1_ffi_type->array.type)); + } else if (ssa_ops[idx].op1_def >= 0 + && frame + && frame->call + && TRACE_FRAME_FFI(frame->call) + && (opline->opcode == ZEND_SEND_VAR_EX + || opline->opcode == ZEND_SEND_VAR_NO_REF + || opline->opcode == ZEND_SEND_VAR_NO_REF_EX + || opline->opcode == ZEND_SEND_FUNC_ARG) + && opline->op2_type != IS_CONST) { + ssa_var_info[ssa_ops[idx].op1_def] = ssa_var_info[ssa_ops[idx].op1_use]; + ssa_var_info[ssa_ops[idx].op1_def].type &= ~MAY_BE_GUARD; +#endif } else { if (zend_update_type_info(op_array, tssa, script, (zend_op*)opline, ssa_ops + idx, ssa_opcodes, optimization_level) == FAILURE) { // TODO: @@ -2607,7 +2708,12 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin } else if (p->op == ZEND_JIT_TRACE_INIT_CALL) { call = top; - TRACE_FRAME_INIT(call, p->func, 0, 0); + TRACE_FRAME_INIT(call, p->func, frame_flags, 0); +#ifdef HAVE_FFI + if (TRACE_FRAME_FFI(call)) { + call->call_opline = (const zend_op*)(void*)frame_ffi_func_type; + } +#endif call->prev = frame->call; call->used_stack = 0; frame->call = call; From c1741f097d7e29a8372da8fa02a17daa8fda57f4 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 23 Oct 2024 11:14:20 +0300 Subject: [PATCH 076/101] Fix build without FFI --- ext/opcache/jit/zend_jit_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 6d554e47a3b83..fb5c8cc2e8993 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1804,8 +1804,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin max_used_stack = used_stack = -1; } -#ifdef HAVE_FFI uint32_t frame_flags = 0; +#ifdef HAVE_FFI zend_ffi_type *frame_ffi_func_type = NULL; #endif From b22fbd81c9f5cdfbb0ef67ff75bba97f05e2400c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 23 Oct 2024 11:21:54 +0300 Subject: [PATCH 077/101] Fix incorrect type info --- ext/opcache/jit/zend_jit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 4058e85aa0876..accf5778623a3 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -200,7 +200,7 @@ static uint32_t zend_jit_ffi_type_info(zend_ffi_type *type) case ZEND_FFI_TYPE_POINTER: if ((type->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { - info = IS_STRING; + info = MAY_BE_STRING; } break; default: From 60f01a3cddf6e3c081617be2e66dbce8d2ca6a88 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 24 Oct 2024 12:12:44 +0300 Subject: [PATCH 078/101] Fix arguments cleanup --- ext/opcache/jit/zend_jit_ir_ffi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index f7425e4345932..d07e645986f44 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -507,7 +507,7 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, case ZEND_FFI_TYPE_BOOL: jit_set_Z_TYPE_INFO(jit, res_addr, ir_ADD_U32(ir_ZEXT_U32(ref), ir_CONST_U32(IS_FALSE))); - return 1; + goto cleanup; case ZEND_FFI_TYPE_CHAR: jit_set_Z_PTR(jit, res_addr, ir_LOAD_A( ir_ADD_A(ir_CONST_ADDR(zend_one_char_string), @@ -518,11 +518,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, if ((ret_type->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(ret_type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) { ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), jit_ZVAL_ADDR(jit, res_addr), ref); - return 1; + goto cleanup; } else { ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ret_type), ref); - return 1; + goto cleanup; } break; default: @@ -534,6 +534,7 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } } +cleanup: if (num_args) { zend_jit_trace_stack *stack = call->stack; From 523f74582ed7e95e3ed84415165ce61e2d1a9126 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 24 Oct 2024 17:52:29 +0300 Subject: [PATCH 079/101] JIR for FFI::string() --- ext/opcache/jit/zend_jit.h | 1 + ext/opcache/jit/zend_jit_helpers.c | 59 +++++------- ext/opcache/jit/zend_jit_internal.h | 19 +++- ext/opcache/jit/zend_jit_ir.c | 1 + ext/opcache/jit/zend_jit_ir_ffi.c | 142 +++++++++++++++++++++------- ext/opcache/jit/zend_jit_trace.c | 11 ++- 6 files changed, 160 insertions(+), 73 deletions(-) diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index 1ad4e8927e176..c423ab426160a 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -184,6 +184,7 @@ void zend_jit_restart(void); #define ZREG_FFI_PTR_LOAD (1<<3) #define ZREG_FFI_ZVAL_DTOR (1<<4) +#define ZREG_FFI_ZVAL_DEREF (1<<5) #define ZREG_NONE -1 diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 28d241199a99f..7010bd9f407ce 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3373,6 +3373,15 @@ static void ZEND_FASTCALL zend_jit_zval_string(zval *zv, const char *str) } } +static void ZEND_FASTCALL zend_jit_zval_stringl(zval *zv, const char *str, size_t len) +{ + if (str) { + ZVAL_STRINGL(zv, str, len); + } else { + ZVAL_NULL(zv); + } +} + static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, void *ptr) { ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER); @@ -3421,18 +3430,11 @@ static void ZEND_FASTCALL zend_jit_zval_ffi_obj(zval *zv, zend_ffi_type *type, v } } -static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr(zval *zv) +static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr(zend_ffi_cdata *cdata) { - zend_ffi_cdata *cdata, *new_cdata; + zend_ffi_cdata *new_cdata; zend_ffi_type *type, *new_type; - if (Z_TYPE_P(zv) == IS_INDIRECT) { - zv = Z_INDIRECT_P(zv); - } - ZVAL_DEREF(zv); - ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); - - cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); type = ZEND_FFI_TYPE(cdata->type); // if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER @@ -3467,19 +3469,11 @@ static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr(zval *zv) return new_cdata; } -static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr_var(zval *zv) +static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr_var(zend_ffi_cdata *cdata) { - zend_ffi_cdata *cdata, *new_cdata; + zend_ffi_cdata *new_cdata; zend_ffi_type *type, *new_type; - zval *arg = zv; - - if (Z_TYPE_P(zv) == IS_INDIRECT) { - zv = Z_INDIRECT_P(zv); - } - ZVAL_DEREF(zv); - ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); - cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); type = ZEND_FFI_TYPE(cdata->type); // if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER @@ -3511,23 +3505,16 @@ static zend_ffi_cdata* ZEND_FASTCALL zend_jit_zval_ffi_addr_var(zval *zv) new_cdata->ptr = (void*)&new_cdata->ptr_holder; new_cdata->ptr_holder = cdata->ptr; - if (Z_REFCOUNTED_P(arg)) { - zend_refcounted *ref = Z_COUNTED_P(arg); - - if (!GC_DELREF(ref)) { - if (ref == (zend_refcounted*)cdata || GC_REFCOUNT(&cdata->std) == 1) { - if (ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { - /* transfer type ownership */ - cdata->type = type; - new_type->pointer.type = ZEND_FFI_TYPE_MAKE_OWNED(type); - } - if (cdata->flags & ZEND_FFI_FLAG_OWNED) { - /* transfer ownership */ - cdata->flags &= ~ZEND_FFI_FLAG_OWNED; - new_cdata->flags |= ZEND_FFI_FLAG_OWNED; - } - } - rc_dtor_func(ref); + if (GC_REFCOUNT(&cdata->std) == 1 /*&& Z_REFCOUNT_P(arg) == 1*/) { + if (ZEND_FFI_TYPE_IS_OWNED(cdata->type)) { + /* transfer type ownership */ + cdata->type = type; + new_type->pointer.type = ZEND_FFI_TYPE_MAKE_OWNED(type); + } + if (cdata->flags & ZEND_FFI_FLAG_OWNED) { + /* transfer ownership */ + cdata->flags &= ~ZEND_FFI_FLAG_OWNED; + new_cdata->flags |= ZEND_FFI_FLAG_OWNED; } } diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index cdd008183e399..0504a0d232e24 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -575,7 +575,20 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS 0x00000400 #define TRACE_FRAME_MASK_FFI 0x00000800 -#define TRACE_FRAME_MASK_FFI_ADDR 0x00001000 +#define TRACE_FRAME_MASK_FFI_FUNC 0x0000f000 + +#define TRACE_FRAME_FFI_FUNC_NEW 0x00001000 +#define TRACE_FRAME_FFI_FUNC_FREE 0x00002000 +#define TRACE_FRAME_FFI_FUNC_CAST 0x00003000 +#define TRACE_FRAME_FFI_FUNC_TYPEOF 0x00004000 +#define TRACE_FRAME_FFI_FUNC_ARRAY_TYPE 0x00005000 +#define TRACE_FRAME_FFI_FUNC_ADDR 0x00006000 +#define TRACE_FRAME_FFI_FUNC_SIZEOF 0x00007000 +#define TRACE_FRAME_FFI_FUNC_MEMCPY 0x00008000 +#define TRACE_FRAME_FFI_FUNC_MEMCMP 0x00009000 +#define TRACE_FRAME_FFI_FUNC_MEMSET 0x0000a000 +#define TRACE_FRAME_FFI_FUNC_STRING 0x0000b000 +#define TRACE_FRAME_FFI_FUNC_IS_NULL 0x0000c000 #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ @@ -617,8 +630,8 @@ struct _zend_jit_trace_stack_frame { ((frame)->_info & TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS) #define TRACE_FRAME_FFI(frame) \ ((frame)->_info & TRACE_FRAME_MASK_FFI) -#define TRACE_FRAME_FFI_ADDR(frame) \ - ((frame)->_info & TRACE_FRAME_MASK_FFI_ADDR) +#define TRACE_FRAME_FFI_FUNC(frame) \ + ((frame)->_info & TRACE_FRAME_MASK_FFI_FUNC) #define TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(frame) do { \ (frame)->_info |= (0xffffu << TRACE_FRAME_SHIFT_NUM_ARGS); \ diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index df2d00a919ae0..19401770c4296 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3102,6 +3102,7 @@ static void zend_jit_setup_disasm(void) #ifdef HAVE_FFI REGISTER_HELPER(zend_jit_zval_string); + REGISTER_HELPER(zend_jit_zval_stringl); REGISTER_HELPER(zend_jit_zval_ffi_ptr); REGISTER_HELPER(zend_jit_zval_ffi_obj); REGISTER_HELPER(zend_jit_zval_ffi_addr); diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index d07e645986f44..57fa53b8f457a 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -115,22 +115,50 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, uint8_t arg_type = IS_UNDEF; uint8_t arg_flags = 0; - if (!type) { - ZEND_ASSERT(TRACE_FRAME_FFI_ADDR(call)); - ZEND_ASSERT(opline->op2.num == 1); - ZEND_ASSERT(op1_ffi_type); - - if (opline->op1_type == IS_VAR) { - ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_ffi_addr_var), - jit_ZVAL_ADDR(jit, op1_addr)); + if (TRACE_FRAME_FFI_FUNC(call)) { + if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR) { + ZEND_ASSERT(opline->op2.num == 1); + ZEND_ASSERT(op1_ffi_type); + + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_STRING) { + if (opline->op2.num == 1) { + ZEND_ASSERT(op1_ffi_type); + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } else { + ZEND_ASSERT(opline->op2.num == 2); + if (op1_info == MAY_BE_LONG) { + ref = jit_Z_LVAL(jit, op1_addr); + } else if (op1_info == MAY_BE_NULL) { + ref = IR_NULL; + } else { + ZEND_ASSERT(0 && "NIY"); + } + SET_STACK_TYPE(stack, 1, IS_LONG, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + } } else { - ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_ffi_addr), - jit_ZVAL_ADDR(jit, op1_addr)); + ZEND_UNREACHABLE(); } - - SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); - SET_STACK_REF_EX(stack, 0, ref, 0); - return 1; } ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); @@ -328,6 +356,21 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, return 1; } +static ir_ref zend_jit_gc_deref(zend_jit_ctx *jit, ir_ref ref) +{ + ir_ref if_ref, ref2; + + if_ref = ir_IF(ir_EQ( + ir_AND_U32( + ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_refcounted_h, u.type_info))), + ir_CONST_U32(GC_TYPE_MASK)), + ir_CONST_U32(IS_REFERENCE))); + ir_IF_TRUE(if_ref); + ref2 = jit_Z_PTR_ref(jit, ir_ADD_OFFSET(ref, offsetof(zend_reference, val))); + ir_MERGE_WITH_EMPTY_FALSE(if_ref); + return ir_PHI_2(IR_ADDR, ref2, ref); +} + static int zend_jit_ffi_do_call(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -339,20 +382,49 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, zend_jit_trace_stack *stack = call->stack; zend_ffi_type *type = (zend_ffi_type*)(void*)call->call_opline; ir_ref func_ref = (intptr_t)(void*)call->ce; - uint32_t i, num_args; + uint32_t i, num_args = TRACE_FRAME_NUM_ARGS(call);; ir_type ret_type = IR_VOID; ir_ref ref = IR_UNUSED; zend_ffi_type_kind type_kind; - if (!type) { - ZEND_ASSERT(TRACE_FRAME_FFI_ADDR(call)); - - ref = STACK_REF(stack, 0); - - jit_set_Z_PTR(jit, res_addr, ref); - jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); - - return 1; + if (TRACE_FRAME_FFI_FUNC(call)) { + if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DTOR) { + ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_ffi_addr_var), ref); + } else { + ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_zval_ffi_addr), ref); + } + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_STRING) { + ZEND_ASSERT(num_args > 0 && STACK_TYPE(stack, 0) == IS_OBJECT); + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_PTR(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_PTR_LOAD) { + ref = ir_LOAD_A(ref); + } + if (num_args == 1 || STACK_REF(stack, 1) == IR_NULL) { + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_string), + jit_ZVAL_ADDR(jit, res_addr), ref); + } else { + ZEND_ASSERT(num_args == 2); + ZEND_ASSERT(STACK_TYPE(stack, 1) == IS_LONG); + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_stringl), + jit_ZVAL_ADDR(jit, res_addr), ref, STACK_REF(stack, 1)); + } + } else { + ZEND_UNREACHABLE(); + } + goto cleanup; } ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); @@ -417,7 +489,6 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } } - num_args = TRACE_FRAME_NUM_ARGS(call); if (num_args) { ir_ref *args = alloca(sizeof(ir_ref) * num_args); @@ -543,7 +614,12 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, uint8_t type = STACK_TYPE(stack, i); ir_ref ref = STACK_REF(stack, i); - if (type == IS_STRING) { + if (STACK_FLAGS(stack, i) & ZREG_FFI_ZVAL_DEREF) { + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, ref)); + ir_IF_FALSE(if_not_zero); + jit_ZVAL_DTOR(jit, ref, MAY_BE_REF|MAY_BE_ANY, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ + } else if (type == IS_STRING) { ir_ref if_interned = ir_IF(ir_AND_U32( ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_refcounted, gc.u.type_info))), ir_CONST_U32(IS_STR_INTERNED))); @@ -565,12 +641,14 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } } - func_ref = (intptr_t)(void*)call->ce; - if (!IR_IS_CONST_REF(func_ref)) { - ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, func_ref)); - ir_IF_FALSE(if_not_zero); - jit_ZVAL_DTOR(jit, func_ref, MAY_BE_OBJECT, opline); - ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ + if (!TRACE_FRAME_FFI_FUNC(call)) { + func_ref = (intptr_t)(void*)call->ce; + if (!IR_IS_CONST_REF(func_ref)) { + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, func_ref)); + ir_IF_FALSE(if_not_zero); + jit_ZVAL_DTOR(jit, func_ref, MAY_BE_OBJECT, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ + } } return 1; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index fb5c8cc2e8993..19d5c8b7ac0a4 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6983,9 +6983,16 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par || zend_string_equals_literal_ci(Z_STR_P(zv), "\\FFI"))) { zval *zv = RT_CONSTANT(opline, opline->op2); if (Z_TYPE_P(zv) == IS_STRING - && zend_string_equals_literal(Z_STR_P(zv), "addr") + && zend_string_equals_literal_ci(Z_STR_P(zv), "addr") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_MASK_FFI_ADDR; + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_ADDR; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "string") + && (opline->extended_value == 1 || opline->extended_value == 2)) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_STRING; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; From 1e067d5118883711f0eae6b77b2eb4544350fd3e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 25 Oct 2024 12:01:53 +0300 Subject: [PATCH 080/101] JIT for FFI::typeof() and FFI:isNull() --- ext/ffi/ffi.c | 12 ++++++ ext/ffi/php_ffi.h | 2 + ext/opcache/jit/zend_jit_internal.h | 13 +++--- ext/opcache/jit/zend_jit_ir_ffi.c | 67 ++++++++++++++++++++++++++++- ext/opcache/jit/zend_jit_trace.c | 31 +++++++++++++ 5 files changed, 118 insertions(+), 7 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 9f2eb08914bbb..c663e13b2ddc6 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -372,6 +372,16 @@ static zend_ffi_cdata* zend_ffi_cdata_create(void *ptr, zend_ffi_type *type) /* } /* }}} */ +static zend_ffi_ctype* zend_ffi_ctype_create(zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_ctype *ctype = emalloc(sizeof(zend_ffi_ctype)); + + zend_ffi_object_init(&ctype->std, zend_ffi_ctype_ce); + ctype->type = type; + return ctype; +} +/* }}} */ + static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */ { zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); @@ -5611,8 +5621,10 @@ ZEND_MINIT_FUNCTION(ffi) ffi_api.scope_ce = zend_ffi_ce; ffi_api.cdata_ce = zend_ffi_cdata_ce; + ffi_api.ctype_ce = zend_ffi_ctype_ce; ffi_api.cdata_create = zend_ffi_cdata_create; + ffi_api.ctype_create = zend_ffi_ctype_create; ffi_api.type_print = zend_ffi_type_print; ffi_api.is_compatible_type = zend_ffi_is_compatible_type; diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index ee1f95c3c338b..75549d9356c45 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -416,8 +416,10 @@ typedef struct _zend_ffi { struct _zend_ffi_api { zend_class_entry *scope_ce; zend_class_entry *cdata_ce; + zend_class_entry *ctype_ce; zend_ffi_cdata* (*cdata_create)(void *ptr, zend_ffi_type *type); + zend_ffi_ctype* (*ctype_create)(zend_ffi_type *type); void (*type_print)(FILE *f, const zend_ffi_type *type); bool (*is_compatible_type)(zend_ffi_type *dst_type, zend_ffi_type *src_type); diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 0504a0d232e24..5b9ad73a84226 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -583,12 +583,13 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_FFI_FUNC_TYPEOF 0x00004000 #define TRACE_FRAME_FFI_FUNC_ARRAY_TYPE 0x00005000 #define TRACE_FRAME_FFI_FUNC_ADDR 0x00006000 -#define TRACE_FRAME_FFI_FUNC_SIZEOF 0x00007000 -#define TRACE_FRAME_FFI_FUNC_MEMCPY 0x00008000 -#define TRACE_FRAME_FFI_FUNC_MEMCMP 0x00009000 -#define TRACE_FRAME_FFI_FUNC_MEMSET 0x0000a000 -#define TRACE_FRAME_FFI_FUNC_STRING 0x0000b000 -#define TRACE_FRAME_FFI_FUNC_IS_NULL 0x0000c000 +#define TRACE_FRAME_FFI_FUNC_ALIGNOF 0x00007000 +#define TRACE_FRAME_FFI_FUNC_SIZEOF 0x00008000 +#define TRACE_FRAME_FFI_FUNC_MEMCPY 0x00009000 +#define TRACE_FRAME_FFI_FUNC_MEMCMP 0x0000a000 +#define TRACE_FRAME_FFI_FUNC_MEMSET 0x0000b000 +#define TRACE_FRAME_FFI_FUNC_STRING 0x0000c000 +#define TRACE_FRAME_FFI_FUNC_IS_NULL 0x0000d000 #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 57fa53b8f457a..81b4f9ab531b9 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -21,6 +21,11 @@ static ir_ref jit_FFI_CDATA_PTR(zend_jit_ctx *jit, ir_ref obj_ref) return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, ptr))); } +static ir_ref jit_FFI_CDATA_TYPE(zend_jit_ctx *jit, ir_ref obj_ref) +{ + return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, type))); +} + static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, const zend_op *opline, zend_ssa *ssa, @@ -116,10 +121,19 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, uint8_t arg_flags = 0; if (TRACE_FRAME_FFI_FUNC(call)) { - if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR) { + if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ALIGNOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_SIZEOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPEOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) { ZEND_ASSERT(opline->op2.num == 1); ZEND_ASSERT(op1_ffi_type); + if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) { + ZEND_ASSERT(op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER); + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { arg_flags |= ZREG_FFI_ZVAL_DTOR; } @@ -401,6 +415,57 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ALIGNOF) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_TYPE(jit, ref); + // TODO: type flags ??? + ref = ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_ffi_type, align))); + if (sizeof(void*) == 8) { + ref = ir_ZEXT_L(ref); + } + jit_set_Z_LVAL(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_SIZEOF) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_TYPE(jit, ref); + // TODO: type flags ??? + ref = ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_ffi_type, size))); + if (sizeof(void*) == 8) { + ref = ir_ZEXT_L(ref); + } + jit_set_Z_LVAL(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPEOF) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_TYPE(jit, ref); + // TODO: type flags ??? + ref = ir_CALL_1(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->ctype_create), ref); + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_TYPE(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_PTR_LOAD) { + ref = ir_LOAD_A(ref); + } + ref = ir_ADD_U32(ir_ZEXT_U32(ir_EQ(ref, IR_NULL)), ir_CONST_U32(IS_FALSE)); + jit_set_Z_TYPE_INFO_ex(jit, res_addr, ref); } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_STRING) { ZEND_ASSERT(num_args > 0 && STACK_TYPE(stack, 0) == IS_OBJECT); ref = STACK_REF(stack, 0); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 19d5c8b7ac0a4..f49a438efb206 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6996,6 +6996,37 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; +#if 0 +// TODO: add support for FFI::CType argument ??? + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "alignof") + && opline->extended_value == 1) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_ALIGNOF; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "sizeof") + && opline->extended_value == 1) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_SIZEOF; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; +#endif + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "typeof") + && opline->extended_value == 1) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_TYPEOF; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "isnull") + && opline->extended_value == 1) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_IS_NULL; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; } } } From 453226a5defd25cf95f27d5ac8e3e28305d8c264 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 28 Oct 2024 13:55:07 +0300 Subject: [PATCH 081/101] ws --- ext/opcache/jit/zend_jit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index accf5778623a3..cb802710f8f76 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -70,7 +70,7 @@ static bool zend_jit_ffi_supported_type(zend_ffi_type *type) { if (sizeof(void*) == 4) { if (ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_UINT64 - || ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_SINT64) { + || ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_SINT64) { return false; } } From 247927e855ffaec41fca31ac86f6a8c9310ade68 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 28 Oct 2024 15:55:41 +0300 Subject: [PATCH 082/101] Improve FFI type compatibility checks --- ext/opcache/jit/zend_jit.c | 64 ++++++++++++++++--------------- ext/opcache/jit/zend_jit_ir_ffi.c | 17 ++++++++ ext/opcache/jit/zend_jit_trace.c | 18 +++------ 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index cb802710f8f76..d4e8a9d098194 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -115,51 +115,55 @@ static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, } else if ((src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG || (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE) { return dst_type->kind < ZEND_FFI_TYPE_POINTER && dst_type->kind != ZEND_FFI_TYPE_VOID; - } else if (src_info == MAY_BE_FALSE || src_info == MAY_BE_TRUE || src_info == (MAY_BE_FALSE|MAY_BE_TRUE)) { + } else if ((src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_FALSE + || (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_TRUE + || (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == (MAY_BE_FALSE|MAY_BE_TRUE)) { return dst_type->kind == ZEND_FFI_TYPE_BOOL; + } else if ((src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING) { + return dst_type->kind == ZEND_FFI_TYPE_CHAR; + } else if ((src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_NULL) { + return dst_type->kind == ZEND_FFI_TYPE_POINTER; } else if (src_type) { if (!zend_jit_ffi_supported_type(src_type)) { return false; } - if (src_type->kind >= ZEND_FFI_TYPE_POINTER) { - return false; - } if (dst_type == src_type -// TODO: calls between shared extensions doesn't work on Windows -// || zend_ffi_is_compatible_type(dst_type, src_type) - ) { + || zend_ffi_api->is_compatible_type(dst_type, src_type)) { return true; } } return false; } -static bool zend_jit_ffi_compatible_addr(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type) +static bool zend_jit_ffi_compatible_op(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type, uint8_t op) { - if (dst_type->kind == ZEND_FFI_TYPE_POINTER) { - if (src_info == MAY_BE_NULL) { - return true; - } else if (src_type - && src_type->kind == ZEND_FFI_TYPE_POINTER - && (dst_type == src_type - || ZEND_FFI_TYPE(dst_type->pointer.type) == ZEND_FFI_TYPE(src_type->pointer.type) - || ZEND_FFI_TYPE(dst_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID - || ZEND_FFI_TYPE(src_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID -// TODO: calls between shared extensions doesn't work on Windows -// || zend_ffi_is_compatible_type(dst_type, src_type) - )) { - return true; - } - } - return false; -} - -static bool zend_jit_ffi_compatible_addr_op(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type, uint8_t opcode) -{ - if (dst_type->kind == ZEND_FFI_TYPE_POINTER + dst_type = ZEND_FFI_TYPE(dst_type); + if (!zend_jit_ffi_supported_type(dst_type)) { + return false; + } else if (dst_type->kind == ZEND_FFI_TYPE_FLOAT || dst_type->kind == ZEND_FFI_TYPE_DOUBLE) { + return (op == ZEND_ADD || op == ZEND_SUB || op == ZEND_MUL) + && ((src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE + || (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG); + } else if (dst_type->kind == ZEND_FFI_TYPE_BOOL) { + return (op == ZEND_BW_AND || op == ZEND_BW_OR) + && (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG; + } else if (dst_type->kind == ZEND_FFI_TYPE_UINT8 + || dst_type->kind == ZEND_FFI_TYPE_UINT16 + || dst_type->kind == ZEND_FFI_TYPE_UINT32 + || dst_type->kind == ZEND_FFI_TYPE_UINT64 + || dst_type->kind == ZEND_FFI_TYPE_SINT8 + || dst_type->kind == ZEND_FFI_TYPE_SINT16 + || dst_type->kind == ZEND_FFI_TYPE_SINT32 + || dst_type->kind == ZEND_FFI_TYPE_SINT64 + || dst_type->kind == ZEND_FFI_TYPE_CHAR) { + return (op == ZEND_ADD || op == ZEND_SUB || op == ZEND_MUL + || op == ZEND_BW_AND || op == ZEND_BW_OR || op == ZEND_BW_XOR + || op == ZEND_SL || op == ZEND_SR || op == ZEND_MOD) + && (src_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG; + } else if (dst_type->kind == ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(dst_type->pointer.type)->size != 0 && src_info == MAY_BE_LONG - && (opcode == ZEND_ADD || opcode == ZEND_SUB)) { + && (op == ZEND_ADD || op == ZEND_SUB)) { return true; } return false; diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 81b4f9ab531b9..3f0d4b46925ff 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -1292,6 +1292,11 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, || ZEND_FFI_TYPE(val_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID || ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID)) { ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr))); + } else if (val_ffi_type + && val_ffi_type->kind == ZEND_FFI_TYPE_ARRAY + && (ZEND_FFI_TYPE(val_ffi_type->pointer.type) == ZEND_FFI_TYPE(ffi_type->pointer.type) + || ZEND_FFI_TYPE(ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID)) { + ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)); } else { ZEND_UNREACHABLE(); } @@ -1302,6 +1307,18 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, } break; default: + if (val_ffi_type + && (val_ffi_type == ffi_type + || (zend_ffi_api->is_compatible_type(ffi_type, val_ffi_type) + && ffi_type->size == val_ffi_type->size))) { + ref = jit_FFI_CDATA_PTR(jit, jit_Z_PTR(jit, val_addr)); + ir_CALL_3(IR_ADDR, ir_CONST_FUNC(memcpy), ptr, ref, ir_CONST_LONG(ffi_type->size)); + if (res_addr) { + ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_obj), + jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ref); + } + break; + } ZEND_UNREACHABLE(); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index f49a438efb206..2adef60b4b7a5 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4920,8 +4920,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && op2_info == MAY_BE_LONG - && (zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type) - || zend_jit_ffi_compatible_addr_op(op1_ffi_type->array.type, op1_data_info, op3_ffi_type, opline->extended_value))) { + && zend_jit_ffi_compatible_op(op1_ffi_type->array.type, op1_data_info, op3_ffi_type, opline->extended_value)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5191,8 +5190,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->is_const && !field->bits - && (zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type) - || zend_jit_ffi_compatible_addr_op(field->type, op1_data_info, op3_ffi_type, opline->extended_value))) { + && zend_jit_ffi_compatible_op(field->type, op1_data_info, op3_ffi_type, opline->extended_value)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5224,8 +5222,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym && sym->kind == ZEND_FFI_SYM_VAR - && (zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type) - || zend_jit_ffi_compatible_addr_op(sym->type, op1_data_info, op3_ffi_type, opline->extended_value))) { + && zend_jit_ffi_compatible_op(sym->type, op1_data_info, op3_ffi_type, opline->extended_value)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5330,8 +5327,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (field && !field->is_const && !field->bits - && (zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type) - || zend_jit_ffi_compatible_addr(field->type, op1_data_info, op3_ffi_type))) { + && zend_jit_ffi_compatible(field->type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5375,8 +5371,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par Z_STR_P(RT_CONSTANT(opline, opline->op2))); if (sym && sym->kind == ZEND_FFI_SYM_VAR - && (zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type) - || zend_jit_ffi_compatible_addr(sym->type, op1_data_info, op3_ffi_type))) { + && zend_jit_ffi_compatible(sym->type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -5451,8 +5446,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (op1_ffi_type && (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY || op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) && op2_info == MAY_BE_LONG - && (zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type) - || zend_jit_ffi_compatible_addr(op1_ffi_type->array.type, op1_data_info, op3_ffi_type))) { + && zend_jit_ffi_compatible(op1_ffi_type->array.type, op1_data_info, op3_ffi_type)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } From 52dab2353205e38473c46499cedb7fa2e1fd6911 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 29 Oct 2024 13:23:25 +0300 Subject: [PATCH 083/101] Fix FFI scope reference counting during FFI call --- ext/opcache/jit/zend_jit_ir_ffi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 3f0d4b46925ff..0133ce6aa4d19 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -66,6 +66,11 @@ static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, return 0; } + if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + // TODO: usually the object is released only after the call ??? + jit_GC_DELREF(jit, jit_Z_PTR(jit, op1_addr)); + } + if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { *ffi_func_ref = ir_CONST_FC_FUNC(sym->addr); } else { From 3e4e6d05e5f42045187c8d3f4daec533a15d7648 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 29 Oct 2024 20:46:57 +0300 Subject: [PATCH 084/101] Improve type inference for ZEND_FETCH_OBJ/DIM_FUNC_ARG --- ext/opcache/jit/zend_jit_trace.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 2adef60b4b7a5..60c8ae8bd8ed1 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2064,7 +2064,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin case ZEND_FETCH_DIM_FUNC_ARG: if (!frame || !frame->call - || !frame->call->func +//??? || !frame->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(frame->call)) { break; } @@ -2259,8 +2259,17 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin break; case ZEND_CHECK_FUNC_ARG: if (!frame - || !frame->call - || !frame->call->func) { + || !frame->call) { + break; + } +#ifdef HAVE_FFI + if (TRACE_FRAME_FFI(frame->call)) { + /* FFI arguments alwyas sent by value ??? */ + TRACE_FRAME_SET_LAST_SEND_BY_VAL(frame->call); + break; + } +#endif + if (!frame->call->func) { break; } if (opline->op2_type == IS_CONST @@ -2278,7 +2287,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin case ZEND_FETCH_OBJ_FUNC_ARG: if (!frame || !frame->call - || !frame->call->func +//??? || !frame->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(frame->call)) { break; } From 086efdeb38a27c7abdbbefd45b8388ea489e9d91 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 31 Oct 2024 11:27:07 +0300 Subject: [PATCH 085/101] Add IS_OBJECT type assertions --- ext/opcache/jit/zend_jit_ir_ffi.c | 116 ++++++++++++++++++------------ ext/opcache/jit/zend_jit_trace.c | 12 +++- 2 files changed, 80 insertions(+), 48 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 0133ce6aa4d19..26aec3163d65d 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -31,18 +31,20 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, zend_ssa *ssa, int use, int def, + uint32_t info, zend_jit_addr addr, HashTable *ffi_symbols, zend_jit_ffi_info *ffi_info); -static int zend_jit_ffi_guard(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - int use, - int def, - ir_ref ref, - zend_ffi_type *ffi_type, - zend_jit_ffi_info *ffi_info); +static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + uint32_t info, + zend_jit_addr addr, + zend_ffi_type *ffi_type, + zend_jit_ffi_info *ffi_info); static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, const zend_op *opline, @@ -62,7 +64,7 @@ static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, type = ZEND_FFI_TYPE(sym->type); ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { return 0; } @@ -92,14 +94,15 @@ static int zend_jit_ffi_init_call_obj(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info, ir_ref *ffi_func_ref) { - ir_ref obj_ref = jit_Z_PTR(jit, op2_addr); + ir_ref obj_ref; zend_ffi_type *type; ZEND_ASSERT(op2_ffi_type->kind == ZEND_FFI_TYPE_POINTER); type = ZEND_FFI_TYPE(op2_ffi_type->pointer.type); ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC); - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op2_use, -1, obj_ref, op2_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op2_use, -1, op2_info, op2_addr, op2_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -890,15 +893,22 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, return 1; } -static int zend_jit_ffi_guard(zend_jit_ctx *jit, - const zend_op *opline, - zend_ssa *ssa, - int use, - int def, - ir_ref ref, - zend_ffi_type *ffi_type, - zend_jit_ffi_info *ffi_info) +static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit, + const zend_op *opline, + zend_ssa *ssa, + int use, + int def, + uint32_t info, + zend_jit_addr addr, + zend_ffi_type *ffi_type, + zend_jit_ffi_info *ffi_info) { + ir_ref ref; + + /* MAY_BE_GUARD may be added by zend_jit_fetch_reference() */ + ZEND_ASSERT((info & (/*MAY_BE_GUARD|*/MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT); + + ref = jit_Z_PTR(jit, addr); if (ssa->var_info && use >= 0 && ssa->var_info[use].ce != zend_ffi_api->cdata_ce) { @@ -930,7 +940,7 @@ static int zend_jit_ffi_guard(zend_jit_ctx *jit, } } - return 1; + return ref; } static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, @@ -938,12 +948,15 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, zend_ssa *ssa, int use, int def, + uint32_t info, zend_jit_addr addr, HashTable *ffi_symbols, zend_jit_ffi_info *ffi_info) { ir_ref ref = IR_UNUSED; + ZEND_ASSERT((info & (MAY_BE_GUARD|MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT); + if (ssa->var_info && use >= 0 && ssa->var_info[use].ce != zend_ffi_api->scope_ce) { @@ -1007,9 +1020,10 @@ static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1349,9 +1363,10 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1596,9 +1611,10 @@ static int zend_jit_ffi_assign_dim_op(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { zend_ffi_type *el_type = ZEND_FFI_TYPE(op1_ffi_type->array.type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1644,9 +1660,10 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, { uint32_t res_info = RES_INFO(); zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1692,9 +1709,10 @@ static int zend_jit_ffi_fetch_val(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { uint32_t res_info = RES_INFO(); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1742,7 +1760,7 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, uint32_t res_info = RES_INFO(); zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_addr, op1_ffi_symbols, ffi_info)) { + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { return 0; } @@ -1791,9 +1809,10 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1840,9 +1859,10 @@ static int zend_jit_ffi_assign_val(zend_jit_ctx *jit, zend_ffi_type *val_ffi_type, zend_jit_ffi_info *ffi_info) { - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1891,7 +1911,7 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, { zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { return 0; } @@ -1935,9 +1955,10 @@ static int zend_jit_ffi_assign_obj_op(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -1971,9 +1992,10 @@ static int zend_jit_ffi_assign_val_op(zend_jit_ctx *jit, zend_ffi_type *op1_ffi_type, zend_jit_ffi_info *ffi_info) { - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -2009,7 +2031,7 @@ static int zend_jit_ffi_assign_sym_op(zend_jit_ctx *jit, { zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { return 0; } @@ -2249,9 +2271,10 @@ static int zend_jit_ffi_incdec_obj(zend_jit_ctx *jit, zend_jit_ffi_info *ffi_info) { zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type); - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -2281,9 +2304,10 @@ static int zend_jit_ffi_incdec_val(zend_jit_ctx *jit, zend_ffi_type *op1_ffi_type, zend_jit_ffi_info *ffi_info) { - ir_ref obj_ref = jit_Z_PTR(jit, op1_addr); + ir_ref obj_ref; - if (!zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, obj_ref, op1_ffi_type, ffi_info)) { + obj_ref = zend_jit_ffi_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_type, ffi_info); + if (!obj_ref) { return 0; } @@ -2315,7 +2339,7 @@ static int zend_jit_ffi_incdec_sym(zend_jit_ctx *jit, { zend_ffi_type *sym_type = ZEND_FFI_TYPE(sym->type); - if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_addr, op1_ffi_symbols, ffi_info)) { + if (!zend_jit_ffi_symbols_guard(jit, opline, ssa, ssa_op->op1_use, ssa_op->op1_def, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { return 0; } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 60c8ae8bd8ed1..eed23f71fea41 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2324,9 +2324,17 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin #endif break; case ZEND_INIT_DYNAMIC_CALL: - if (orig_op2_type == IS_OBJECT && op2_ce == zend_ce_closure) { - ADD_OP2_TRACE_GUARD(); +#ifdef HAVE_FFI + if (orig_op2_type != IS_OBJECT + || (op2_ce != zend_ce_closure && !op2_ffi_type)) { + break; + } +#else + if (orig_op2_type != IS_OBJECT || op2_ce != zend_ce_closure) { + break; } +#endif + ADD_OP2_TRACE_GUARD(); break; case ZEND_SEND_ARRAY: case ZEND_SEND_UNPACK: From ea75ce113faf4aed1672e586cf718af4dce0f5cd Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 31 Oct 2024 16:37:32 +0300 Subject: [PATCH 086/101] cleanup --- ext/ffi/ffi.c | 267 ++++++++++++++++++++++------------------------ ext/ffi/php_ffi.h | 2 + 2 files changed, 132 insertions(+), 137 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index c663e13b2ddc6..a9f625e785eab 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -356,32 +356,6 @@ static ffi_type *zend_ffi_get_type(zend_ffi_type *type) /* {{{ */ } /* }}} */ -static zend_ffi_cdata* zend_ffi_cdata_create(void *ptr, zend_ffi_type *type) /* {{{ */ -{ - zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); - - zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce); - cdata->std.handlers = - (type->kind < ZEND_FFI_TYPE_POINTER) ? - &zend_ffi_cdata_value_handlers : - &zend_ffi_cdata_handlers; - cdata->type = type; - cdata->flags = 0; - cdata->ptr = ptr; - return cdata; -} -/* }}} */ - -static zend_ffi_ctype* zend_ffi_ctype_create(zend_ffi_type *type) /* {{{ */ -{ - zend_ffi_ctype *ctype = emalloc(sizeof(zend_ffi_ctype)); - - zend_ffi_object_init(&ctype->std, zend_ffi_ctype_ce); - ctype->type = type; - return ctype; -} -/* }}} */ - static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */ { zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); @@ -2903,6 +2877,119 @@ static void *dlsym_loaded(char *symbol) # define DL_FETCH_SYMBOL(h, s) (h == NULL ? dlsym_loaded(s) : GetProcAddress(h, s)) #endif +static zend_ffi_cdata* zend_ffi_cdata_create(void *ptr, zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); + + zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce); + cdata->std.handlers = + (type->kind < ZEND_FFI_TYPE_POINTER) ? + &zend_ffi_cdata_value_handlers : + &zend_ffi_cdata_handlers; + cdata->type = type; + cdata->flags = 0; + cdata->ptr = ptr; + return cdata; +} +/* }}} */ + +static zend_ffi_ctype* zend_ffi_ctype_create(zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_ctype *ctype = emalloc(sizeof(zend_ffi_ctype)); + + zend_ffi_object_init(&ctype->std, zend_ffi_ctype_ce); + ctype->type = type; + return ctype; +} +/* }}} */ + +static void zend_ffi_type_print(FILE *f, const zend_ffi_type *type) /* {{{ */ +{ + zend_ffi_ctype_name_buf buf; + + buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4); + if (!zend_ffi_ctype_name(&buf, ZEND_FFI_TYPE(type))) { + } else { + fwrite(buf.start, buf.end - buf.start, 1, f); + } +} + +static bool zend_ffi_cache_type_get(zend_string *type_def, zend_ffi_dcl *dcl) /* {{{ */ +{ + if (ffi_api.cache_type_get) { + zend_ffi_dcl *cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)); + + if (cached_dcl) { + memcpy(dcl, cached_dcl, sizeof(zend_ffi_dcl)); + return true; + } + } + return false; +} +/* }}} */ + +static void zend_ffi_cache_type_add(zend_string *type_def, zend_ffi_dcl *dcl) /* {{{ */ +{ + if (ffi_api.cache_type_add) { + zend_ffi_dcl *cached_dcl = ffi_api.cache_type_add(type_def, dcl, FFI_G(symbols)); + + if (cached_dcl) { + if (ZEND_FFI_TYPE_IS_OWNED(dcl->type)) { + _zend_ffi_type_dtor(dcl->type); + } + memcpy(dcl, cached_dcl, sizeof(zend_ffi_dcl)); + } + } +} +/* }}} */ + +static zend_ffi* zend_ffi_cache_scope_get(zend_string *code, DL_HANDLE handle) /* {{{ */ +{ + if (ffi_api.cache_scope_get) { + zend_ffi_scope *scope = ffi_api.cache_scope_get(code); + + if (scope) { + zend_ffi *ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); + ffi->lib = handle; + ffi->symbols = scope->symbols; + ffi->tags = scope->tags; + ffi->persistent = true; + return ffi; + } + } + + return NULL; +} +/* }}} */ + +static bool zend_ffi_cache_scope_add(zend_string *code) /* {{{ */ +{ + if (ffi_api.cache_scope_add) { + zend_ffi_scope scope, *cached_scope; + + scope.symbols = FFI_G(symbols); + scope.tags = FFI_G(tags); + cached_scope = ffi_api.cache_scope_add(code, &scope); + if (cached_scope) { + if (FFI_G(symbols)) { + zend_hash_destroy(FFI_G(symbols)); + efree(FFI_G(symbols)); + FFI_G(symbols) = NULL; + } + if (FFI_G(tags)) { + zend_hash_destroy(FFI_G(tags)); + efree(FFI_G(tags)); + FFI_G(tags) = NULL; + } + FFI_G(symbols) = cached_scope->symbols; + FFI_G(tags) = cached_scope->tags; + return true; + } + } + return false; +} +/* }}} */ + ZEND_METHOD(FFI, cdef) /* {{{ */ { zend_string *code = NULL; @@ -2949,16 +3036,9 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ FFI_G(tags) = NULL; if (code && ZSTR_LEN(code)) { - if (ffi_api.cache_scope_get) { - zend_ffi_scope *scope = ffi_api.cache_scope_get(code); - if (scope) { - ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); - ffi->lib = handle; - ffi->symbols = scope->symbols; - ffi->tags = scope->tags; - ffi->persistent = true; - RETURN_OBJ(&ffi->std); - } + ffi = zend_ffi_cache_scope_get(code, handle); + if (ffi) { + RETURN_OBJ(&ffi->std); } /* Parse C definitions */ @@ -3004,28 +3084,7 @@ ZEND_METHOD(FFI, cdef) /* {{{ */ } ZEND_HASH_FOREACH_END(); } - if (ffi_api.cache_scope_add) { - zend_ffi_scope scope, *cached_scope; - - scope.symbols = FFI_G(symbols); - scope.tags = FFI_G(tags); - cached_scope = ffi_api.cache_scope_add(code, &scope); - if (cached_scope) { - if (FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - if (FFI_G(tags)) { - zend_hash_destroy(FFI_G(tags)); - efree(FFI_G(tags)); - FFI_G(tags) = NULL; - } - FFI_G(symbols) = cached_scope->symbols; - FFI_G(tags) = cached_scope->tags; - persistent = true; - } - } + persistent = zend_ffi_cache_scope_add(code); } ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); @@ -3276,14 +3335,9 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ close(fd); ZSTR_VAL(code)[code_size] = 0; - if (!preload && ffi_api.cache_scope_get) { - zend_ffi_scope *scope = ffi_api.cache_scope_get(code); - if (scope) { - ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); - ffi->lib = handle; - ffi->symbols = scope->symbols; - ffi->tags = scope->tags; - ffi->persistent = true; + if (!preload) { + ffi = zend_ffi_cache_scope_get(code, handle); + if (ffi) { return ffi; } } @@ -3487,27 +3541,7 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ ffi->tags = scope->tags; ffi->persistent = 1; } else { - if (ffi_api.cache_scope_add) { - zend_ffi_scope scope, *cached_scope; - - scope.symbols = FFI_G(symbols); - scope.tags = FFI_G(tags); - cached_scope = ffi_api.cache_scope_add(code, &scope); - if (cached_scope) { - if (FFI_G(symbols)) { - zend_hash_destroy(FFI_G(symbols)); - efree(FFI_G(symbols)); - FFI_G(symbols) = NULL; - } - if (FFI_G(tags)) { - zend_hash_destroy(FFI_G(tags)); - efree(FFI_G(tags)); - FFI_G(tags) = NULL; - } - FFI_G(symbols) = cached_scope->symbols; - FFI_G(tags) = cached_scope->tags; - } - } + persistent = zend_ffi_cache_scope_add(code); ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce); ffi->lib = handle; @@ -3800,7 +3834,6 @@ ZEND_METHOD(FFI, new) /* {{{ */ if (type_def) { zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT; - zend_ffi_dcl *cached_dcl; if (!is_static_call) { zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This)); @@ -3815,9 +3848,8 @@ ZEND_METHOD(FFI, new) /* {{{ */ FFI_G(default_type_attr) = 0; - if (ffi_api.cache_type_get - && (cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)))) { - memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); + if (zend_ffi_cache_type_get(type_def, &dcl)) { + /* pass */ } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { @@ -3836,15 +3868,7 @@ ZEND_METHOD(FFI, new) /* {{{ */ zend_ffi_tags_cleanup(&dcl); } - if (zend_ffi_api->cache_type_add) { - cached_dcl = zend_ffi_api->cache_type_add(type_def, &dcl, FFI_G(symbols)); - if (cached_dcl) { - if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { - _zend_ffi_type_dtor(dcl.type); - } - memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); - } - } + zend_ffi_cache_type_add(type_def, &dcl); if (clean_symbols && FFI_G(symbols)) { zend_hash_destroy(FFI_G(symbols)); @@ -3965,7 +3989,6 @@ ZEND_METHOD(FFI, cast) /* {{{ */ if (type_def) { zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT; - zend_ffi_dcl *cached_dcl; if (!is_static_call) { zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This)); @@ -3980,9 +4003,8 @@ ZEND_METHOD(FFI, cast) /* {{{ */ FFI_G(default_type_attr) = 0; - if (ffi_api.cache_type_get - && (cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)))) { - memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); + if (zend_ffi_cache_type_get(type_def, &dcl)) { + /* pass */ } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { @@ -4001,15 +4023,7 @@ ZEND_METHOD(FFI, cast) /* {{{ */ zend_ffi_tags_cleanup(&dcl); } - if (ffi_api.cache_type_add) { - cached_dcl = ffi_api.cache_type_add(type_def, &dcl, FFI_G(symbols)); - if (cached_dcl) { - if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { - _zend_ffi_type_dtor(dcl.type); - } - memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); - } - } + zend_ffi_cache_type_add(type_def, &dcl); if (clean_symbols && FFI_G(symbols)) { zend_hash_destroy(FFI_G(symbols)); @@ -4138,7 +4152,6 @@ ZEND_METHOD(FFI, type) /* {{{ */ { zend_ffi_ctype *ctype; zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT; - zend_ffi_dcl *cached_dcl; zend_string *type_def; bool is_static_call = Z_TYPE(EX(This)) != IS_OBJECT; @@ -4167,9 +4180,8 @@ ZEND_METHOD(FFI, type) /* {{{ */ FFI_G(default_type_attr) = 0; - if (ffi_api.cache_type_get - && (cached_dcl = ffi_api.cache_type_get(type_def, FFI_G(symbols)))) { - memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); + if (zend_ffi_cache_type_get(type_def, &dcl)) { + /* pass */ } else if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) { zend_ffi_type_dtor(dcl.type); if (clean_tags && FFI_G(tags)) { @@ -4188,15 +4200,7 @@ ZEND_METHOD(FFI, type) /* {{{ */ zend_ffi_tags_cleanup(&dcl); } - if (ffi_api.cache_type_add) { - cached_dcl = ffi_api.cache_type_add(type_def, &dcl, FFI_G(symbols)); - if (cached_dcl) { - if (ZEND_FFI_TYPE_IS_OWNED(dcl.type)) { - _zend_ffi_type_dtor(dcl.type); - } - memcpy(&dcl, cached_dcl, sizeof(zend_ffi_dcl)); - } - } + zend_ffi_cache_type_add(type_def, &dcl); if (clean_symbols && FFI_G(symbols)) { zend_hash_destroy(FFI_G(symbols)); @@ -5324,17 +5328,6 @@ static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */ } /* }}} */ -static void zend_ffi_type_print(FILE *f, const zend_ffi_type *type) /* {{{ */ -{ - zend_ffi_ctype_name_buf buf; - - buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4); - if (!zend_ffi_ctype_name(&buf, ZEND_FFI_TYPE(type))) { - } else { - fwrite(buf.start, buf.end - buf.start, 1, f); - } -} - ZEND_INI_BEGIN() ZEND_INI_ENTRY_EX("ffi.enable", "preload", ZEND_INI_SYSTEM, OnUpdateFFIEnable, zend_ffi_enable_displayer_cb) STD_ZEND_INI_ENTRY("ffi.preload", NULL, ZEND_INI_SYSTEM, OnUpdateString, preload, zend_ffi_globals, ffi_globals) diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index 75549d9356c45..c2e4bd8b24d77 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -418,11 +418,13 @@ struct _zend_ffi_api { zend_class_entry *cdata_ce; zend_class_entry *ctype_ce; + /* ext/ffi interface for ext/opcache */ zend_ffi_cdata* (*cdata_create)(void *ptr, zend_ffi_type *type); zend_ffi_ctype* (*ctype_create)(zend_ffi_type *type); void (*type_print)(FILE *f, const zend_ffi_type *type); bool (*is_compatible_type)(zend_ffi_type *dst_type, zend_ffi_type *src_type); + /* ext/opcache interface for ext/ffi */ zend_ffi_dcl* (*cache_type_get)(zend_string *str, void *context); zend_ffi_dcl* (*cache_type_add)(zend_string *str, zend_ffi_dcl *dcl, void *context); zend_ffi_scope* (*cache_scope_get)(zend_string *str); From c3ec5a7a9c4f43dc4fa35cf45cb13b89cd52a3e6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 31 Oct 2024 16:55:26 +0300 Subject: [PATCH 087/101] cleanup --- ext/opcache/jit/zend_jit_trace.c | 4 ---- ext/opcache/jit/zend_jit_vm_helpers.c | 1 - 2 files changed, 5 deletions(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index eed23f71fea41..f5ea7cda74b06 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2064,7 +2064,6 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin case ZEND_FETCH_DIM_FUNC_ARG: if (!frame || !frame->call -//??? || !frame->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(frame->call)) { break; } @@ -2287,7 +2286,6 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin case ZEND_FETCH_OBJ_FUNC_ARG: if (!frame || !frame->call -//??? || !frame->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(frame->call)) { break; } @@ -6267,7 +6265,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_FETCH_DIM_FUNC_ARG: if (!JIT_G(current_frame) || !JIT_G(current_frame)->call -//??? || !JIT_G(current_frame)->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { break; } @@ -6507,7 +6504,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_FETCH_OBJ_FUNC_ARG: if (!JIT_G(current_frame) || !JIT_G(current_frame)->call -//??? || !JIT_G(current_frame)->call->func || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { break; } diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index aefb1fa07b576..060de226492cd 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1252,7 +1252,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, func = NULL; ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_MEGAMORPHIC); } - if (!func) { info = ZEND_JIT_TRACE_NUM_ARGS_INFO(ZEND_CALL_NUM_ARGS(EX(call))); } From 082d01deac498349e68ab1131b88d534faabe45d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 31 Oct 2024 21:39:02 +0300 Subject: [PATCH 088/101] JIT for FFI::memcpy(), FFI::memcmp() and FFI::memset() --- ext/opcache/jit/zend_jit_ir_ffi.c | 237 ++++++++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 24 ++- 2 files changed, 260 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 26aec3163d65d..425765a82ec2b 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -178,6 +178,141 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, SET_STACK_TYPE(stack, 1, IS_LONG, 0); SET_STACK_REF_EX(stack, 1, ref, 0); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMCPY) { + if (opline->op2.num == 1) { + ZEND_ASSERT(op1_ffi_type); + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } else if (opline->op2.num == 2) { + if (op1_ffi_type) { + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, arg_flags); + } else { + ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING); + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 1, IS_STRING, 0); + SET_STACK_REF_EX(stack, 1, ref, arg_flags); + } + } else { + ZEND_ASSERT(opline->op2.num == 3); + ZEND_ASSERT(op1_info == MAY_BE_LONG); + ref = jit_Z_LVAL(jit, op1_addr); + SET_STACK_TYPE(stack, 2, IS_LONG, 0); + SET_STACK_REF_EX(stack, 2, ref, 0); + } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMCMP) { + if (opline->op2.num == 1) { + if (op1_ffi_type) { + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } else { + ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING); + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_STRING, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } + } else if (opline->op2.num == 2) { + if (op1_ffi_type) { + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, arg_flags); + } else { + ZEND_ASSERT((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_STRING); + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 1, IS_STRING, 0); + SET_STACK_REF_EX(stack, 1, ref, arg_flags); + } + } else { + ZEND_ASSERT(opline->op2.num == 3); + ZEND_ASSERT(op1_info == MAY_BE_LONG); + ref = jit_Z_LVAL(jit, op1_addr); + SET_STACK_TYPE(stack, 2, IS_LONG, 0); + SET_STACK_REF_EX(stack, 2, ref, 0); + } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMSET) { + if (opline->op2.num == 1) { + ZEND_ASSERT(op1_ffi_type); + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + arg_flags |= ZREG_FFI_PTR_LOAD; + } + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } else if (opline->op2.num == 2) { + ZEND_ASSERT(op1_info == MAY_BE_LONG); + ref = jit_Z_LVAL(jit, op1_addr); + SET_STACK_TYPE(stack, 1, IS_LONG, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + } else { + ZEND_ASSERT(opline->op2.num == 3); + ZEND_ASSERT(op1_info == MAY_BE_LONG); + ref = jit_Z_LVAL(jit, op1_addr); + SET_STACK_TYPE(stack, 2, IS_LONG, 0); + SET_STACK_REF_EX(stack, 2, ref, 0); + } } else { ZEND_UNREACHABLE(); } @@ -494,6 +629,108 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_stringl), jit_ZVAL_ADDR(jit, res_addr), ref, STACK_REF(stack, 1)); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMCPY) { + ir_ref ref2; + + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_PTR(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_PTR_LOAD) { + ref = ir_LOAD_A(ref); + } + + if (STACK_TYPE(stack, 1) == IS_OBJECT) { + ref2 = STACK_REF(stack, 1); + if (STACK_FLAGS(stack, 1) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref2 = zend_jit_gc_deref(jit, ref2); + } + ref2 = jit_FFI_CDATA_PTR(jit, ref2); + if (STACK_FLAGS(stack, 1) & ZREG_FFI_PTR_LOAD) { + ref2 = ir_LOAD_A(ref2); + } + } else { + ZEND_ASSERT(STACK_TYPE(stack, 1) == IS_STRING); + ref2 = STACK_REF(stack, 1); + if (STACK_FLAGS(stack, 1) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref2 = zend_jit_gc_deref(jit, ref2); + } + ref2 = ir_ADD_OFFSET(ref2, offsetof(zend_string, val)); + } + + ir_CALL_3(IR_VOID, ir_CONST_FUNC(memcpy), ref, ref2, STACK_REF(stack, 2)); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); + } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMCMP) { + ir_ref ref2; + + if (STACK_TYPE(stack, 0) == IS_OBJECT) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_PTR(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_PTR_LOAD) { + ref = ir_LOAD_A(ref); + } + } else { + ZEND_ASSERT(STACK_TYPE(stack, 0) == IS_STRING); + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = ir_ADD_OFFSET(ref, offsetof(zend_string, val)); + } + + if (STACK_TYPE(stack, 1) == IS_OBJECT) { + ref2 = STACK_REF(stack, 1); + if (STACK_FLAGS(stack, 1) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref2 = zend_jit_gc_deref(jit, ref2); + } + ref2 = jit_FFI_CDATA_PTR(jit, ref2); + if (STACK_FLAGS(stack, 1) & ZREG_FFI_PTR_LOAD) { + ref2 = ir_LOAD_A(ref2); + } + } else { + ZEND_ASSERT(STACK_TYPE(stack, 1) == IS_STRING); + ref2 = STACK_REF(stack, 1); + if (STACK_FLAGS(stack, 1) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref2 = zend_jit_gc_deref(jit, ref2); + } + ref2 = ir_ADD_OFFSET(ref2, offsetof(zend_string, val)); + } + + ref = ir_CALL_3(IR_I32, ir_CONST_FUNC(memcmp), ref, ref2, STACK_REF(stack, 2)); + if (res_addr) { + /* (-1, 0, 1) = (ret > 0) - (ret < 0) */ + ref = ir_SUB_L(ir_SEXT_L(ir_GT(ref, ir_CONST_I32(0))), ir_SEXT_L(ir_LT(ref, ir_CONST_I32(0)))); + jit_set_Z_LVAL(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); + } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMSET) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ref = jit_FFI_CDATA_PTR(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_PTR_LOAD) { + ref = ir_LOAD_A(ref); + } + + ir_CALL_3(IR_VOID, ir_CONST_FUNC(memset), ref, STACK_REF(stack, 1), STACK_REF(stack, 2)); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); + } } else { ZEND_UNREACHABLE(); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index f5ea7cda74b06..ca9d31ccd49ac 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5849,7 +5849,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (JIT_G(current_frame) && JIT_G(current_frame)->call && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { - if (!zend_jit_ffi_do_call(&ctx, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) { + if (!zend_jit_ffi_do_call(&ctx, opline, op_array, ssa, ssa_op, + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0 )) { goto jit_failure; } goto done; @@ -7034,6 +7035,27 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "memcpy") + && opline->extended_value == 3) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_MEMCPY; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "memcmp") + && opline->extended_value == 3) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_MEMCMP; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "memset") + && opline->extended_value == 3) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_MEMSET; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; } } } From 3efb86c8f43424a281d9fa411ad0f717e02fc8d3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 1 Nov 2024 11:22:09 +0300 Subject: [PATCH 089/101] Add test for FETCH_DIM_W/VAR --- .../jit/044_struct_write_nested_array2.phpt | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 ext/ffi/tests/jit/044_struct_write_nested_array2.phpt diff --git a/ext/ffi/tests/jit/044_struct_write_nested_array2.phpt b/ext/ffi/tests/jit/044_struct_write_nested_array2.phpt new file mode 100644 index 0000000000000..be65d7f19f462 --- /dev/null +++ b/ext/ffi/tests/jit/044_struct_write_nested_array2.phpt @@ -0,0 +1,96 @@ +--TEST-- +FFI/JIT 044: Write Struct (nested array - FETCH_DIM_W/VAR) +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +;opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=0 +opcache.jit_hot_return=0 +opcache.jit_hot_side_exit=0 +;opcache.jit_debug=0x180005 +--FILE-- +new("struct {int32_t a[5][5]; int32_t b;}"); + for ($i = 0; $i < 5; $i++) { + $a->a[$i][$i] = $i; + } + var_dump($a); +} +test(); +?> +--EXPECTF-- +object(FFI\CData:struct )#%d (2) { + ["a"]=> + object(FFI\CData:int32_t[5][5])#%d (5) { + [0]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(0) + [2]=> + int(0) + [3]=> + int(0) + [4]=> + int(0) + } + [1]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(1) + [2]=> + int(0) + [3]=> + int(0) + [4]=> + int(0) + } + [2]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(0) + [2]=> + int(2) + [3]=> + int(0) + [4]=> + int(0) + } + [3]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(0) + [2]=> + int(0) + [3]=> + int(3) + [4]=> + int(0) + } + [4]=> + object(FFI\CData:int32_t[5])#%d (5) { + [0]=> + int(0) + [1]=> + int(0) + [2]=> + int(0) + [3]=> + int(0) + [4]=> + int(4) + } + } + ["b"]=> + int(0) +} From c9853b663930fa7b2c9ed6bcc1a9596afd3fd146 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 1 Nov 2024 11:50:23 +0300 Subject: [PATCH 090/101] JIT for FFI::sizeof() and FFI::alignof() --- ext/opcache/jit/zend_jit.h | 1 + ext/opcache/jit/zend_jit_ir_ffi.c | 37 +++++++++++++++++++++++++++---- ext/opcache/jit/zend_jit_trace.c | 9 +++----- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index c423ab426160a..0d2f6c1b7575c 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -185,6 +185,7 @@ void zend_jit_restart(void); #define ZREG_FFI_PTR_LOAD (1<<3) #define ZREG_FFI_ZVAL_DTOR (1<<4) #define ZREG_FFI_ZVAL_DEREF (1<<5) +#define ZREG_FFI_CTYPE (1<<6) #define ZREG_NONE -1 diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 425765a82ec2b..3a4eeb0f0205e 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -26,6 +26,11 @@ static ir_ref jit_FFI_CDATA_TYPE(zend_jit_ctx *jit, ir_ref obj_ref) return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_cdata, type))); } +static ir_ref jit_FFI_CTYPE_TYPE(zend_jit_ctx *jit, ir_ref obj_ref) +{ + return ir_LOAD_A(ir_ADD_OFFSET(obj_ref, offsetof(zend_ffi_ctype, type))); +} + static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, const zend_op *opline, zend_ssa *ssa, @@ -119,6 +124,7 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr op1_def_addr, + zend_class_entry *op1_ce, zend_ffi_type *op1_ffi_type) { zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; @@ -130,8 +136,6 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, if (TRACE_FRAME_FFI_FUNC(call)) { if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR - || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ALIGNOF - || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_SIZEOF || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPEOF || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) { ZEND_ASSERT(opline->op2.num == 1); @@ -151,6 +155,23 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, ref = jit_Z_PTR(jit, op1_addr); SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ALIGNOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_SIZEOF) { + ZEND_ASSERT(opline->op2.num == 1); + + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + } + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ZEND_ASSERT(op1_ffi_type || op1_ce == zend_ffi_api->ctype_ce); + if (!op1_ffi_type && op1_ce == zend_ffi_api->ctype_ce) { + arg_flags |= ZREG_FFI_CTYPE; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_STRING) { if (opline->op2.num == 1) { ZEND_ASSERT(op1_ffi_type); @@ -564,7 +585,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, // TODO: try to remove this dereference ??? ref = zend_jit_gc_deref(jit, ref); } - ref = jit_FFI_CDATA_TYPE(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_CTYPE) { + ref = jit_FFI_CTYPE_TYPE(jit, ref); + } else { + ref = jit_FFI_CDATA_TYPE(jit, ref); + } // TODO: type flags ??? ref = ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_ffi_type, align))); if (sizeof(void*) == 8) { @@ -578,7 +603,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, // TODO: try to remove this dereference ??? ref = zend_jit_gc_deref(jit, ref); } - ref = jit_FFI_CDATA_TYPE(jit, ref); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_CTYPE) { + ref = jit_FFI_CTYPE_TYPE(jit, ref); + } else { + ref = jit_FFI_CDATA_TYPE(jit, ref); + } // TODO: type flags ??? ref = ir_LOAD_U32(ir_ADD_OFFSET(ref, offsetof(zend_ffi_type, size))); if (sizeof(void*) == 8) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index ca9d31ccd49ac..f2d91887e0d7f 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5689,7 +5689,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && JIT_G(current_frame)->call && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { if (!zend_jit_ffi_send_val(&ctx, opline, op_array, ssa, ssa_op, - op1_info, OP1_REG_ADDR(), 0, op1_ffi_type)) { + op1_info, OP1_REG_ADDR(), 0, op1_ce, op1_ffi_type)) { goto jit_failure; } goto done; @@ -5721,7 +5721,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && JIT_G(current_frame)->call && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { if (!zend_jit_ffi_send_val(&ctx, opline, op_array, ssa, ssa_op, - op1_info, OP1_REG_ADDR(), 0, op1_ffi_type)) { + op1_info, OP1_REG_ADDR(), 0, op1_ce, op1_ffi_type)) { goto jit_failure; } goto done; @@ -5763,7 +5763,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par && JIT_G(current_frame)->call && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { if (!zend_jit_ffi_send_val(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, op1_def_addr, op1_ffi_type)) { + op1_info, op1_addr, op1_def_addr, op1_ce, op1_ffi_type)) { goto jit_failure; } goto done; @@ -7004,8 +7004,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; -#if 0 -// TODO: add support for FFI::CType argument ??? } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "alignof") && opline->extended_value == 1) { @@ -7020,7 +7018,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; -#endif } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "typeof") && opline->extended_value == 1) { From 474866ea67d859c7167535b4e7e20c75703a777d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Sat, 2 Nov 2024 14:35:17 +0300 Subject: [PATCH 091/101] JIT for FETCH_DIM_W/VAR with FFI::CData op1 --- ext/opcache/jit/zend_jit_trace.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index f2d91887e0d7f..915c74b0387b9 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2152,6 +2152,9 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin // case ZEND_FETCH_DIM_UNSET: case ZEND_FETCH_LIST_W: if (opline->op1_type != IS_CV +#ifdef HAVE_FFI + && !op1_ffi_type +#endif && (orig_op1_type == IS_UNKNOWN || !(orig_op1_type & IS_TRACE_INDIRECT))) { break; @@ -6350,6 +6353,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par // case ZEND_FETCH_DIM_UNSET: case ZEND_FETCH_LIST_W: if (opline->op1_type != IS_CV +#ifdef HAVE_FFI + && !op1_ffi_type +#endif && (orig_op1_type == IS_UNKNOWN || !(orig_op1_type & IS_TRACE_INDIRECT))) { break; @@ -6365,8 +6371,6 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) { goto jit_failure; } - } else { - break; } } if (orig_op1_type != IS_UNKNOWN From 4f0ee233e118907a99fbda93c4b4d0cc165a3669 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 5 Nov 2024 12:15:37 +0300 Subject: [PATCH 092/101] Record FFI::CTypes --- ext/opcache/jit/zend_jit_ir_ffi.c | 4 +- ext/opcache/jit/zend_jit_vm_helpers.c | 86 ++++++++++++--------------- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 3a4eeb0f0205e..fc7c33e7e724b 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -165,8 +165,8 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, if (op1_info & MAY_BE_REF) { arg_flags |= ZREG_FFI_ZVAL_DEREF; } - ZEND_ASSERT(op1_ffi_type || op1_ce == zend_ffi_api->ctype_ce); - if (!op1_ffi_type && op1_ce == zend_ffi_api->ctype_ce) { + ZEND_ASSERT(op1_ffi_type); + if (op1_ce == zend_ffi_api->ctype_ce) { arg_flags |= ZREG_FFI_CTYPE; } ref = jit_Z_PTR(jit, op1_addr); diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 060de226492cd..41d8c624c344b 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -559,6 +559,29 @@ static int zend_jit_trace_subtrace(zend_jit_trace_rec *trace_buffer, int start, return idx + (end - start); } +#ifdef HAVE_FFI +static zend_ffi_type* zend_jit_trace_ffi_persistent_type(zend_ffi_type *ffi_type) +{ + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { + return ffi_type; + } + } else { + ffi_type = ZEND_FFI_TYPE(ffi_type); + if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { + ffi_type = ffi_type->pointer.type; + if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { + if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { + /* OWNED flag means POINTER TO */ + return ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); + } + } + } + } + return NULL; +} +#endif + /* * Trace Linking Rules * =================== @@ -719,22 +742,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (zend_ffi_api && ce1 == zend_ffi_api->cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); zend_ffi_type *ffi_type = cdata->type; - if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { - if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { - op1_ffi_type = ffi_type; - } - } else { - ffi_type = ZEND_FFI_TYPE(ffi_type); - if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - ffi_type = ffi_type->pointer.type; - if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { - if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { - /* OWNED flag means POINTER TO */ - op1_ffi_type = ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); - } - } - } - } + op1_ffi_type = zend_jit_trace_ffi_persistent_type(ffi_type); + } else if (zend_ffi_api && ce1 == zend_ffi_api->ctype_ce) { + zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv); + zend_ffi_type *ffi_type = ctype->type; + op1_ffi_type = zend_jit_trace_ffi_persistent_type(ffi_type); } else if (zend_ffi_api && ce1 == zend_ffi_api->scope_ce) { zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(zv); if (ffi->persistent && ffi->symbols) { @@ -795,22 +807,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (zend_ffi_api && ce2 == zend_ffi_api->cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); zend_ffi_type *ffi_type = cdata->type; - if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { - if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { - op2_ffi_type = ffi_type; - } - } else { - ffi_type = ZEND_FFI_TYPE(ffi_type); - if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - ffi_type = ffi_type->pointer.type; - if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { - if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { - /* OWNED flag means POINTER TO */ - op2_ffi_type = ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); - } - } - } - } + op2_ffi_type = zend_jit_trace_ffi_persistent_type(ffi_type); + } else if (zend_ffi_api && ce2 == zend_ffi_api->ctype_ce) { + zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv); + zend_ffi_type *ffi_type = ctype->type; + op2_ffi_type = zend_jit_trace_ffi_persistent_type(ffi_type); } #endif } @@ -845,22 +846,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (zend_ffi_api && ce3 == zend_ffi_api->cdata_ce) { zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); zend_ffi_type *ffi_type = cdata->type; - if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { - if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { - op3_ffi_type = ffi_type; - } - } else { - ffi_type = ZEND_FFI_TYPE(ffi_type); - if (ffi_type->kind == ZEND_FFI_TYPE_POINTER) { - ffi_type = ffi_type->pointer.type; - if (!ZEND_FFI_TYPE_IS_OWNED(ffi_type)) { - if (ffi_type->attr & ZEND_FFI_ATTR_PERSISTENT) { - /* OWNED flag means POINTER TO */ - op3_ffi_type = ZEND_FFI_TYPE_MAKE_OWNED(ffi_type); - } - } - } - } + op3_ffi_type = zend_jit_trace_ffi_persistent_type(ffi_type); + } else if (zend_ffi_api && ce3 == zend_ffi_api->ctype_ce) { + zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv); + zend_ffi_type *ffi_type = ctype->type; + op3_ffi_type = zend_jit_trace_ffi_persistent_type(ffi_type); } #endif } From 6affe2cb349b579884dfdb7e21655ee6818ccb94 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 5 Nov 2024 12:30:36 +0300 Subject: [PATCH 093/101] Add test for FFI::type() --- ext/ffi/tests/jit/054_typeof.phpt | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 ext/ffi/tests/jit/054_typeof.phpt diff --git a/ext/ffi/tests/jit/054_typeof.phpt b/ext/ffi/tests/jit/054_typeof.phpt deleted file mode 100644 index 186a63576d5e3..0000000000000 --- a/ext/ffi/tests/jit/054_typeof.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -FFI/JIT 054: FFI::typeof() ---EXTENSIONS-- -ffi ---INI-- -ffi.enable=1 -;opcache.jit=tracing -opcache.jit_hot_loop=1 -opcache.jit_hot_func=0 -opcache.jit_hot_return=0 -opcache.jit_hot_side_exit=0 -;opcache.jit_debug=0x180005 ---FILE-- -new("int[10]"); - $x[0] = 42; - for ($i = 0; $i < 5; $i++) { - $ret = FFI::typeof($x); - } - var_dump($ret); -} -test(); -?> ---EXPECT-- -object(FFI\CType:int32_t[10])#3 (0) { -} From 850a281d4e77e8d0f0f0b1421399ca9e969e19c0 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 5 Nov 2024 13:29:26 +0300 Subject: [PATCH 094/101] JIT for FFI::type() --- ext/opcache/jit/zend_jit_internal.h | 1 + ext/opcache/jit/zend_jit_ir_ffi.c | 7 +++ ext/opcache/jit/zend_jit_trace.c | 67 +++++++++++++++++++++-------- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index 5b9ad73a84226..fc0e672f0e1b3 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -590,6 +590,7 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_FFI_FUNC_MEMSET 0x0000b000 #define TRACE_FRAME_FFI_FUNC_STRING 0x0000c000 #define TRACE_FRAME_FFI_FUNC_IS_NULL 0x0000d000 +#define TRACE_FRAME_FFI_FUNC_TYPE 0x0000e000 #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index fc7c33e7e724b..5e1409c95cbc0 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -334,6 +334,8 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, SET_STACK_TYPE(stack, 2, IS_LONG, 0); SET_STACK_REF_EX(stack, 2, ref, 0); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { + /* nothing to do */ } else { ZEND_UNREACHABLE(); } @@ -760,6 +762,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, if (res_addr) { jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { + ref = ir_CONST_ADDR(type); + ref = ir_CALL_1(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->ctype_create), ref); + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } else { ZEND_UNREACHABLE(); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 915c74b0387b9..7871628341b10 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6943,26 +6943,57 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } #ifdef HAVE_FFI if (op1_ffi_symbols) { - zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, - Z_STR_P(RT_CONSTANT(opline, opline->op2))); - if (sym - && sym->kind == ZEND_FFI_SYM_FUNC - && zend_jit_ffi_supported_func(ZEND_FFI_TYPE(sym->type))) { - ir_ref ffi_func_ref; - - if (!ffi_info) { - ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); + + if (zend_string_equals_literal_ci(name, "type")) { + if (opline->extended_value == 1 + && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL + && (p+2)->op == ZEND_JIT_TRACE_VM + && ((p+2)->opline->opcode == ZEND_SEND_VAL + || (p+2)->opline->opcode == ZEND_SEND_VAL_EX) + && (p+2)->opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT((p+2)->opline, (p+2)->opline->op1); + + if (Z_TYPE_P(zv) == IS_STRING) { + zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), op1_ffi_symbols); + + if (dcl) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, + ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_TYPE; + frame_ffi_func_type = dcl->type; + frame_ffi_func_ref = IR_UNUSED; + goto done; + } + } } - if (!zend_jit_ffi_init_call_sym(&ctx, opline, op_array, ssa, ssa_op, - op1_info, op1_addr, - sym, - op1_ffi_symbols, ffi_info, &ffi_func_ref)) { - goto jit_failure; + } else { + zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, name); + + if (sym + && sym->kind == ZEND_FFI_SYM_FUNC + && zend_jit_ffi_supported_func(ZEND_FFI_TYPE(sym->type))) { + ir_ref ffi_func_ref; + + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_init_call_sym(&ctx, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, + sym, + op1_ffi_symbols, ffi_info, &ffi_func_ref)) { + goto jit_failure; + } + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_func_type = ZEND_FFI_TYPE(sym->type); + frame_ffi_func_ref = ffi_func_ref; + goto done; } - frame_flags = TRACE_FRAME_MASK_FFI; - frame_ffi_func_type = ZEND_FFI_TYPE(sym->type); - frame_ffi_func_ref = ffi_func_ref; - goto done; } } #endif From f5b390c68fcbcae3ca9ffa276244af2257b4cd45 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 5 Nov 2024 18:05:00 +0300 Subject: [PATCH 095/101] JIT for FFI::new() with single argument --- ext/opcache/jit/zend_jit_ir_ffi.c | 38 ++++++++++++--- ext/opcache/jit/zend_jit_trace.c | 81 ++++++++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 12 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 5e1409c95cbc0..89eefcb402c80 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -334,7 +334,11 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, SET_STACK_TYPE(stack, 2, IS_LONG, 0); SET_STACK_REF_EX(stack, 2, ref, 0); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW) { + ZEND_ASSERT(opline->op2.num == 1); + /* nothing to do */ } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { + ZEND_ASSERT(opline->op2.num == 1); /* nothing to do */ } else { ZEND_UNREACHABLE(); @@ -762,11 +766,31 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, if (res_addr) { jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW) { + if (res_addr) { +#if ZEND_DEBUG + ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), + ir_CONST_LONG(1), + ir_CONST_ADDR(type->size), + op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL, + ir_CONST_U32(opline ? opline->lineno : 0), + IR_NULL, + ir_CONST_U32(0)); +#else + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size)); +#endif + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); + ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(ZEND_FFI_FLAG_OWNED)); + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { - ref = ir_CONST_ADDR(type); - ref = ir_CALL_1(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->ctype_create), ref); - jit_set_Z_PTR(jit, res_addr, ref); - jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + if (res_addr) { + ref = ir_CONST_ADDR(type); + ref = ir_CALL_1(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->ctype_create), ref); + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } } else { ZEND_UNREACHABLE(); } @@ -987,9 +1011,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } } - if (!TRACE_FRAME_FFI_FUNC(call)) { + if (!TRACE_FRAME_FFI_FUNC(call) + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { func_ref = (intptr_t)(void*)call->ce; - if (!IR_IS_CONST_REF(func_ref)) { + if (func_ref && !IR_IS_CONST_REF(func_ref)) { ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, func_ref)); ir_IF_FALSE(if_not_zero); jit_ZVAL_DTOR(jit, func_ref, MAY_BE_OBJECT, opline); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 7871628341b10..82e5ddb78768a 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6942,10 +6942,72 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } #ifdef HAVE_FFI - if (op1_ffi_symbols) { + if (zend_ffi_api && op1_ce == zend_ffi_api->scope_ce && !ZEND_OBSERVER_ENABLED) { zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); - if (zend_string_equals_literal_ci(name, "type")) { + if (zend_string_equals_literal_ci(name, "new")) { + if (opline->extended_value == 1 + // TODO: support for FFI::new() with 2 and 3 arguments ??? + && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL + && (p+2)->op == ZEND_JIT_TRACE_VM) { + if (((p+2)->opline->opcode == ZEND_SEND_VAL + || (p+2)->opline->opcode == ZEND_SEND_VAL_EX) + && (p+2)->opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT((p+2)->opline, (p+2)->opline->op1); + + if (Z_TYPE_P(zv) == IS_STRING) { + zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), op1_ffi_symbols); + + if (dcl + && !ZEND_FFI_TYPE_IS_OWNED(dcl->type) + && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT) + && dcl->type->size != 0) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, + ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_NEW; + frame_ffi_func_type = dcl->type; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); + } else { + frame_ffi_func_ref = IR_UNUSED; + } + goto done; + } + } + } else if ((p+2)->opline->opcode == ZEND_SEND_VAR_EX + && (p+3)->op == ZEND_JIT_TRACE_OP1_TYPE + && (p+3)->ce == zend_ffi_api->ctype_ce + && (p+4)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + zend_ffi_type *type = (zend_ffi_type*)(p+4)->ptr; + + if (!ZEND_FFI_TYPE_IS_OWNED(type) + && (type->attr & ZEND_FFI_ATTR_PERSISTENT) + && type->size != 0) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, + ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + // TODO: Guard for FFI::CType argument + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_NEW; + frame_ffi_func_type = type; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); + } else { + frame_ffi_func_ref = IR_UNUSED; + } + goto done; + } + } + } + } else if (zend_string_equals_literal_ci(name, "type")) { if (opline->extended_value == 1 && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL && (p+2)->op == ZEND_JIT_TRACE_VM @@ -6957,7 +7019,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (Z_TYPE_P(zv) == IS_STRING) { zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), op1_ffi_symbols); - if (dcl) { + if (dcl + && !ZEND_FFI_TYPE_IS_OWNED(dcl->type) + && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT)) { if (!ffi_info) { ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); } @@ -6967,12 +7031,16 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_TYPE; frame_ffi_func_type = dcl->type; - frame_ffi_func_ref = IR_UNUSED; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); + } else { + frame_ffi_func_ref = IR_UNUSED; + } goto done; } } } - } else { + } else if (op1_ffi_symbols) { zend_ffi_symbol *sym = zend_hash_find_ptr(op1_ffi_symbols, name); if (sym @@ -7019,7 +7087,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } #ifdef HAVE_FFI if (opline->op1_type == IS_CONST - && opline->op2_type == IS_CONST) { + && opline->op2_type == IS_CONST + && !ZEND_OBSERVER_ENABLED) { zval *zv = RT_CONSTANT(opline, opline->op1); if (Z_TYPE_P(zv) == IS_STRING && (zend_string_equals_literal_ci(Z_STR_P(zv), "FFI") From a1adbe79046ac7ed3dd179809dcbc6bc5466a12e Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 6 Nov 2024 13:02:41 +0300 Subject: [PATCH 096/101] JIT for FFI::new() with 2 and 3 arguments --- ext/opcache/jit/zend_jit.c | 62 +++++++++++++++++++++++++ ext/opcache/jit/zend_jit_ir_ffi.c | 60 +++++++++++++++++++----- ext/opcache/jit/zend_jit_trace.c | 76 +++++++------------------------ 3 files changed, 127 insertions(+), 71 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index d4e8a9d098194..53df4664e63e1 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -107,6 +107,68 @@ static bool zend_jit_ffi_supported_func(zend_ffi_type *type) return true; } +static bool zend_jit_is_send_bool_const(const zend_jit_trace_rec *p) +{ + if (p->op == ZEND_JIT_TRACE_VM + && (p->opline->opcode == ZEND_SEND_VAL + || p->opline->opcode == ZEND_SEND_VAL_EX) + && p->opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT(p->opline, p->opline->op1); + + if (Z_TYPE_P(zv) <= IS_STRING) { + return true; + } + } + return false; +} + +static zend_ffi_type* zend_jit_ffi_supported_new(const zend_op *opline, HashTable *ffi_symbols, const zend_jit_trace_rec *p) +{ + zend_ffi_type *type = NULL; + + if ((opline->extended_value == 1 + || opline->extended_value == 2 + || opline->extended_value == 3) + && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL + && (p+2)->op == ZEND_JIT_TRACE_VM) { + if (((p+2)->opline->opcode == ZEND_SEND_VAL + || (p+2)->opline->opcode == ZEND_SEND_VAL_EX) + && (p+2)->opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT((p+2)->opline, (p+2)->opline->op1); + + if (Z_TYPE_P(zv) == IS_STRING) { + zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), ffi_symbols); + + if (dcl) { + type = dcl->type; + p += 3; + } + } + } else if ((p+2)->opline->opcode == ZEND_SEND_VAR_EX + && (p+3)->op == ZEND_JIT_TRACE_OP1_TYPE + && (p+3)->ce == zend_ffi_api->ctype_ce + && (p+4)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + type = (zend_ffi_type*)(p+4)->ptr; + p += 5; + } + } + + if (type + && !ZEND_FFI_TYPE_IS_OWNED(type) + && (type->attr & ZEND_FFI_ATTR_PERSISTENT) + && type->size != 0 + && (opline->extended_value == 1 + || (opline->extended_value == 2 + && zend_jit_is_send_bool_const(p)) + || (opline->extended_value == 3 + && zend_jit_is_send_bool_const(p) + && zend_jit_is_send_bool_const(p + 1)))) { + return type; + } + + return NULL; +} + static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type) { dst_type = ZEND_FFI_TYPE(dst_type); diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 89eefcb402c80..aebe92261d332 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -335,11 +335,34 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, SET_STACK_REF_EX(stack, 2, ref, 0); } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW) { - ZEND_ASSERT(opline->op2.num == 1); - /* nothing to do */ + if (opline->op2.num == 1) { + if (op1_ffi_type && (opline->op1_type & (IS_VAR|IS_TMP_VAR))) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } + } else if (opline->op2.num == 2) { + ZEND_ASSERT(opline->op1_type == IS_CONST); + SET_STACK_TYPE(stack, 1, zend_is_true(RT_CONSTANT(opline, opline->op1)) ? IS_TRUE : IS_FALSE, 0); + } else if (opline->op2.num == 3) { + ZEND_ASSERT(opline->op1_type == IS_CONST); + SET_STACK_TYPE(stack, 2, zend_is_true(RT_CONSTANT(opline, opline->op1)) ? IS_TRUE : IS_FALSE, 0); + } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { ZEND_ASSERT(opline->op2.num == 1); - /* nothing to do */ + if (op1_ffi_type && (opline->op1_type & (IS_VAR|IS_TMP_VAR))) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } } else { ZEND_UNREACHABLE(); } @@ -768,19 +791,32 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW) { if (res_addr) { + uint32_t flags = ZEND_FFI_FLAG_OWNED; + + if (num_args > 1 && STACK_TYPE(stack, 1) == IS_FALSE) { + flags &= ~ZEND_FFI_FLAG_OWNED; + } + if (num_args > 2 && STACK_TYPE(stack, 2) == IS_TRUE) { + flags |= ZEND_FFI_FLAG_PERSISTENT; + } + // TODO: ZEND_FFI_FLAG_CONST flag ??? + if (flags & ZEND_FFI_FLAG_PERSISTENT) { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(calloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size)); + } else { #if ZEND_DEBUG - ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), - ir_CONST_LONG(1), - ir_CONST_ADDR(type->size), - op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL, - ir_CONST_U32(opline ? opline->lineno : 0), - IR_NULL, - ir_CONST_U32(0)); + ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), + ir_CONST_LONG(1), + ir_CONST_ADDR(type->size), + op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL, + ir_CONST_U32(opline ? opline->lineno : 0), + IR_NULL, + ir_CONST_U32(0)); #else - ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size)); + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size)); #endif + } ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); - ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(ZEND_FFI_FLAG_OWNED)); + ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(flags)); jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 82e5ddb78768a..0506972f9c697 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -6946,66 +6946,24 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2)); if (zend_string_equals_literal_ci(name, "new")) { - if (opline->extended_value == 1 - // TODO: support for FFI::new() with 2 and 3 arguments ??? - && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL - && (p+2)->op == ZEND_JIT_TRACE_VM) { - if (((p+2)->opline->opcode == ZEND_SEND_VAL - || (p+2)->opline->opcode == ZEND_SEND_VAL_EX) - && (p+2)->opline->op1_type == IS_CONST) { - zval *zv = RT_CONSTANT((p+2)->opline, (p+2)->opline->op1); - - if (Z_TYPE_P(zv) == IS_STRING) { - zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), op1_ffi_symbols); - - if (dcl - && !ZEND_FFI_TYPE_IS_OWNED(dcl->type) - && (dcl->type->attr & ZEND_FFI_ATTR_PERSISTENT) - && dcl->type->size != 0) { - if (!ffi_info) { - ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); - } - if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, - ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { - goto jit_failure; - } - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_NEW; - frame_ffi_func_type = dcl->type; - if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); - } else { - frame_ffi_func_ref = IR_UNUSED; - } - goto done; - } - } - } else if ((p+2)->opline->opcode == ZEND_SEND_VAR_EX - && (p+3)->op == ZEND_JIT_TRACE_OP1_TYPE - && (p+3)->ce == zend_ffi_api->ctype_ce - && (p+4)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { - zend_ffi_type *type = (zend_ffi_type*)(p+4)->ptr; - - if (!ZEND_FFI_TYPE_IS_OWNED(type) - && (type->attr & ZEND_FFI_ATTR_PERSISTENT) - && type->size != 0) { - if (!ffi_info) { - ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); - } - if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, - ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { - goto jit_failure; - } - // TODO: Guard for FFI::CType argument - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_NEW; - frame_ffi_func_type = type; - if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); - } else { - frame_ffi_func_ref = IR_UNUSED; - } - goto done; - } + zend_ffi_type *type = zend_jit_ffi_supported_new(opline, op1_ffi_symbols, p); + if (type) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, + ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + // TODO: Guard for FFI::CType argument ??? + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_NEW; + frame_ffi_func_type = type; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); + } else { + frame_ffi_func_ref = IR_UNUSED; } + goto done; } } else if (zend_string_equals_literal_ci(name, "type")) { if (opline->extended_value == 1 From 3305bb5f0b1a8a5bc172d2ae51397d474e0091c5 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 6 Nov 2024 16:11:28 +0300 Subject: [PATCH 097/101] JIT for FFI::free() --- ext/ffi/ffi.c | 34 ++++++++++++++++++++----------- ext/ffi/php_ffi.h | 1 + ext/opcache/jit/zend_jit_ir_ffi.c | 13 +++++++++++- ext/opcache/jit/zend_jit_trace.c | 7 +++++++ 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index a9f625e785eab..531cd0cacc67f 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -3926,22 +3926,12 @@ ZEND_METHOD(FFI, new) /* {{{ */ } /* }}} */ -ZEND_METHOD(FFI, free) /* {{{ */ +static bool zend_ffi_cdata_free(zend_ffi_cdata *cdata) /* {{{ */ { - zval *zv; - zend_ffi_cdata *cdata; - - ZEND_FFI_VALIDATE_API_RESTRICTION(); - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_OBJECT_OF_CLASS_EX(zv, zend_ffi_cdata_ce, 0, 1); - ZEND_PARSE_PARAMETERS_END(); - - cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); - if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) { if (!cdata->ptr) { zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference"); - RETURN_THROWS(); + return false; } if (cdata->ptr != (void*)&cdata->ptr_holder) { pefree(*(void**)cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT); @@ -3956,6 +3946,25 @@ ZEND_METHOD(FFI, free) /* {{{ */ cdata->std.handlers = &zend_ffi_cdata_free_handlers; } else { zend_throw_error(zend_ffi_exception_ce, "free() non a C pointer"); + return false; + } + return true; +} +/* }}} */ + +ZEND_METHOD(FFI, free) /* {{{ */ +{ + zval *zv; + zend_ffi_cdata *cdata; + + ZEND_FFI_VALIDATE_API_RESTRICTION(); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT_OF_CLASS_EX(zv, zend_ffi_cdata_ce, 0, 1); + ZEND_PARSE_PARAMETERS_END(); + + cdata = (zend_ffi_cdata*)Z_OBJ_P(zv); + if (!zend_ffi_cdata_free(cdata)) { + RETURN_THROWS(); } } /* }}} */ @@ -5618,6 +5627,7 @@ ZEND_MINIT_FUNCTION(ffi) ffi_api.cdata_create = zend_ffi_cdata_create; ffi_api.ctype_create = zend_ffi_ctype_create; + ffi_api.cdata_free = zend_ffi_cdata_free; ffi_api.type_print = zend_ffi_type_print; ffi_api.is_compatible_type = zend_ffi_is_compatible_type; diff --git a/ext/ffi/php_ffi.h b/ext/ffi/php_ffi.h index c2e4bd8b24d77..7c06fcba052ee 100644 --- a/ext/ffi/php_ffi.h +++ b/ext/ffi/php_ffi.h @@ -421,6 +421,7 @@ struct _zend_ffi_api { /* ext/ffi interface for ext/opcache */ zend_ffi_cdata* (*cdata_create)(void *ptr, zend_ffi_type *type); zend_ffi_ctype* (*ctype_create)(zend_ffi_type *type); + bool (*cdata_free)(zend_ffi_cdata *cdata); void (*type_print)(FILE *f, const zend_ffi_type *type); bool (*is_compatible_type)(zend_ffi_type *dst_type, zend_ffi_type *src_type); diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index aebe92261d332..b1a81bb0e2db3 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -137,7 +137,8 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, if (TRACE_FRAME_FFI_FUNC(call)) { if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPEOF - || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) { + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_FREE) { ZEND_ASSERT(opline->op2.num == 1); ZEND_ASSERT(op1_ffi_type); @@ -667,6 +668,16 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } ref = ir_ADD_U32(ir_ZEXT_U32(ir_EQ(ref, IR_NULL)), ir_CONST_U32(IS_FALSE)); jit_set_Z_TYPE_INFO_ex(jit, res_addr, ref); + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_FREE) { + ref = STACK_REF(stack, 0); + if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { + // TODO: try to remove this dereference ??? + ref = zend_jit_gc_deref(jit, ref); + } + ir_CALL_1(IR_VOID, ir_CONST_FUNC(zend_ffi_api->cdata_free), ref); + if (res_addr) { + jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL); + } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_STRING) { ZEND_ASSERT(num_args > 0 && STACK_TYPE(stack, 0) == IS_OBJECT); ref = STACK_REF(stack, 0); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 0506972f9c697..643e07b022021 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -7115,6 +7115,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; + } else if (Z_TYPE_P(zv) == IS_STRING + && zend_string_equals_literal_ci(Z_STR_P(zv), "free") + && opline->extended_value == 1) { + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_FREE; + frame_ffi_func_type = NULL; + frame_ffi_func_ref = IR_UNUSED; + goto done; } } } From dc68c969f16cca79ed0098e78e60a4bebc34847b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 8 Nov 2024 15:51:23 +0300 Subject: [PATCH 098/101] JIT for FFI::cast() --- ext/opcache/jit/zend_jit.c | 39 ++++++ ext/opcache/jit/zend_jit_helpers.c | 35 +++--- ext/opcache/jit/zend_jit_ir.c | 1 + ext/opcache/jit/zend_jit_ir_ffi.c | 188 ++++++++++++++++++++++++++--- ext/opcache/jit/zend_jit_trace.c | 72 +++++++++++ 5 files changed, 305 insertions(+), 30 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 53df4664e63e1..9067f81d58319 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -169,6 +169,45 @@ static zend_ffi_type* zend_jit_ffi_supported_new(const zend_op *opline, HashTabl return NULL; } +static zend_ffi_type* zend_jit_ffi_supported_cast(const zend_op *opline, HashTable *ffi_symbols, const zend_jit_trace_rec *p) +{ + zend_ffi_type *type = NULL; + + if (opline->extended_value == 2 + && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL + && (p+2)->op == ZEND_JIT_TRACE_VM) { + if (((p+2)->opline->opcode == ZEND_SEND_VAL + || (p+2)->opline->opcode == ZEND_SEND_VAL_EX) + && (p+2)->opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT((p+2)->opline, (p+2)->opline->op1); + + if (Z_TYPE_P(zv) == IS_STRING) { + zend_ffi_dcl *dcl = zend_ffi_api->cache_type_get(Z_STR_P(zv), ffi_symbols); + + if (dcl) { + type = dcl->type; + p += 3; + } + } + } else if ((p+2)->opline->opcode == ZEND_SEND_VAR_EX + && (p+3)->op == ZEND_JIT_TRACE_OP1_TYPE + && (p+3)->ce == zend_ffi_api->ctype_ce + && (p+4)->op == ZEND_JIT_TRACE_OP1_FFI_TYPE) { + type = (zend_ffi_type*)(p+4)->ptr; + p += 5; + } + } + + if (type + && !ZEND_FFI_TYPE_IS_OWNED(type) + && (type->attr & ZEND_FFI_ATTR_PERSISTENT) + && type->size != 0) { + return type; + } + + return NULL; +} + static bool zend_jit_ffi_compatible(zend_ffi_type *dst_type, uint32_t src_info, zend_ffi_type *src_type) { dst_type = ZEND_FFI_TYPE(dst_type); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 7010bd9f407ce..e9af2367079a5 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -3382,24 +3382,31 @@ static void ZEND_FASTCALL zend_jit_zval_stringl(zval *zv, const char *str, size_ } } +static zend_ffi_cdata* ZEND_FASTCALL zend_jit_ffi_create_ptr(zend_ffi_type *type, void *ptr) +{ + ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER); + zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); + + // inlined zend_ffi_object_init() + GC_SET_REFCOUNT(&cdata->std, 1); + GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); + cdata->std.extra_flags = 0; + cdata->std.ce = zend_ffi_api->cdata_ce; + cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ + cdata->std.properties = NULL; + zend_objects_store_put(&cdata->std); + cdata->type = type; + cdata->flags = 0; + cdata->ptr = (void*)&cdata->ptr_holder; + cdata->ptr_holder = ptr; + return cdata; +} + static void ZEND_FASTCALL zend_jit_zval_ffi_ptr(zval *zv, zend_ffi_type *type, void *ptr) { ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_POINTER); if (ptr) { - zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata)); - - // inlined zend_ffi_object_init() - GC_SET_REFCOUNT(&cdata->std, 1); - GC_TYPE_INFO(&cdata->std) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT); - cdata->std.extra_flags = 0; - cdata->std.ce = zend_ffi_api->cdata_ce; - cdata->std.handlers = zend_ffi_api->cdata_ce->default_object_handlers; /* zend_ffi_cdata_handlers */ - cdata->std.properties = NULL; - zend_objects_store_put(&cdata->std); - cdata->type = type; - cdata->flags = 0; - cdata->ptr = (void*)&cdata->ptr_holder; - cdata->ptr_holder = ptr; + zend_ffi_cdata *cdata = zend_jit_ffi_create_ptr(type, ptr); ZVAL_OBJ(zv, &cdata->std); } else { ZVAL_NULL(zv); diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 19401770c4296..98400517fbf43 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3107,6 +3107,7 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_zval_ffi_obj); REGISTER_HELPER(zend_jit_zval_ffi_addr); REGISTER_HELPER(zend_jit_zval_ffi_addr_var); + REGISTER_HELPER(zend_jit_ffi_create_ptr); #endif #ifndef ZTS diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index b1a81bb0e2db3..2c964a1d1fac4 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -51,6 +51,40 @@ static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit, zend_ffi_type *ffi_type, zend_jit_ffi_info *ffi_info); +static int zend_jit_ffi_write(zend_jit_ctx *jit, + zend_ffi_type *ffi_type, + ir_ref ptr, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *val_ffi_type, + zend_jit_addr res_addr); + +static ir_ref zend_jit_calloc(zend_jit_ctx *jit, + size_t size, + bool persistent, + const zend_op *opline, + const zend_op_array *op_array) +{ + ir_ref ref; + + if (persistent) { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(calloc), ir_CONST_LONG(1), ir_CONST_ADDR(size)); + } else { +#if ZEND_DEBUG + ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), + ir_CONST_LONG(1), + ir_CONST_ADDR(size), + op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL, + ir_CONST_U32(opline ? opline->lineno : 0), + IR_NULL, + ir_CONST_U32(0)); +#else + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(size)); +#endif + } + return ref; +} + static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, @@ -349,10 +383,135 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, } else if (opline->op2.num == 2) { ZEND_ASSERT(opline->op1_type == IS_CONST); SET_STACK_TYPE(stack, 1, zend_is_true(RT_CONSTANT(opline, opline->op1)) ? IS_TRUE : IS_FALSE, 0); - } else if (opline->op2.num == 3) { + } else { + ZEND_ASSERT(opline->op2.num == 3); ZEND_ASSERT(opline->op1_type == IS_CONST); SET_STACK_TYPE(stack, 2, zend_is_true(RT_CONSTANT(opline, opline->op1)) ? IS_TRUE : IS_FALSE, 0); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST) { + if (opline->op2.num == 1) { + if (op1_ffi_type && (opline->op1_type & (IS_VAR|IS_TMP_VAR))) { + arg_flags |= ZREG_FFI_ZVAL_DTOR; + if (op1_info & MAY_BE_REF) { + arg_flags |= ZREG_FFI_ZVAL_DEREF; + } + ref = jit_Z_PTR(jit, op1_addr); + SET_STACK_TYPE(stack, 0, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 0, ref, arg_flags); + } + } else { + ZEND_ASSERT(opline->op2.num == 2); + + /* Call FFI::cast() right here and pass result */ + if (op1_ffi_type) { + + ref = jit_Z_PTR(jit, op1_addr); + if (op1_info & MAY_BE_REF) { + ir_ref if_ref = ir_IF(ir_EQ(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_REFERENCE))); + ir_IF_TRUE(if_ref); + ir_ref ref2 = jit_Z_PTR_ref(jit, ir_ADD_OFFSET(ref, offsetof(zend_reference, val))); + ir_MERGE_WITH_EMPTY_FALSE(if_ref); + ref = ir_PHI_2(IR_ADDR, ref2, ref); + } + + if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && type->kind != ZEND_FFI_TYPE_POINTER + && ZEND_FFI_TYPE(op1_ffi_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) { + /* automatically dereference void* pointers ??? */ + // JIT: cdata->ptr = *(void**)arg->ptr + ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, ref)); + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); + } else if (op1_ffi_type->kind == ZEND_FFI_TYPE_ARRAY + && type->kind == ZEND_FFI_TYPE_POINTER + && zend_ffi_api->is_compatible_type(ZEND_FFI_TYPE(op1_ffi_type->array.type), ZEND_FFI_TYPE(type->pointer.type))) { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_ffi_create_ptr), + ir_CONST_ADDR(type), jit_FFI_CDATA_PTR(jit, ref)); + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + } else if (op1_ffi_type->kind == ZEND_FFI_TYPE_POINTER + && type->kind == ZEND_FFI_TYPE_ARRAY + && zend_ffi_api->is_compatible_type(ZEND_FFI_TYPE(op1_ffi_type->pointer.type), ZEND_FFI_TYPE(type->array.type))) { + // JIT: cdata->ptr = *arg->ptr; + ref = ir_LOAD_A(jit_FFI_CDATA_PTR(jit, ref)); + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); + } else if (type->size > op1_ffi_type->size) { + ZEND_ASSERT(type->size <= op1_ffi_type->size); + } else { + // TODO: holder usage ??? + ref = jit_FFI_CDATA_PTR(jit, ref); + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); + } + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + /* release argument and transfer ownweship */ + ir_ref src = jit_Z_PTR(jit, op1_addr); + + // JIT: if (!GC_DELREF(src)) + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, src)); + ir_IF_FALSE(if_not_zero); + + // JIT: if (Z_TYPE_P(arg) == IS_REFERENCE) + ir_ref if_ref = ir_IF(ir_EQ(jit_Z_TYPE(jit, op1_addr), ir_CONST_U8(IS_REFERENCE))); + ir_IF_TRUE(if_ref); + + // JIT: arg = Z_PTR(Z_REFVAL_P(arg)); + ir_ref src_obj = jit_Z_PTR_ref(jit, ir_ADD_OFFSET(src, offsetof(zend_reference, val))); + + // JIT: if (GC_REFCOUNT(arg_obj) == 1) + ir_ref if_one = ir_IF(ir_EQ(jit_GC_REFCOUNT(jit, src_obj), ir_CONST_U32(1))); + ir_IF_TRUE(if_one); + ir_MERGE_WITH_EMPTY_FALSE(if_ref); + src_obj = ir_PHI_2(IR_ADDR, src_obj, src); + + // JIT: if (old_cdata->flags & ZEND_FFI_FLAG_OWNED) + ir_ref a = ir_ADD_OFFSET(src_obj, offsetof(zend_ffi_cdata, flags)); + ir_ref f = ir_LOAD_U32(a); + ir_ref if_owned = ir_IF(ir_AND_U32(f, ir_CONST_U32(ZEND_FFI_FLAG_OWNED))); + ir_IF_TRUE(if_owned); + + /* transfer ownership */ + // JIT: old_cdata->flags &= ~ZEND_FFI_FLAG_OWNED; + ir_STORE(a, ir_AND_U32(f, ir_CONST_U32(~ZEND_FFI_FLAG_OWNED))); + // JIT: cdata->flags |= ZEND_FFI_FLAG_OWNED; + a = ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)); + ir_STORE(a, ir_OR_U32(ir_LOAD_U32(a), ir_CONST_U32(ZEND_FFI_FLAG_OWNED))); + + ir_MERGE_WITH_EMPTY_FALSE(if_owned); + ir_MERGE_WITH_EMPTY_FALSE(if_one); + + /* release */ + jit_ZVAL_DTOR(jit, src, op1_info, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); + } + } else if (type->kind < ZEND_FFI_TYPE_POINTER) { + /* numeric conversion */ + ref = zend_jit_calloc(jit, type->size, 0, opline, op_array); + if (!zend_jit_ffi_write(jit, type, ref, op1_info, op1_addr, op1_ffi_type, 0)) { + return 0; + } + ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); + ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(ZEND_FFI_FLAG_OWNED)); + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + } else if (type->kind == ZEND_FFI_TYPE_POINTER) { + if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_NULL) { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_ffi_create_ptr), + ir_CONST_ADDR(type), IR_NULL); + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + } else if ((op1_info & (MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG) { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_ffi_create_ptr), + ir_CONST_ADDR(type), jit_Z_LVAL(jit, op1_addr)); + SET_STACK_TYPE(stack, 1, IS_OBJECT, 0); + SET_STACK_REF_EX(stack, 1, ref, 0); + } else { + ZEND_UNREACHABLE(); + } + } else { + ZEND_UNREACHABLE(); + } + } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { ZEND_ASSERT(opline->op2.num == 1); if (op1_ffi_type && (opline->op1_type & (IS_VAR|IS_TMP_VAR))) { @@ -811,26 +970,22 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, flags |= ZEND_FFI_FLAG_PERSISTENT; } // TODO: ZEND_FFI_FLAG_CONST flag ??? - if (flags & ZEND_FFI_FLAG_PERSISTENT) { - ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(calloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size)); - } else { -#if ZEND_DEBUG - ref = ir_CALL_6(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), - ir_CONST_LONG(1), - ir_CONST_ADDR(type->size), - op_array->filename ? ir_CONST_ADDR(op_array->filename->val) : IR_NULL, - ir_CONST_U32(opline ? opline->lineno : 0), - IR_NULL, - ir_CONST_U32(0)); -#else - ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(_ecalloc), ir_CONST_LONG(1), ir_CONST_ADDR(type->size)); -#endif - } + ref = zend_jit_calloc(jit, type->size, (flags & ZEND_FFI_FLAG_PERSISTENT) != 0, opline, op_array); ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(flags)); jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); } + } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST) { + ZEND_ASSERT(STACK_TYPE(stack, 1) == IS_OBJECT); + ref = STACK_REF(stack, 1); + if (res_addr) { + jit_set_Z_PTR(jit, res_addr, ref); + jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + } else { + // TODO: release object ??? + ZEND_UNREACHABLE(); + } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { if (res_addr) { ref = ir_CONST_ADDR(type); @@ -1060,6 +1215,7 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, if (!TRACE_FRAME_FFI_FUNC(call) || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { func_ref = (intptr_t)(void*)call->ce; if (func_ref && !IR_IS_CONST_REF(func_ref)) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 643e07b022021..d7a4365c4a97e 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1330,6 +1330,31 @@ static uint32_t find_trampoline_num_args(zend_jit_trace_rec *start, zend_jit_tra return 0; } +#ifdef HAVE_FFI +static bool zend_jit_ffi_is_send_by_ref(zend_jit_trace_stack_frame *call, uint32_t arg_num) +{ + if (TRACE_FRAME_FFI_FUNC(call)) { + if (((TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_FREE + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPEOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ADDR + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_SIZEOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ALIGNOF + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMSET + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_STRING + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) + && arg_num == 1) + || (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST + && arg_num == 2) + || ((TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMCPY + || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_MEMCMP) + && (arg_num == 1 || arg_num == 2))) { + return true; + } + } + return false; +} +#endif + static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uint32_t parent_trace, uint32_t exit_num, zend_script *script, const zend_op_array **op_arrays, int *num_op_arrays_ptr) { zend_ssa *tssa; @@ -2195,7 +2220,26 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin /* Named parameters not supported in JIT */ break; } +#ifdef HAVE_FFI + if (!op1_ffi_type + || !frame->call->func + || !ARG_SHOULD_BE_SENT_BY_REF(frame->call->func, opline->op2.num)) { + ADD_OP1_TRACE_GUARD(); + } +#else ADD_OP1_TRACE_GUARD(); +#endif +/* + if (!frame->call->func + || (tssa->ops[idx].op1_use >= 0 + && !(tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_NULL)) +#ifdef HAVE_FFI + || (TRACE_FRAME_FFI(frame->call) && !TRACE_FRAME_FFI_FUNC(frame->call)) +#endif + ) { + ADD_OP1_TRACE_GUARD(); + } +*/ propagate_arg: /* Propagate argument type */ if (frame->call @@ -2266,6 +2310,10 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin } #ifdef HAVE_FFI if (TRACE_FRAME_FFI(frame->call)) { + if (zend_jit_ffi_is_send_by_ref(frame->call, opline->op2.num)) { + TRACE_FRAME_SET_LAST_SEND_BY_REF(frame->call); + break; + } /* FFI arguments alwyas sent by value ??? */ TRACE_FRAME_SET_LAST_SEND_BY_VAL(frame->call); break; @@ -5817,6 +5865,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } #ifdef HAVE_FFI if (TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + if (zend_jit_ffi_is_send_by_ref(JIT_G(current_frame)->call, opline->op2.num)) { + TRACE_FRAME_SET_LAST_SEND_BY_REF(JIT_G(current_frame)->call); + goto done; + } /* FFI arguments alwyas sent by value ??? */ TRACE_FRAME_SET_LAST_SEND_BY_VAL(JIT_G(current_frame)->call); goto done; @@ -6965,6 +7017,26 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } goto done; } + } else if (zend_string_equals_literal_ci(name, "cast")) { + zend_ffi_type *type = zend_jit_ffi_supported_cast(opline, op1_ffi_symbols, p); + if (type) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } + if (!zend_jit_ffi_symbols_guard(&ctx, opline, ssa, + ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { + goto jit_failure; + } + // TODO: Guard for FFI::CType argument ??? + frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_CAST; + frame_ffi_func_type = type; + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); + } else { + frame_ffi_func_ref = IR_UNUSED; + } + goto done; + } } else if (zend_string_equals_literal_ci(name, "type")) { if (opline->extended_value == 1 && (p+1)->op == ZEND_JIT_TRACE_INIT_CALL From 5fb8e8e9e2fd03c1077b400b509fb38dd2702bba Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 11 Nov 2024 23:46:26 +0300 Subject: [PATCH 099/101] Eliminate useless guards --- ext/opcache/jit/zend_jit_ir_ffi.c | 216 +++++++++++++++++++++--------- ext/opcache/jit/zend_jit_trace.c | 5 +- 2 files changed, 154 insertions(+), 67 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index 2c964a1d1fac4..fa09f74433f1a 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -51,13 +51,16 @@ static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit, zend_ffi_type *ffi_type, zend_jit_ffi_info *ffi_info); -static int zend_jit_ffi_write(zend_jit_ctx *jit, - zend_ffi_type *ffi_type, - ir_ref ptr, - uint32_t val_info, - zend_jit_addr val_addr, - zend_ffi_type *val_ffi_type, - zend_jit_addr res_addr); +static int zend_jit_ffi_write(zend_jit_ctx *jit, + zend_ssa *ssa, + zend_jit_ffi_info *ffi_info, + zend_ffi_type *ffi_type, + ir_ref ptr, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *val_ffi_type, + zend_jit_addr res_addr, + int res_def); static ir_ref zend_jit_calloc(zend_jit_ctx *jit, size_t size, @@ -487,7 +490,7 @@ static int zend_jit_ffi_send_val(zend_jit_ctx *jit, } else if (type->kind < ZEND_FFI_TYPE_POINTER) { /* numeric conversion */ ref = zend_jit_calloc(jit, type->size, 0, opline, op_array); - if (!zend_jit_ffi_write(jit, type, ref, op1_info, op1_addr, op1_ffi_type, 0)) { + if (!zend_jit_ffi_write(jit, NULL, NULL, type, ref, op1_info, op1_addr, op1_ffi_type, 0, -1)) { return 0; } ref = ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ref, ir_CONST_ADDR(type)); @@ -743,7 +746,8 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, - zend_jit_addr res_addr) + zend_jit_addr res_addr, + zend_jit_ffi_info *ffi_info) { zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; zend_jit_trace_stack *stack = call->stack; @@ -768,6 +772,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + //??? ffi_info[ssa_op->result_def].type = type; + //??? ffi_info[ssa_op->result_def].info = 0; } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_ALIGNOF) { ref = STACK_REF(stack, 0); if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { @@ -815,6 +824,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, ref = ir_CALL_1(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->ctype_create), ref); jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->ctype_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + //??? ffi_info[ssa_op->result_def].type = type; + //??? ffi_info[ssa_op->result_def].info = 0; } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_IS_NULL) { ref = STACK_REF(stack, 0); if (STACK_FLAGS(stack, 0) & ZREG_FFI_ZVAL_DEREF) { @@ -975,6 +989,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, ir_STORE(ir_ADD_OFFSET(ref, offsetof(zend_ffi_cdata, flags)), ir_CONST_U32(flags)); jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = type; + ffi_info[ssa_op->result_def].info = 0; } } else if (TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST) { ZEND_ASSERT(STACK_TYPE(stack, 1) == IS_OBJECT); @@ -982,6 +1001,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, if (res_addr) { jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = type; + ffi_info[ssa_op->result_def].info = 0; } else { // TODO: release object ??? ZEND_UNREACHABLE(); @@ -992,6 +1016,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, ref = ir_CALL_1(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->ctype_create), ref); jit_set_Z_PTR(jit, res_addr, ref); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->ctype_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = type; + ffi_info[ssa_op->result_def].info = 0; } } else { ZEND_UNREACHABLE(); @@ -1165,6 +1194,11 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, } else { ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ret_type), ref); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = type; + ffi_info[ssa_op->result_def].info = 0; goto cleanup; } break; @@ -1295,9 +1329,12 @@ static int zend_jit_ffi_abc(zend_jit_ctx *jit, } static int zend_jit_ffi_read(zend_jit_ctx *jit, + zend_ssa *ssa, + zend_jit_ffi_info *ffi_info, zend_ffi_type *ffi_type, ir_ref ptr, - zend_jit_addr res_addr) + zend_jit_addr res_addr, + int res_def) { uint32_t res_type; zend_ffi_type_kind type_kind = ffi_type->kind; @@ -1371,6 +1408,12 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, case ZEND_FFI_TYPE_STRUCT: ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_obj), jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ptr); + if (res_def >= 0) { + ssa->var_info[res_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[res_def].is_instanceof = 0; + ffi_info[res_def].type = ffi_type; + ffi_info[res_def].info = 0; + } return 1; case ZEND_FFI_TYPE_POINTER: if ((ffi_type->attr & ZEND_FFI_ATTR_CONST) @@ -1380,6 +1423,12 @@ static int zend_jit_ffi_read(zend_jit_ctx *jit, } else { ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ir_LOAD_A(ptr)); + if (res_def >= 0) { + ssa->var_info[res_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[res_def].is_instanceof = 0; + ffi_info[res_def].type = ffi_type; + ffi_info[res_def].info = 0; + } return 1; } break; @@ -1411,15 +1460,15 @@ static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit, ZEND_ASSERT((info & (/*MAY_BE_GUARD|*/MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT); ref = jit_Z_PTR(jit, addr); - if (ssa->var_info - && use >= 0 - && ssa->var_info[use].ce != zend_ffi_api->cdata_ce) { - if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_api->cdata_ce)) { - return 0; + if (ssa->var_info && use >= 0) { + if (ssa->var_info[use].ce != zend_ffi_api->cdata_ce) { + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_api->cdata_ce)) { + return 0; + } + ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[use].ce = zend_ffi_api->cdata_ce; + ssa->var_info[use].is_instanceof = 0; } - ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[use].ce = zend_ffi_api->cdata_ce; - ssa->var_info[use].is_instanceof = 0; if (def >= 0) { ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; ssa->var_info[def].ce = zend_ffi_api->cdata_ce; @@ -1427,15 +1476,15 @@ static ir_ref zend_jit_ffi_guard(zend_jit_ctx *jit, } } - if (ffi_info - && use >= 0 - && (ffi_info[use].type != ffi_type - || (ffi_info[use].info & FFI_TYPE_GUARD))) { - if (!zend_jit_ffi_type_guard(jit, opline, ref, ffi_type)) { - return 0; + if (ffi_info && use >= 0) { + if (ffi_info[use].type != ffi_type + || (ffi_info[use].info & FFI_TYPE_GUARD)) { + if (!zend_jit_ffi_type_guard(jit, opline, ref, ffi_type)) { + return 0; + } + ffi_info[use].info &= ~FFI_TYPE_GUARD; + ffi_info[use].type = ffi_type; } - ffi_info[use].info &= ~FFI_TYPE_GUARD; - ffi_info[use].type = ffi_type; if (def >= 0) { ffi_info[def].info &= ~FFI_TYPE_GUARD; ffi_info[def].type = ffi_type; @@ -1459,16 +1508,16 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, ZEND_ASSERT((info & (MAY_BE_GUARD|MAY_BE_REF|MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT); - if (ssa->var_info - && use >= 0 - && ssa->var_info[use].ce != zend_ffi_api->scope_ce) { - ref = jit_Z_PTR(jit, addr); - if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_api->scope_ce)) { - return 0; + if (ssa->var_info && use >= 0) { + if (ssa->var_info[use].ce != zend_ffi_api->scope_ce) { + ref = jit_Z_PTR(jit, addr); + if (!zend_jit_class_guard(jit, opline, ref, zend_ffi_api->scope_ce)) { + return 0; + } + ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; + ssa->var_info[use].ce = zend_ffi_api->scope_ce; + ssa->var_info[use].is_instanceof = 0; } - ssa->var_info[use].type |= MAY_BE_CLASS_GUARD; - ssa->var_info[use].ce = zend_ffi_api->scope_ce; - ssa->var_info[use].is_instanceof = 0; if (def >= 0) { ssa->var_info[def].type |= MAY_BE_CLASS_GUARD; ssa->var_info[def].ce = zend_ffi_api->scope_ce; @@ -1476,26 +1525,26 @@ static int zend_jit_ffi_symbols_guard(zend_jit_ctx *jit, } } - if (ffi_info - && use >= 0 - && (ffi_info[use].symbols != ffi_symbols - || (ffi_info[use].info & FFI_SYMBOLS_GUARD))) { - if (!ref) { - ref = jit_Z_PTR(jit, addr); - } + if (ffi_info && use >= 0) { + if (ffi_info[use].symbols != ffi_symbols + || (ffi_info[use].info & FFI_SYMBOLS_GUARD)) { + if (!ref) { + ref = jit_Z_PTR(jit, addr); + } - int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); - const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!exit_addr) { - return 0; - } + if (!exit_addr) { + return 0; + } - ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(ref, offsetof(zend_ffi, symbols))), ir_CONST_ADDR(ffi_symbols)), - ir_CONST_ADDR(exit_addr)); + ir_GUARD(ir_EQ(ir_LOAD_A(ir_ADD_OFFSET(ref, offsetof(zend_ffi, symbols))), ir_CONST_ADDR(ffi_symbols)), + ir_CONST_ADDR(exit_addr)); - ffi_info[use].info &= ~FFI_SYMBOLS_GUARD; - ffi_info[use].symbols = ffi_symbols; + ffi_info[use].info &= ~FFI_SYMBOLS_GUARD; + ffi_info[use].symbols = ffi_symbols; + } if (def >= 0) { ffi_info[def].info &= ~FFI_SYMBOLS_GUARD; ffi_info[def].symbols = ffi_symbols; @@ -1547,8 +1596,13 @@ static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(el_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = el_type; + ffi_info[ssa_op->result_def].info = 0; } else { - if (!zend_jit_ffi_read(jit, el_type, ptr, res_addr)) { + if (!zend_jit_ffi_read(jit, ssa, ffi_info, el_type, ptr, res_addr, ssa_op->result_def)) { return 0; } } @@ -1567,13 +1621,16 @@ static int zend_jit_ffi_fetch_dim(zend_jit_ctx *jit, return 1; } -static int zend_jit_ffi_write(zend_jit_ctx *jit, - zend_ffi_type *ffi_type, - ir_ref ptr, - uint32_t val_info, - zend_jit_addr val_addr, - zend_ffi_type *val_ffi_type, - zend_jit_addr res_addr) +static int zend_jit_ffi_write(zend_jit_ctx *jit, + zend_ssa *ssa, + zend_jit_ffi_info *ffi_info, + zend_ffi_type *ffi_type, + ir_ref ptr, + uint32_t val_info, + zend_jit_addr val_addr, + zend_ffi_type *val_ffi_type, + zend_jit_addr res_addr, + int res_def) { ir_ref ref = IR_UNUSED; zend_ffi_type_kind type_kind = ffi_type->kind; @@ -1825,6 +1882,12 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, if (res_addr) { ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_ptr), jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ref); + if (res_def >= 0) { + ssa->var_info[res_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[res_def].is_instanceof = 0; + ffi_info[res_def].type = ffi_type; + ffi_info[res_def].info = 0; + } } break; default: @@ -1837,6 +1900,12 @@ static int zend_jit_ffi_write(zend_jit_ctx *jit, if (res_addr) { ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_zval_ffi_obj), jit_ZVAL_ADDR(jit, res_addr), ir_CONST_ADDR(ffi_type), ref); + if (res_def >= 0) { + ssa->var_info[res_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[res_def].is_instanceof = 0; + ffi_info[res_def].type = ffi_type; + ffi_info[res_def].info = 0; + } } break; } @@ -1895,7 +1964,7 @@ static int zend_jit_ffi_assign_dim(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); - if (!zend_jit_ffi_write(jit, el_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + if (!zend_jit_ffi_write(jit, ssa, ffi_info, el_type, ptr, val_info, val_addr, val_ffi_type, res_addr, ssa_op->result_def)) { return 0; } @@ -2177,8 +2246,13 @@ static int zend_jit_ffi_fetch_obj(zend_jit_ctx *jit, ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(field_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = op1_ffi_type; + ffi_info[ssa_op->result_def].info = 0; } else { - if (!zend_jit_ffi_read(jit, field_type, ptr, res_addr)) { + if (!zend_jit_ffi_read(jit, ssa, ffi_info, field_type, ptr, res_addr, ssa_op->result_def)) { return 0; } } @@ -2225,8 +2299,13 @@ static int zend_jit_ffi_fetch_val(zend_jit_ctx *jit, ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(op1_ffi_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = op1_ffi_type; + ffi_info[ssa_op->result_def].info = 0; } else { - if (!zend_jit_ffi_read(jit, op1_ffi_type, ptr, res_addr)) { + if (!zend_jit_ffi_read(jit, ssa, ffi_info, op1_ffi_type, ptr, res_addr, ssa_op->result_def)) { return 0; } } @@ -2273,8 +2352,13 @@ static int zend_jit_ffi_fetch_sym(zend_jit_ctx *jit, ir_CALL_2(IR_ADDR, ir_CONST_FUNC(zend_ffi_api->cdata_create), ptr, ir_CONST_ADDR(sym_type))); jit_set_Z_TYPE_INFO(jit, res_addr, IS_OBJECT_EX); + ZEND_ASSERT(ssa_op->result_def >= 0); + ssa->var_info[ssa_op->result_def].ce = zend_ffi_api->cdata_ce; + ssa->var_info[ssa_op->result_def].is_instanceof = 0; + ffi_info[ssa_op->result_def].type = sym_type; + ffi_info[ssa_op->result_def].info = 0; } else { - if (!zend_jit_ffi_read(jit, sym_type, ptr, res_addr)) { + if (!zend_jit_ffi_read(jit, ssa, ffi_info, sym_type, ptr, res_addr, ssa_op->result_def)) { return 0; } } @@ -2332,7 +2416,7 @@ static int zend_jit_ffi_assign_obj(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); - if (!zend_jit_ffi_write(jit, field_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + if (!zend_jit_ffi_write(jit, ssa, ffi_info, field_type, ptr, val_info, val_addr, val_ffi_type, res_addr, ssa_op->result_def)) { return 0; } @@ -2381,7 +2465,7 @@ static int zend_jit_ffi_assign_val(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); - if (!zend_jit_ffi_write(jit, op1_ffi_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + if (!zend_jit_ffi_write(jit, ssa, ffi_info, op1_ffi_type, ptr, val_info, val_addr, val_ffi_type, res_addr, ssa_op->result_def)) { return 0; } @@ -2429,7 +2513,7 @@ static int zend_jit_ffi_assign_sym(zend_jit_ctx *jit, ZEND_ASSERT(!res_addr || RETURN_VALUE_USED(opline)); ir_ref ptr = ir_CONST_ADDR(sym->addr); - if (!zend_jit_ffi_write(jit, sym_type, ptr, val_info, val_addr, val_ffi_type, res_addr)) { + if (!zend_jit_ffi_write(jit, ssa, ffi_info, sym_type, ptr, val_info, val_addr, val_ffi_type, res_addr, ssa_op->result_def)) { return 0; } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index d7a4365c4a97e..ff95383296396 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5904,8 +5904,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (JIT_G(current_frame) && JIT_G(current_frame)->call && TRACE_FRAME_FFI(JIT_G(current_frame)->call)) { + if (!ffi_info) { + ffi_info = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_ffi_info)); + } if (!zend_jit_ffi_do_call(&ctx, opline, op_array, ssa, ssa_op, - (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0 )) { + (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, ffi_info)) { goto jit_failure; } goto done; From 79982627fe1e3a6dace5e5bc332f6b9fa53df8db Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 12 Nov 2024 12:03:31 +0300 Subject: [PATCH 100/101] Properly restore FFI_G(persistent) --- ext/ffi/ffi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 531cd0cacc67f..42e123a9bdfee 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -3295,7 +3295,7 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ zend_ffi_symbol *sym; zend_ffi_tag *tag; void *addr; - bool persistent = false; + bool persistent = false, old_persistent; if (stat(filename, &buf) != 0) { if (preload) { @@ -3342,6 +3342,8 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ } } + old_persistent = FFI_G(persistent); + FFI_G(symbols) = NULL; FFI_G(tags) = NULL; FFI_G(persistent) = preload; @@ -3355,7 +3357,7 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ code_pos = zend_ffi_parse_directives(filename, ZSTR_VAL(code), &scope_name, &lib, preload); if (!code_pos) { zend_string_release(code); - FFI_G(persistent) = 0; + FFI_G(persistent) = old_persistent; return NULL; } code_size -= code_pos - ZSTR_VAL(code); @@ -3553,7 +3555,7 @@ static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */ zend_string_release(code); FFI_G(symbols) = NULL; FFI_G(tags) = NULL; - FFI_G(persistent) = persistent; + FFI_G(persistent) = old_persistent; return ffi; From 18c9ee556978b94b79913368f9fa9e0f2a5d4fb1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 13 Nov 2024 16:53:47 +0300 Subject: [PATCH 101/101] Refactor information about FFI calls --- ext/opcache/jit/zend_jit_internal.h | 43 +++++++++++------- ext/opcache/jit/zend_jit_ir_ffi.c | 18 +++++--- ext/opcache/jit/zend_jit_trace.c | 69 +++++++++++++++++++---------- 3 files changed, 83 insertions(+), 47 deletions(-) diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index fc0e672f0e1b3..efb71ffd87b0b 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -555,6 +555,11 @@ struct _zend_jit_trace_stack_frame { int used_stack; int old_checked_stack; int old_peek_checked_stack; +#ifdef HAVE_FFI + uint32_t ffi_info; + int ffi_obj_ref; + int ffi_func_ref; +#endif zend_jit_trace_stack stack[1]; }; @@ -575,23 +580,24 @@ struct _zend_jit_trace_stack_frame { #define TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS 0x00000400 #define TRACE_FRAME_MASK_FFI 0x00000800 -#define TRACE_FRAME_MASK_FFI_FUNC 0x0000f000 - -#define TRACE_FRAME_FFI_FUNC_NEW 0x00001000 -#define TRACE_FRAME_FFI_FUNC_FREE 0x00002000 -#define TRACE_FRAME_FFI_FUNC_CAST 0x00003000 -#define TRACE_FRAME_FFI_FUNC_TYPEOF 0x00004000 -#define TRACE_FRAME_FFI_FUNC_ARRAY_TYPE 0x00005000 -#define TRACE_FRAME_FFI_FUNC_ADDR 0x00006000 -#define TRACE_FRAME_FFI_FUNC_ALIGNOF 0x00007000 -#define TRACE_FRAME_FFI_FUNC_SIZEOF 0x00008000 -#define TRACE_FRAME_FFI_FUNC_MEMCPY 0x00009000 -#define TRACE_FRAME_FFI_FUNC_MEMCMP 0x0000a000 -#define TRACE_FRAME_FFI_FUNC_MEMSET 0x0000b000 -#define TRACE_FRAME_FFI_FUNC_STRING 0x0000c000 -#define TRACE_FRAME_FFI_FUNC_IS_NULL 0x0000d000 -#define TRACE_FRAME_FFI_FUNC_TYPE 0x0000e000 +#define TRACE_FRAME_FFI_FUNC_NEW 0x00000001 +#define TRACE_FRAME_FFI_FUNC_FREE 0x00000002 +#define TRACE_FRAME_FFI_FUNC_CAST 0x00000003 +#define TRACE_FRAME_FFI_FUNC_TYPEOF 0x00000004 +#define TRACE_FRAME_FFI_FUNC_ARRAY_TYPE 0x00000005 +#define TRACE_FRAME_FFI_FUNC_ADDR 0x00000006 +#define TRACE_FRAME_FFI_FUNC_ALIGNOF 0x00000007 +#define TRACE_FRAME_FFI_FUNC_SIZEOF 0x00000008 +#define TRACE_FRAME_FFI_FUNC_MEMCPY 0x00000009 +#define TRACE_FRAME_FFI_FUNC_MEMCMP 0x0000000a +#define TRACE_FRAME_FFI_FUNC_MEMSET 0x0000000b +#define TRACE_FRAME_FFI_FUNC_STRING 0x0000000c +#define TRACE_FRAME_FFI_FUNC_IS_NULL 0x0000000d +#define TRACE_FRAME_FFI_FUNC_TYPE 0x0000000e + +#define TRACE_FRAME_MASK_FFI_FUNC 0x0000000f +#define TRACE_FRAME_MASK_FFI_OBJ_DTOR 0x00000010 #define TRACE_FRAME_INIT(frame, _func, _flags, num_args) do { \ zend_jit_trace_stack_frame *_frame = (frame); \ @@ -632,8 +638,11 @@ struct _zend_jit_trace_stack_frame { ((frame)->_info & TRACE_FRAME_MASK_ALWAYS_RELEASE_THIS) #define TRACE_FRAME_FFI(frame) \ ((frame)->_info & TRACE_FRAME_MASK_FFI) + #define TRACE_FRAME_FFI_FUNC(frame) \ - ((frame)->_info & TRACE_FRAME_MASK_FFI_FUNC) + ((frame)->ffi_info & TRACE_FRAME_MASK_FFI_FUNC) +#define TRACE_FRAME_FFI_OBJ_DTOR(frame) \ + ((frame)->ffi_info & TRACE_FRAME_MASK_FFI_OBJ_DTOR) #define TRACE_FRAME_SET_UNKNOWN_NUM_ARGS(frame) do { \ (frame)->_info |= (0xffffu << TRACE_FRAME_SHIFT_NUM_ARGS); \ diff --git a/ext/opcache/jit/zend_jit_ir_ffi.c b/ext/opcache/jit/zend_jit_ir_ffi.c index fa09f74433f1a..d94b58603e485 100644 --- a/ext/opcache/jit/zend_jit_ir_ffi.c +++ b/ext/opcache/jit/zend_jit_ir_ffi.c @@ -110,11 +110,6 @@ static int zend_jit_ffi_init_call_sym(zend_jit_ctx *jit, return 0; } - if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { - // TODO: usually the object is released only after the call ??? - jit_GC_DELREF(jit, jit_Z_PTR(jit, op1_addr)); - } - if (type->func.abi == ZEND_FFI_ABI_FASTCALL) { *ffi_func_ref = ir_CONST_FC_FUNC(sym->addr); } else { @@ -752,7 +747,7 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, zend_jit_trace_stack_frame *call = JIT_G(current_frame)->call; zend_jit_trace_stack *stack = call->stack; zend_ffi_type *type = (zend_ffi_type*)(void*)call->call_opline; - ir_ref func_ref = (intptr_t)(void*)call->ce; + ir_ref func_ref = call->ffi_func_ref; uint32_t i, num_args = TRACE_FRAME_NUM_ARGS(call);; ir_type ret_type = IR_VOID; ir_ref ref = IR_UNUSED; @@ -1251,13 +1246,22 @@ static int zend_jit_ffi_do_call(zend_jit_ctx *jit, || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_NEW || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_CAST || TRACE_FRAME_FFI_FUNC(call) == TRACE_FRAME_FFI_FUNC_TYPE) { - func_ref = (intptr_t)(void*)call->ce; + func_ref = call->ffi_func_ref; if (func_ref && !IR_IS_CONST_REF(func_ref)) { ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, func_ref)); ir_IF_FALSE(if_not_zero); jit_ZVAL_DTOR(jit, func_ref, MAY_BE_OBJECT, opline); ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ } + if (TRACE_FRAME_FFI_OBJ_DTOR(call)) { + ir_ref obj_ref = call->ffi_obj_ref; + + ZEND_ASSERT(obj_ref && !IR_IS_CONST_REF(obj_ref)); + ir_ref if_not_zero = ir_IF(jit_GC_DELREF(jit, obj_ref)); + ir_IF_FALSE(if_not_zero); + jit_ZVAL_DTOR(jit, obj_ref, MAY_BE_OBJECT, opline); + ir_MERGE_WITH_EMPTY_TRUE(if_not_zero); /* don't add to GC roots */ + } } return 1; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index ff95383296396..24a5ecb87fee4 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4299,6 +4299,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI zend_jit_ffi_info *ffi_info = NULL; zend_ffi_type *frame_ffi_func_type = NULL; + uint32_t frame_ffi_info = 0; + ir_ref frame_ffi_obj_ref = IR_UNUSED; ir_ref frame_ffi_func_ref = IR_UNUSED; #endif @@ -4647,6 +4649,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_flags = 0; #ifdef HAVE_FFI frame_ffi_func_type = NULL; + frame_ffi_info = 0; + frame_ffi_obj_ref = IR_UNUSED; frame_ffi_func_ref = IR_UNUSED; #endif @@ -7011,12 +7015,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } // TODO: Guard for FFI::CType argument ??? - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_NEW; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_NEW; frame_ffi_func_type = type; + frame_ffi_func_ref = IR_UNUSED; + frame_ffi_obj_ref = jit_Z_PTR(jit, op1_addr); if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); - } else { - frame_ffi_func_ref = IR_UNUSED; + frame_ffi_info |= TRACE_FRAME_MASK_FFI_OBJ_DTOR; } goto done; } @@ -7031,12 +7036,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } // TODO: Guard for FFI::CType argument ??? - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_CAST; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_CAST; frame_ffi_func_type = type; + frame_ffi_func_ref = IR_UNUSED; + frame_ffi_obj_ref = jit_Z_PTR(jit, op1_addr); if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); - } else { - frame_ffi_func_ref = IR_UNUSED; + frame_ffi_info |= TRACE_FRAME_MASK_FFI_OBJ_DTOR; } goto done; } @@ -7062,12 +7068,13 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ssa_op->op1_use, -1, op1_info, op1_addr, op1_ffi_symbols, ffi_info)) { goto jit_failure; } - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_TYPE; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_TYPE; frame_ffi_func_type = dcl->type; + frame_ffi_func_ref = IR_UNUSED; + frame_ffi_obj_ref = jit_Z_PTR(jit, op1_addr); if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { - frame_ffi_func_ref = jit_Z_PTR(jit, op1_addr); - } else { - frame_ffi_func_ref = IR_UNUSED; + frame_ffi_info |= TRACE_FRAME_MASK_FFI_OBJ_DTOR; } goto done; } @@ -7093,6 +7100,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_flags = TRACE_FRAME_MASK_FFI; frame_ffi_func_type = ZEND_FFI_TYPE(sym->type); frame_ffi_func_ref = ffi_func_ref; + frame_ffi_obj_ref = jit_Z_PTR(jit, op1_addr); + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + frame_ffi_info |= TRACE_FRAME_MASK_FFI_OBJ_DTOR; + } goto done; } } @@ -7130,70 +7141,80 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "addr") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_ADDR; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_ADDR; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "string") && (opline->extended_value == 1 || opline->extended_value == 2)) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_STRING; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_STRING; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "alignof") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_ALIGNOF; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_ALIGNOF; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "sizeof") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_SIZEOF; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_SIZEOF; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "typeof") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_TYPEOF; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_TYPEOF; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "isnull") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_IS_NULL; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_IS_NULL; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "memcpy") && opline->extended_value == 3) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_MEMCPY; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_MEMCPY; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "memcmp") && opline->extended_value == 3) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_MEMCMP; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_MEMCMP; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "memset") && opline->extended_value == 3) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_MEMSET; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_MEMSET; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; } else if (Z_TYPE_P(zv) == IS_STRING && zend_string_equals_literal_ci(Z_STR_P(zv), "free") && opline->extended_value == 1) { - frame_flags = TRACE_FRAME_MASK_FFI | TRACE_FRAME_FFI_FUNC_FREE; + frame_flags = TRACE_FRAME_MASK_FFI; + frame_ffi_info = TRACE_FRAME_FFI_FUNC_FREE; frame_ffi_func_type = NULL; frame_ffi_func_ref = IR_UNUSED; goto done; @@ -7854,7 +7875,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par #ifdef HAVE_FFI if (TRACE_FRAME_FFI(call)) { call->call_opline = (const zend_op*)(void*)frame_ffi_func_type; - call->ce = (zend_class_entry*)(intptr_t)frame_ffi_func_ref; + call->ffi_info = frame_ffi_info; + call->ffi_obj_ref = frame_ffi_obj_ref; + call->ffi_func_ref = frame_ffi_func_ref; } #endif call->prev = frame->call;