diff options
-rw-r--r-- | Zend/zend_execute.c | 2 | ||||
-rw-r--r-- | Zend/zend_execute.h | 2 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit.c | 43 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_disasm_x86.c | 3 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_helpers.c | 38 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_trace.c | 78 | ||||
-rw-r--r-- | ext/opcache/jit/zend_jit_x86.dasc | 383 |
7 files changed, 518 insertions, 31 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 589a0bf499..25c36d537b 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -919,7 +919,7 @@ static zend_always_inline zend_bool i_zend_verify_property_type(zend_property_in return 0; } -zend_bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) { +ZEND_API zend_bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) { return i_zend_verify_property_type(info, property, strict); } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 35f4ee588a..43b88dbd2c 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -418,7 +418,7 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call); #define ZEND_CLASS_HAS_TYPE_HINTS(ce) ((ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) == ZEND_ACC_HAS_TYPE_HINTS) -zend_bool zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict); +ZEND_API zend_bool zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict); ZEND_COLD void zend_verify_property_type_error(zend_property_info *info, zval *property); #define ZEND_REF_ADD_TYPE_SOURCE(ref, source) \ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index d514428840..2ed83d0efe 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2457,6 +2457,49 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto jit_failure; } goto done; + case ZEND_ASSIGN_OBJ: + if (opline->op1_type == IS_VAR) { + break; + } + 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; + } + if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { + break; + } + ce = NULL; + ce_is_instanceof = 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(); + if (!(op1_info & MAY_BE_OBJECT)) { + break; + } + op1_addr = OP1_REG_ADDR(); + if (ssa->var_info && ssa->ops) { + 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->ce->create_object) { + ce = op1_ssa->ce; + ce_is_instanceof = op1_ssa->is_instanceof; + } + } + } + } + if (!zend_jit_assign_obj(&dasm_state, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, OP1_DATA_INFO(), + 0, ce, ce_is_instanceof, 0, NULL, + zend_may_throw(opline, ssa_op, op_array, ssa))) { + goto jit_failure; + } + goto done; case ZEND_ASSIGN: if (opline->op1_type != IS_CV) { break; diff --git a/ext/opcache/jit/zend_jit_disasm_x86.c b/ext/opcache/jit/zend_jit_disasm_x86.c index b044d63235..ffe290dc4b 100644 --- a/ext/opcache/jit/zend_jit_disasm_x86.c +++ b/ext/opcache/jit/zend_jit_disasm_x86.c @@ -456,6 +456,7 @@ static int zend_jit_disasm_init(void) 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_invalid_property_assign); REGISTER_HELPER(zend_jit_prepare_assign_dim_ref); REGISTER_HELPER(zend_jit_pre_inc); REGISTER_HELPER(zend_jit_pre_dec); @@ -466,6 +467,8 @@ static int zend_jit_disasm_init(void) REGISTER_HELPER(zend_jit_array_free); REGISTER_HELPER(zend_jit_zval_array_dup); REGISTER_HELPER(zend_jit_add_arrays_helper); + REGISTER_HELPER(zend_jit_assign_obj_helper); + REGISTER_HELPER(zend_jit_assign_to_typed_prop); #undef REGISTER_HELPER #ifndef _WIN32 diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 42d98e383e..995b69c57e 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1858,6 +1858,13 @@ static void ZEND_FASTCALL zend_jit_invalid_property_write(zval *container, const property_name, zend_zval_type_name(container)); } +static void ZEND_FASTCALL zend_jit_invalid_property_assign(zval *container, const char *property_name) +{ + zend_throw_error(NULL, + "Attempt to assign 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) { @@ -1927,3 +1934,34 @@ static zend_array *ZEND_FASTCALL zend_jit_add_arrays_helper(zend_array *op1, zen zend_hash_merge(res, op2, zval_add_ref, 0); return res; } + +static void ZEND_FASTCALL zend_jit_assign_obj_helper(zend_object *zobj, zend_string *name, zval *value, void **cache_slot, zval *result) +{ + ZVAL_DEREF(value); + value = zobj->handlers->write_property(zobj, name, value, cache_slot); + if (result) { + ZVAL_COPY_DEREF(result, value); + } +} + +static void ZEND_FASTCALL zend_jit_assign_to_typed_prop(zval *property_val, zend_property_info *info, zval *value, zval *result) +{ + zend_execute_data *execute_data = EG(current_execute_data); + zval tmp; + + ZVAL_DEREF(value); + ZVAL_COPY(&tmp, value); + + if (UNEXPECTED(!zend_verify_property_type(info, &tmp, EX_USES_STRICT_TYPES()))) { + zval_ptr_dtor(&tmp); + if (result) { + ZVAL_NULL(result); + } + return; + } + + value = zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES()); + if (result) { + ZVAL_COPY_DEREF(result, value); + } +} diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 817c23d820..30aa0a71fc 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1426,6 +1426,15 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin ADD_OP2_TRACE_GUARD(); } break; + case ZEND_ASSIGN_OBJ: + 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; + } +// ADD_OP1_DATA_TRACE_GUARD(); + ADD_OP1_TRACE_GUARD(); + break; case ZEND_IS_EQUAL: case ZEND_IS_NOT_EQUAL: case ZEND_IS_SMALLER: @@ -2795,7 +2804,8 @@ static zend_bool zend_jit_may_delay_fetch_this(zend_ssa *ssa, const zend_op **ss } } else if (opline->opcode != ZEND_FETCH_OBJ_R && opline->opcode != ZEND_FETCH_OBJ_IS - && opline->opcode != ZEND_FETCH_OBJ_W) { + && opline->opcode != ZEND_FETCH_OBJ_W + && opline->opcode != ZEND_ASSIGN_OBJ) { return 0; } @@ -3708,6 +3718,72 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } goto done; + case ZEND_ASSIGN_OBJ: + 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; + delayed_fetch_this = 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 { + if (ssa_op->op1_use >= 0) { + delayed_fetch_this = ssa->var_info[ssa_op->op1_use].delayed_fetch_this; + } + op1_info = OP1_INFO(); + if (!(op1_info & MAY_BE_OBJECT)) { + break; + } + op1_addr = OP1_REG_ADDR(); + 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(&dasm_state, opline, orig_op1_type, + &op1_info, &op1_addr, !ssa->var_info[ssa_op->op1_use].indirect_reference)) { + 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, + !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) { + goto jit_failure; + } + if (opline->op1_type == IS_CV + && zend_jit_var_may_alias(op_array, op_array_ssa, EX_VAR_TO_NUM(opline->op1.var)) == NO_ALIAS) { + ssa->var_info[ssa_op->op1_use].guarded_reference = 1; + } + } else { + CHECK_OP1_TRACE_TYPE(); + } + 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->ce->create_object) { + ce = op1_ssa->ce; + ce_is_instanceof = op1_ssa->is_instanceof; + } + } + } + } + op1_data_info = OP1_DATA_INFO(); + CHECK_OP1_DATA_TRACE_TYPE(); + if (!zend_jit_assign_obj(&dasm_state, opline, op_array, ssa, ssa_op, + op1_info, op1_addr, op1_data_info, + op1_indirect, ce, ce_is_instanceof, delayed_fetch_this, op1_ce, + zend_may_throw(opline, ssa_op, op_array, ssa))) { + goto jit_failure; + } + goto done; case ZEND_ASSIGN_DIM: op1_info = OP1_INFO(); op1_addr = OP1_REG_ADDR(); diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 78a3cd027c..e5e1d16a44 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -5848,6 +5848,52 @@ static int zend_jit_assign_to_typed_ref(dasm_State **Dst, return 1; } +static int zend_jit_assign_to_variable_call(dasm_State **Dst, + const zend_op *opline, + zend_jit_addr __var_use_addr, + zend_jit_addr var_addr, + uint32_t __var_info, + uint32_t __var_def_info, + zend_uchar val_type, + zend_jit_addr val_addr, + uint32_t val_info, + zend_jit_addr __res_addr, + zend_bool __check_exception) +{ + if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1a || Z_OFFSET(var_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG1a, var_addr + } + if (Z_MODE(val_addr) != IS_MEM_ZVAL || Z_REG(val_addr) != ZREG_FCARG2a || Z_OFFSET(val_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG2a, val_addr + } + if (opline) { + | SET_EX_OPLINE opline, r0 + } + if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { + | call ->assign_tmp + } else if (val_type == IS_CONST) { + | call ->assign_const + } else if (val_type == IS_TMP_VAR) { + | call ->assign_tmp + } else if (val_type == IS_VAR) { + if (!(val_info & MAY_BE_REF)) { + | call ->assign_tmp + } else { + | call ->assign_var + } + } else if (val_type == IS_CV) { + if (!(val_info & MAY_BE_REF)) { + | call ->assign_cv_noref + } else { + | call ->assign_cv + } + } else { + ZEND_UNREACHABLE(); + } + + return 1; +} + static int zend_jit_assign_to_variable(dasm_State **Dst, const zend_op *opline, zend_jit_addr var_use_addr, @@ -6114,36 +6160,13 @@ static int zend_jit_assign_dim(dasm_State **Dst, const zend_op *opline, uint32_t | // value = zend_assign_to_variable(variable_ptr, value, OP_DATA_TYPE); if (opline->op1_type == IS_VAR) { ZEND_ASSERT(opline->result_type == IS_UNUSED); - if (Z_MODE(var_addr) != IS_MEM_ZVAL || Z_REG(var_addr) != ZREG_FCARG1a || Z_OFFSET(var_addr) != 0) { - | LOAD_ZVAL_ADDR FCARG1a, var_addr - } - if (Z_MODE(op3_addr) != IS_MEM_ZVAL || Z_REG(op3_addr) != ZREG_FCARG2a || Z_OFFSET(op3_addr) != 0) { - | LOAD_ZVAL_ADDR FCARG2a, op3_addr + if (!zend_jit_assign_to_variable_call(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) { + return 0; } - | SET_EX_OPLINE opline, r0 - if (!(val_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) { - | call ->assign_tmp - } else if ((opline+1)->op1_type == IS_CONST) { - | call ->assign_const - } else if ((opline+1)->op1_type == IS_TMP_VAR) { - | call ->assign_tmp - } else if ((opline+1)->op1_type == IS_VAR) { - if (!(val_info & MAY_BE_REF)) { - | call ->assign_tmp - } else { - | call ->assign_var - } - } else if ((opline+1)->op1_type == IS_CV) { - if (!(val_info & MAY_BE_REF)) { - | call ->assign_cv_noref - } else { - | call ->assign_cv - } - } else { - ZEND_UNREACHABLE(); + } else { + if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) { + return 0; } - } else if (!zend_jit_assign_to_variable(Dst, opline, var_addr, var_addr, var_info, -1, (opline+1)->op1_type, op3_addr, val_info, res_addr, 0)) { - return 0; } } } @@ -12277,6 +12300,310 @@ static int zend_jit_fetch_obj(dasm_State **Dst, return 1; } +static int zend_jit_assign_obj(dasm_State **Dst, + 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 val_info, + zend_bool op1_indirect, + zend_class_entry *ce, + zend_bool ce_is_instanceof, + zend_bool use_this, + zend_class_entry *trace_ce, + int may_throw) +{ + zval *member; + zend_string *name; + zend_property_info *prop_info; + zend_jit_addr val_addr = OP1_DATA_ADDR(); + zend_jit_addr res_addr = 0; + zend_jit_addr this_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, offsetof(zend_execute_data, This)); + zend_jit_addr prop_addr; + + if (RETURN_VALUE_USED(opline)) { + res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); + } + + ZEND_ASSERT(opline->op2_type == IS_CONST); + ZEND_ASSERT(op1_info & MAY_BE_OBJECT); + + member = RT_CONSTANT(opline, opline->op2); + ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); + name = Z_STR_P(member); + prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + + if (opline->op1_type == IS_UNUSED || use_this) { + | GET_ZVAL_PTR FCARG1a, this_addr + } else { + if (opline->op1_type == IS_VAR + && (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); + } + if (op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)- MAY_BE_OBJECT)) { + if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { + int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, &exit_addr + } else { + | IF_NOT_ZVAL_TYPE op1_addr, IS_OBJECT, >1 + |.cold_code + |1: + | SET_EX_OPLINE opline, r0 + if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) { + | LOAD_ZVAL_ADDR FCARG1a, op1_addr + } + | LOAD_ADDR FCARG2a, ZSTR_VAL(name) + | EXT_CALL zend_jit_invalid_property_assign, r0 + if (RETURN_VALUE_USED(opline)) { + | SET_ZVAL_TYPE_INFO res_addr, IS_NULL + } + if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR)) + && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { + | jmp >8 + } else { + | jmp ->exception_handler + } + |.code + } + } + | GET_ZVAL_PTR FCARG1a, op1_addr + } + + if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { + prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + if (prop_info) { + ce = trace_ce; + ce_is_instanceof = 0; + if (!(op1_info & MAY_BE_CLASS_GUARD)) { + if (!zend_jit_class_guard(Dst, opline, trace_ce)) { + return 0; + } + if (ssa->var_info && ssa_op->op1_use >= 0) { + ssa->var_info[ssa_op->op1_use].type |= MAY_BE_CLASS_GUARD; + } + } + } + } + + if (!prop_info) { + | mov r0, EX->run_time_cache + | mov r2, aword [r0 + opline->extended_value] + | cmp r2, aword [FCARG1a + offsetof(zend_object, ce)] + | jne >5 + if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) { + | mov FCARG2a, aword [r0 + (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2] + } + | mov r0, aword [r0 + opline->extended_value + sizeof(void*)] + | test r0, r0 + | jl >5 + | IF_TYPE byte [FCARG1a + r0 + 8], IS_UNDEF, >5 + | add FCARG1a, r0 + prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0); + if (!ce || ce_is_instanceof || (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS)) { + | test FCARG2a, FCARG2a + | jnz >1 + |.cold_code + |1: + | // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC); + | SET_EX_OPLINE opline, r0 + |.if X64 + | LOAD_ZVAL_ADDR CARG3, val_addr + if (RETURN_VALUE_USED(opline)) { + | LOAD_ZVAL_ADDR CARG4, res_addr + } else { + | xor CARG4, CARG4 + } + |.else + | sub r4, 8 + if (RETURN_VALUE_USED(opline)) { + | PUSH_ZVAL_ADDR res_addr, r0 + } else { + | push 0 + } + | PUSH_ZVAL_ADDR val_addr, r0 + |.endif + + | EXT_CALL zend_jit_assign_to_typed_prop, r0 + + |.if not(X64) + | add r4, 8 + |.endif + + if ((opline+1)->op1_type == IS_CONST) { + | // TODO: ??? + | // if (Z_TYPE_P(value) == orig_type) { + | // CACHE_PTR_EX(cache_slot + 2, NULL); + } + + if (((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR)) + && (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { + | jmp >8 + } else { + | jmp >9 + } + |.code + } + } else { + prop_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, prop_info->offset); + // Undefined property with magic __get()/__set() + if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { + int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + | IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, &exit_addr + } else { + | IF_TYPE byte [FCARG1a + prop_info->offset + 8], IS_UNDEF, >5 + } + if (ZEND_TYPE_IS_SET(prop_info->type)) { + uint32_t info = val_info; + + | // value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC); + | SET_EX_OPLINE opline, r0 + if (ce && ce->ce_flags & ZEND_ACC_IMMUTABLE) { + | LOAD_ADDR FCARG2a, prop_info + } else { + int prop_info_offset = + (((prop_info->offset - (sizeof(zend_object) - sizeof(zval))) / sizeof(zval)) * sizeof(void*)); + + | mov r0, aword [FCARG1a + offsetof(zend_object, ce)] + | mov r0, aword [r0 + offsetof(zend_class_entry, properties_info_table)] + | mov FCARG2a, aword[r0 + prop_info_offset] + } + | LOAD_ZVAL_ADDR FCARG1a, prop_addr + |.if X64 + | LOAD_ZVAL_ADDR CARG3, val_addr + if (RETURN_VALUE_USED(opline)) { + | LOAD_ZVAL_ADDR CARG4, res_addr + } else { + | xor CARG4, CARG4 + } + |.else + | sub r4, 8 + if (RETURN_VALUE_USED(opline)) { + | PUSH_ZVAL_ADDR res_addr, r0 + } else { + | push 0 + } + | PUSH_ZVAL_ADDR val_addr, r0 + |.endif + + | EXT_CALL zend_jit_assign_to_typed_prop, r0 + + |.if not(X64) + | add r4, 8 + |.endif + + if (info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { + info |= MAY_BE_RC1|MAY_BE_RCN; + } + + | FREE_OP (opline+1)->op1_type, (opline+1)->op1, info, 0, opline + } + } + + if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) { + // value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES()); + if (opline->result_type == IS_UNUSED) { + if (!zend_jit_assign_to_variable_call(Dst, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) { + return 0; + } + } else { + if (!zend_jit_assign_to_variable(Dst, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) { + return 0; + } + } + } + + if (!prop_info || JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE) { + |.cold_code + |5: + | SET_EX_OPLINE opline, r0 + | // value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value)); + | LOAD_ADDR FCARG2a, name + |.if X64 + | LOAD_ZVAL_ADDR CARG3, val_addr + | mov CARG4, EX->run_time_cache + | add CARG4, opline->extended_value + if (RETURN_VALUE_USED(opline)) { + |.if X64WIN + | LOAD_ZVAL_ADDR r0, res_addr + | mov aword A5, r0 + |.else + | LOAD_ZVAL_ADDR CARG5, res_addr + |.endif + } else { + |.if X64WIN + | mov aword A5, 0 + |.else + | xor CARG5, CARG5 + |.endif + } + |.else + | sub r4, 4 + if (RETURN_VALUE_USED(opline)) { + | PUSH_ZVAL_ADDR res_addr, r0 + } else { + | push 0 + } + | mov r0, EX->run_time_cache + | add r0, opline->extended_value + | push r0 + | PUSH_ZVAL_ADDR val_addr, r0 + |.endif + + | EXT_CALL zend_jit_assign_obj_helper, r0 + + |.if not(X64) + | add r4, 4 + |.endif + + if (val_info & (MAY_BE_REF|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { + val_info |= MAY_BE_RC1|MAY_BE_RCN; + } + + |8: + | // FREE_OP_DATA(); + | FREE_OP (opline+1)->op1_type, (opline+1)->op1, val_info, 0, opline + | jmp >9 + |.code + } + + |9: + if (opline->op1_type != IS_UNUSED && !use_this && !op1_indirect) { + | FREE_OP opline->op1_type, opline->op1, op1_info, 1, opline + } + + if (may_throw) { + if (!zend_jit_check_exception(Dst)) { + return 0; + } + } + + return 1; +} + static int zend_jit_free(dasm_State **Dst, const zend_op *opline, uint32_t op1_info, int may_throw) { zend_jit_addr op1_addr = OP1_ADDR(); |