From b4b5734d808fef558e715fff3529a2e6907e8041 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 19 Jun 2020 14:36:38 +0300 Subject: [PATCH] JIT for FETCH_OBJ_W --- ext/opcache/jit/zend_jit.c | 11 +- ext/opcache/jit/zend_jit_disasm_x86.c | 5 + ext/opcache/jit/zend_jit_helpers.c | 194 ++++++++++++++++++++++++ ext/opcache/jit/zend_jit_trace.c | 24 ++- ext/opcache/jit/zend_jit_x86.dasc | 205 ++++++++++++++++++++++++-- 5 files changed, 418 insertions(+), 21 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 223dd53ba6..53f47b7fac 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1969,6 +1969,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op zend_bool send_result; zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; zend_class_entry *ce; + zend_bool ce_is_instanceof; if (JIT_G(bisect_limit)) { jit_bisect_pos++; @@ -2759,16 +2760,19 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto done; case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_W: if (opline->op2_type != IS_CONST || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { break; } ce = NULL; + ce_is_instanceof = 0; if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; op1_addr = 0; ce = op_array->scope; + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; } else { op1_info = OP1_INFO(); if (!(op1_info & MAY_BE_OBJECT)) { @@ -2779,14 +2783,15 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; if (ssa_op->op1_use >= 0) { zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; - if (op1_ssa->ce && !op1_ssa->is_instanceof && !op1_ssa->ce->create_object) { + if (op1_ssa->ce && !op1_ssa->ce->create_object) { ce = op1_ssa->ce; + ce_is_instanceof = op1_ssa->is_instanceof; } } } } - if (!zend_jit_fetch_obj_read(&dasm_state, opline, op_array, - op1_info, op1_addr, ce, + if (!zend_jit_fetch_obj(&dasm_state, opline, op_array, + op1_info, op1_addr, 0, ce, ce_is_instanceof, zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } diff --git a/ext/opcache/jit/zend_jit_disasm_x86.c b/ext/opcache/jit/zend_jit_disasm_x86.c index ccabb564c9..039efa5f8c 100644 --- a/ext/opcache/jit/zend_jit_disasm_x86.c +++ b/ext/opcache/jit/zend_jit_disasm_x86.c @@ -432,6 +432,10 @@ static int zend_jit_disasm_init(void) REGISTER_HELPER(zend_jit_fetch_obj_r_dynamic); REGISTER_HELPER(zend_jit_fetch_obj_is_slow); REGISTER_HELPER(zend_jit_fetch_obj_is_dynamic); + REGISTER_HELPER(zend_jit_fetch_obj_w_slow); + REGISTER_HELPER(zend_jit_check_array_promotion); + REGISTER_HELPER(zend_jit_create_typed_ref); + REGISTER_HELPER(zend_jit_extract_helper); REGISTER_HELPER(zend_jit_vm_stack_free_args_helper); REGISTER_HELPER(zend_jit_copy_extra_args_helper); REGISTER_HELPER(zend_jit_deprecated_helper); @@ -447,6 +451,7 @@ static int zend_jit_disasm_init(void) REGISTER_HELPER(zend_jit_only_vars_by_reference); REGISTER_HELPER(zend_jit_invalid_array_access); REGISTER_HELPER(zend_jit_invalid_property_read); + REGISTER_HELPER(zend_jit_invalid_property_write); REGISTER_HELPER(zend_jit_prepare_assign_dim_ref); REGISTER_HELPER(zend_jit_pre_inc); REGISTER_HELPER(zend_jit_pre_dec); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 99b3f8817d..adbcc9004d 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1345,6 +1345,193 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intpt zend_jit_fetch_obj_is_slow(zobj, offset, result, cache_slot); } +static zend_always_inline zend_bool promotes_to_array(zval *val) { + return Z_TYPE_P(val) <= IS_FALSE + || (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE); +} + +static zend_always_inline zend_bool check_type_array_assignable(zend_type type) { + if (!ZEND_TYPE_IS_SET(type)) { + return 1; + } + return (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) != 0; +} + +static zend_property_info *zend_object_fetch_property_type_info( + zend_object *obj, zval *slot) +{ + if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) { + return NULL; + } + + /* Not a declared property */ + if (UNEXPECTED(slot < obj->properties_table || + slot >= obj->properties_table + obj->ce->default_properties_count)) { + return NULL; + } + + return zend_get_typed_property_info_for_slot(obj, slot); +} + +static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) { + zend_string *type_str = zend_type_to_string(prop->type); + zend_type_error( + "Cannot auto-initialize an %s inside property %s::$%s of type %s", + type, + ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), + ZSTR_VAL(type_str) + ); + zend_string_release(type_str); +} + +static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error( + zend_property_info *prop) { + zend_throw_error(NULL, + "Cannot access uninitialized non-nullable property %s::$%s by reference", + ZSTR_VAL(prop->ce->name), + zend_get_unmangled_property_name(prop->name)); +} + +static zend_never_inline zend_bool zend_handle_fetch_obj_flags( + zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags) +{ + switch (flags) { + case ZEND_FETCH_DIM_WRITE: + if (promotes_to_array(ptr)) { + if (!prop_info) { + prop_info = zend_object_fetch_property_type_info(obj, ptr); + if (!prop_info) { + break; + } + } + if (!check_type_array_assignable(prop_info->type)) { + zend_throw_auto_init_in_prop_error(prop_info, "array"); + if (result) ZVAL_ERROR(result); + return 0; + } + } + break; + case ZEND_FETCH_REF: + if (Z_TYPE_P(ptr) != IS_REFERENCE) { + if (!prop_info) { + prop_info = zend_object_fetch_property_type_info(obj, ptr); + if (!prop_info) { + break; + } + } + if (Z_TYPE_P(ptr) == IS_UNDEF) { + if (!ZEND_TYPE_ALLOW_NULL(prop_info->type)) { + zend_throw_access_uninit_prop_by_ref_error(prop_info); + if (result) ZVAL_ERROR(result); + return 0; + } + ZVAL_NULL(ptr); + } + + ZVAL_NEW_REF(ptr, ptr); + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(ptr), prop_info); + } + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + return 1; +} + +static void ZEND_FASTCALL zend_jit_fetch_obj_w_slow(zend_object *zobj, zval *offset, zval *result, uint32_t cache_slot) +{ + zval *retval; + zend_execute_data *execute_data = EG(current_execute_data); + const zend_op *opline = execute_data->opline; + zend_string *name, *tmp_name; + + name = zval_get_tmp_string(offset, &tmp_name); + retval = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_W, CACHE_ADDR(cache_slot)); + if (NULL == retval) { + retval = zobj->handlers->read_property(zobj, name, BP_VAR_W, CACHE_ADDR(cache_slot), result); + if (retval == result) { + if (UNEXPECTED(Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1)) { + ZVAL_UNREF(retval); + } + goto end; + } + } else if (UNEXPECTED(Z_ISERROR_P(retval))) { + ZVAL_ERROR(result); + goto end; + } + + ZVAL_INDIRECT(result, retval); + + /* Support for typed properties */ + do { + uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; + + if (flags) { + zend_property_info *prop_info = NULL; + + if (opline->op2_type == IS_CONST) { + prop_info = CACHED_PTR_EX(CACHE_ADDR(cache_slot) + 2); + if (!prop_info) { + break; + } + } + if (UNEXPECTED(!zend_handle_fetch_obj_flags(result, retval, zobj, prop_info, flags))) { + goto end; + } + } + } while (0); + + if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { + ZVAL_NULL(retval); + } + +end: + zend_tmp_string_release(tmp_name); +} + +static void ZEND_FASTCALL zend_jit_check_array_promotion(zval *val, zend_property_info *prop) +{ + zend_execute_data *execute_data = EG(current_execute_data); + const zend_op *opline = execute_data->opline; + zval *result = EX_VAR(opline->result.var); + + if (((Z_TYPE_P(val) <= IS_FALSE + || (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE)) + && ZEND_TYPE_IS_SET(prop->type) + && ZEND_TYPE_FULL_MASK(prop->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) { + zend_string *type_str = zend_type_to_string(prop->type); + zend_type_error( + "Cannot auto-initialize an array inside property %s::$%s of type %s", + ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), + ZSTR_VAL(type_str) + ); + zend_string_release(type_str); + ZVAL_ERROR(result); + } else { + ZVAL_INDIRECT(result, val); + } +} + +static void ZEND_FASTCALL zend_jit_create_typed_ref(zval *val, zend_property_info *prop, zval *result) +{ + if (!Z_ISREF_P(val)) { + ZVAL_NEW_REF(val, val); + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(val), prop); + } + ZVAL_INDIRECT(result, val); +} + +static void ZEND_FASTCALL zend_jit_extract_helper(zend_refcounted *garbage) +{ + zend_execute_data *execute_data = EG(current_execute_data); + const zend_op *opline = execute_data->opline; + zval *zv = EX_VAR(opline->result.var); + + if (EXPECTED(Z_TYPE_P(zv) == IS_INDIRECT)) { + ZVAL_COPY(zv, Z_INDIRECT_P(zv)); + } + rc_dtor_func(garbage); +} + static void ZEND_FASTCALL zend_jit_vm_stack_free_args_helper(zend_execute_data *call) { zend_vm_stack_free_args(call); @@ -1508,6 +1695,13 @@ static void ZEND_FASTCALL zend_jit_invalid_property_read(zval *container, const zend_error(E_WARNING, "Attempt to read property '%s' on %s", property_name, zend_zval_type_name(container)); } +static void ZEND_FASTCALL zend_jit_invalid_property_write(zval *container, const char *property_name) +{ + zend_throw_error(NULL, + "Attempt to modify property '%s' on %s", + property_name, zend_zval_type_name(container)); +} + static zval * ZEND_FASTCALL zend_jit_prepare_assign_dim_ref(zval *ref) { zval *val = Z_REFVAL_P(ref); if (Z_TYPE_P(val) <= IS_FALSE) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 3bcb5a7697..a2f0784ade 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1557,6 +1557,7 @@ propagate_arg: /* break missing intentionally */ case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_W: if (opline->op2_type != IS_CONST || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { @@ -2632,6 +2633,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par zend_bool send_result = 0; zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; zend_class_entry *ce; + zend_bool ce_is_instanceof; uint32_t i; zend_jit_trace_stack_frame *frame, *top, *call; zend_jit_trace_stack *stack; @@ -2865,6 +2867,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par uint8_t op2_type = p->op2_type; uint8_t op3_type = p->op3_type; uint8_t orig_op1_type = op1_type; + zend_bool op1_indirect; opline = p->opline; if (op1_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) { @@ -3728,15 +3731,19 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par /* break missing intentionally */ case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_W: if (opline->op2_type != IS_CONST || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { break; } ce = NULL; + ce_is_instanceof = 0; + op1_indirect = 0; if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; + ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; op1_addr = 0; } else { op1_info = OP1_INFO(); @@ -3744,6 +3751,16 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par break; } op1_addr = OP1_REG_ADDR(); + if (opline->op1_type == IS_VAR + && opline->opcode == ZEND_FETCH_OBJ_W) { + if (orig_op1_type != IS_UNKNOWN + && (orig_op1_type & IS_TRACE_INDIRECT)) { + op1_indirect = 1; + if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr)) { + goto jit_failure; + } + } + } if (orig_op1_type != IS_UNKNOWN && (orig_op1_type & IS_TRACE_REFERENCE)) { if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr, 1)) { @@ -3755,14 +3772,15 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (ssa->var_info && ssa->ops) { if (ssa_op->op1_use >= 0) { zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; - if (op1_ssa->ce && !op1_ssa->is_instanceof && !op1_ssa->ce->create_object) { + if (op1_ssa->ce && !op1_ssa->ce->create_object) { ce = op1_ssa->ce; + ce_is_instanceof = op1_ssa->is_instanceof; } } } } - if (!zend_jit_fetch_obj_read(&dasm_state, opline, op_array, - op1_info, op1_addr, ce, + if (!zend_jit_fetch_obj(&dasm_state, opline, op_array, + op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof, zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index b4306f5bdb..29d462b4e3 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -10712,10 +10712,12 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zen #define ZEND_WRONG_PROPERTY_OFFSET 0 -static uint32_t zend_get_known_property_offset(zend_class_entry *ce, zend_string *member, zend_bool on_this, zend_string *filename) +static uint32_t zend_get_known_property_offset(zend_class_entry *ce, zend_string *member, zend_bool on_this, zend_string *filename, zend_property_info **prop_info) { zend_property_info *info; + *prop_info = NULL; + if (!ce || !(ce->ce_flags & ZEND_ACC_LINKED) || (ce->ce_flags & ZEND_ACC_TRAIT)) { return ZEND_WRONG_PROPERTY_OFFSET; } @@ -10756,6 +10758,7 @@ static uint32_t zend_get_known_property_offset(zend_class_entry *ce, zend_string return ZEND_WRONG_PROPERTY_OFFSET; } + *prop_info = info; return info->offset; } @@ -10787,10 +10790,11 @@ static zend_bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string return 0; } -static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, zend_class_entry *ce, int may_throw) +static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, zend_bool op1_indirect, zend_class_entry *ce, zend_bool ce_is_instanceof, int may_throw) { zval *member; uint32_t offset; + zend_property_info *prop_info; zend_bool may_be_dynamic = 1; zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); @@ -10801,13 +10805,25 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons member = RT_CONSTANT(opline, opline->op2); ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); - offset = zend_get_known_property_offset(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename); + offset = zend_get_known_property_offset(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename, &prop_info); if (opline->op1_type == IS_UNUSED) { | GET_ZVAL_PTR FCARG1a, this_addr } else { - if (op1_info & MAY_BE_REF) { + if (opline->op1_type == IS_VAR + && opline->opcode == ZEND_FETCH_OBJ_W + && (op1_info & MAY_BE_INDIRECT) + && Z_REG(op1_addr) == ZREG_FP) { | LOAD_ZVAL_ADDR FCARG1a, op1_addr + | IF_NOT_Z_TYPE FCARG1a, IS_INDIRECT, >1 + | GET_Z_PTR FCARG1a, FCARG1a + |1: + op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0); + } + if (op1_info & MAY_BE_REF) { + if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG1a, op1_addr + } | ZVAL_DEREF FCARG1a, op1_info op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0); } @@ -10836,12 +10852,48 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons may_be_dynamic = zend_may_be_dynamic_property(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename); if (may_be_dynamic) { | test r0, r0 - | jl >8 // dynamic property + if (opline->opcode == ZEND_FETCH_OBJ_W) { + | jl >5 + } else { + | jl >8 // dynamic property + } } | mov edx, dword [FCARG1a + r0 + 8] | IF_UNDEF dl, >5 | add FCARG1a, r0 prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0); + if (opline->opcode == ZEND_FETCH_OBJ_W + && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS) + && (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS))) { + uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; + + | mov r0, EX->run_time_cache + | mov FCARG2a, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2] + | test FCARG2a, FCARG2a + | jnz >1 + |.cold_code + |1: + if (flags == ZEND_FETCH_DIM_WRITE) { + | SAVE_VALID_OPLINE opline, r0 + | EXT_CALL zend_jit_check_array_promotion, r0 + | jmp >9 + } else if (flags == ZEND_FETCH_REF) { + |.if X64 + | LOAD_ZVAL_ADDR CARG3, res_addr + |.else + | sub r4, 12 + | PUSH_ZVAL_ADDR res_addr, r0 + |.endif + | EXT_CALL zend_jit_create_typed_ref, r0 + |.if not(X64) + | add r4, 12 + |.endif + | jmp >9 + } else { + ZEND_UNREACHABLE(); + } + |.code + } } else { prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, offset); | mov edx, dword [FCARG1a + offset + 8] @@ -10856,9 +10908,59 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons } else { | IF_UNDEF dl, >5 } + if (opline->opcode == ZEND_FETCH_OBJ_W + && (opline->extended_value & ZEND_FETCH_OBJ_FLAGS) + && ZEND_TYPE_IS_SET(prop_info->type)) { + uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; + + if (flags == ZEND_FETCH_DIM_WRITE) { + if ((ZEND_TYPE_FULL_MASK(prop_info->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) { + | cmp dl, IS_FALSE + | jle >1 + |.cold_code + |1: + if (Z_REG(prop_addr) != ZREG_FCARG1a || Z_OFFSET(prop_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG1a, prop_addr + } + | LOAD_ADDR FCARG2a, prop_info + | SAVE_VALID_OPLINE opline, r0 + | EXT_CALL zend_jit_check_array_promotion, r0 + | jmp >9 + |.code + } + } else if (flags == ZEND_FETCH_REF) { + | IF_TYPE dl, IS_REFERENCE, >1 + if (Z_REG(prop_addr) != ZREG_FCARG1a || Z_OFFSET(prop_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG1a, prop_addr + } + | LOAD_ADDR FCARG2a, prop_info + |.if X64 + | LOAD_ZVAL_ADDR CARG3, res_addr + |.else + | sub r4, 12 + | PUSH_ZVAL_ADDR res_addr, r0 + |.endif + | EXT_CALL zend_jit_create_typed_ref, r0 + |.if not(X64) + | add r4, 12 + |.endif + | jmp >9 + |1: + } else { + ZEND_UNREACHABLE(); + } + } } - if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_R2)) { - return 0; + if (opline->opcode == ZEND_FETCH_OBJ_W) { + if (Z_REG(prop_addr) != ZREG_FCARG1a || Z_OFFSET(prop_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG1a, prop_addr + } + | SET_ZVAL_PTR res_addr, FCARG1a + | SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT + } else { + if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_R2)) { + return 0; + } } |.cold_code @@ -10875,7 +10977,9 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons | PUSH_ZVAL_ADDR res_addr, r0 |.endif | SAVE_VALID_OPLINE opline, r0 - if (opline->opcode != ZEND_FETCH_OBJ_IS) { + if (opline->opcode == ZEND_FETCH_OBJ_W) { + | EXT_CALL zend_jit_fetch_obj_w_slow, r0 + } else if (opline->opcode != ZEND_FETCH_OBJ_IS) { | EXT_CALL zend_jit_fetch_obj_r_slow, r0 } else { | EXT_CALL zend_jit_fetch_obj_is_slow, r0 @@ -10890,7 +10994,8 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons |7: if (opline->opcode != ZEND_FETCH_OBJ_IS) { | SAVE_VALID_OPLINE opline, r1 - if (op1_info & MAY_BE_UNDEF) { + if (opline->opcode != ZEND_FETCH_OBJ_W + && (op1_info & MAY_BE_UNDEF)) { zend_jit_addr orig_op1_addr = OP1_ADDR(); if (op1_info & MAY_BE_ANY) { @@ -10900,17 +11005,27 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons | EXT_CALL zend_jit_undefined_op_helper, r0 |1: | LOAD_ZVAL_ADDR FCARG1a, orig_op1_addr - } else { + } else if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) { | LOAD_ZVAL_ADDR FCARG1a, op1_addr } | LOAD_ADDR FCARG2a, Z_STRVAL_P(member) - | EXT_CALL zend_jit_invalid_property_read, r0 + if (opline->opcode == ZEND_FETCH_OBJ_W) { + | EXT_CALL zend_jit_invalid_property_write, r0 + | SET_ZVAL_TYPE_INFO res_addr, _IS_ERROR + } else { + | EXT_CALL zend_jit_invalid_property_read, r0 + | SET_ZVAL_TYPE_INFO res_addr, IS_NULL + } + | jmp >9 + } else { + | SET_ZVAL_TYPE_INFO res_addr, IS_NULL + | jmp >9 } - | SET_ZVAL_TYPE_INFO res_addr, IS_NULL - | jmp >9 } - if (offset == ZEND_WRONG_PROPERTY_OFFSET && may_be_dynamic) { + if (offset == ZEND_WRONG_PROPERTY_OFFSET + && may_be_dynamic + && opline->opcode != ZEND_FETCH_OBJ_W) { |8: | mov FCARG2a, r0 |.if X64WIN @@ -10941,7 +11056,22 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, const zend_op *opline, cons |.code; |9: // END - | FREE_OP opline->op1_type, opline->op1, op1_info, 1, op_array, opline + if (!op1_indirect) { + if (opline->op1_type == IS_VAR + && opline->opcode == ZEND_FETCH_OBJ_W) { + zend_jit_addr orig_op1_addr = OP1_ADDR(); + + | IF_NOT_ZVAL_REFCOUNTED orig_op1_addr, >1 + | GET_ZVAL_PTR FCARG1a, orig_op1_addr + | GC_DELREF FCARG1a + | jnz >1 + | SAVE_VALID_OPLINE opline, r0 + | EXT_CALL zend_jit_extract_helper, r0 + |1: + } else { + | FREE_OP opline->op1_type, opline->op1, op1_info, 1, op_array, opline + } + } if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && offset != ZEND_WRONG_PROPERTY_OFFSET @@ -11408,6 +11538,51 @@ static zend_bool zend_jit_fetch_reference(dasm_State **Dst, const zend_op *oplin return 1; } +static zend_bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr) +{ + zend_jit_addr var_addr = *var_addr_ptr; + uint32_t var_info = *var_info_ptr; + int32_t exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, 0); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + | IF_NOT_ZVAL_TYPE var_addr, IS_INDIRECT, &exit_addr + | GET_ZVAL_PTR FCARG1a, var_addr + var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0); + *var_addr_ptr = var_addr; + + var_type &= ~IS_TRACE_INDIRECT; + if (!(var_type & IS_TRACE_REFERENCE) + && var_type != IS_UNKNOWN + && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) { + exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + | IF_NOT_Z_TYPE FCARG1a, var_type, &exit_addr + + //var_info = zend_jit_trace_type_to_info_ex(var_type, var_info); + ZEND_ASSERT(var_info & (1 << var_type)); + if (var_type < IS_STRING) { + var_info = (1 << var_type); + } else if (var_type != IS_ARRAY) { + var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN)); + } else { + var_info = MAY_BE_ARRAY | (var_info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY|MAY_BE_RC1|MAY_BE_RCN)); + } + + *var_info_ptr = var_info; + } + + return 1; +} + static zend_bool zend_jit_may_reuse_reg(const zend_op *opline, const zend_ssa_op *ssa_op, zend_ssa *ssa, int def_var, int use_var) { if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) { -- 2.50.1