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++;
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)) {
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;
}
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);
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);
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);
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) {
/* 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') {
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;
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)) {
/* 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();
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)) {
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;
}
#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;
}
return ZEND_WRONG_PROPERTY_OFFSET;
}
+ *prop_info = info;
return info->offset;
}
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));
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);
}
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]
} 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
| 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
|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) {
| 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
|.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
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)) {