Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions ext/opcache/jit/zend_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
Expand Down Expand Up @@ -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)) {
Expand All @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions ext/opcache/jit/zend_jit_disasm_x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
194 changes: 194 additions & 0 deletions ext/opcache/jit/zend_jit_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
24 changes: 21 additions & 3 deletions ext/opcache/jit/zend_jit_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
/* 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') {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -3728,22 +3731,36 @@ 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();
if (!(op1_info & MAY_BE_OBJECT)) {
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)) {
Expand All @@ -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;
}
Expand Down
Loading