Skip to content

JIT: Add CPU registers support for FETCH_OBJ_R #14253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 19, 2024
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
2 changes: 1 addition & 1 deletion ext/opcache/jit/zend_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -2338,7 +2338,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
}
if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op,
op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
IS_UNKNOWN,
RES_REG_ADDR(), IS_UNKNOWN,
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
}
Expand Down
104 changes: 104 additions & 0 deletions ext/opcache/jit/zend_jit_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1956,6 +1956,110 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intpt
zend_jit_fetch_obj_is_slow(zobj);
}

static zval* ZEND_FASTCALL zend_jit_fetch_obj_r_slow_ex(zend_object *zobj)
{
zval *retval;
zend_execute_data *execute_data = EG(current_execute_data);
const zend_op *opline = EX(opline);
zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
zval *result = EX_VAR(opline->result.var);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);

retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, result);
if (retval == result && UNEXPECTED(Z_ISREF_P(retval))) {
zend_unwrap_reference(retval);
}
return retval;
}

static zval* ZEND_FASTCALL zend_jit_fetch_obj_r_dynamic_ex(zend_object *zobj, intptr_t prop_offset)
{
if (zobj->properties) {
zval *retval;
zend_execute_data *execute_data = EG(current_execute_data);
const zend_op *opline = EX(opline);
zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);

if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
intptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);

if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);

if (EXPECTED(p->key == name) ||
(EXPECTED(p->h == ZSTR_H(name)) &&
EXPECTED(p->key != NULL) &&
EXPECTED(zend_string_equal_content(p->key, name)))) {
return &p->val;
}
}
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
}

retval = zend_hash_find_known_hash(zobj->properties, name);

if (EXPECTED(retval)) {
intptr_t idx = (char*)retval - (char*)zobj->properties->arData;
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
return retval;
}
}
return zend_jit_fetch_obj_r_slow_ex(zobj);
}

static zval* ZEND_FASTCALL zend_jit_fetch_obj_is_slow_ex(zend_object *zobj)
{
zval *retval;
zend_execute_data *execute_data = EG(current_execute_data);
const zend_op *opline = EX(opline);
zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
zval *result = EX_VAR(opline->result.var);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);

retval = zobj->handlers->read_property(zobj, name, BP_VAR_IS, cache_slot, result);
if (retval == result && UNEXPECTED(Z_ISREF_P(retval))) {
zend_unwrap_reference(retval);
}
return retval;
}

static zval* ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic_ex(zend_object *zobj, intptr_t prop_offset)
{
if (zobj->properties) {
zval *retval;
zend_execute_data *execute_data = EG(current_execute_data);
const zend_op *opline = EX(opline);
zend_string *name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS);

if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(prop_offset)) {
intptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(prop_offset);

if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);

if (EXPECTED(p->key == name) ||
(EXPECTED(p->h == ZSTR_H(name)) &&
EXPECTED(p->key != NULL) &&
EXPECTED(zend_string_equal_content(p->key, name)))) {
return &p->val;
}
}
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
}

retval = zend_hash_find_known_hash(zobj->properties, name);

if (EXPECTED(retval)) {
intptr_t idx = (char*)retval - (char*)zobj->properties->arData;
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
return retval;
}
}
return zend_jit_fetch_obj_is_slow_ex(zobj);
}

static zend_always_inline 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);
Expand Down
121 changes: 84 additions & 37 deletions ext/opcache/jit/zend_jit_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -3039,9 +3039,13 @@ static void zend_jit_setup_disasm(void)
REGISTER_HELPER(zend_jit_assign_dim_op_helper);
REGISTER_HELPER(zend_jit_fetch_obj_w_slow);
REGISTER_HELPER(zend_jit_fetch_obj_r_slow);
REGISTER_HELPER(zend_jit_fetch_obj_r_slow_ex);
REGISTER_HELPER(zend_jit_fetch_obj_is_slow);
REGISTER_HELPER(zend_jit_fetch_obj_is_slow_ex);
REGISTER_HELPER(zend_jit_fetch_obj_r_dynamic);
REGISTER_HELPER(zend_jit_fetch_obj_r_dynamic_ex);
REGISTER_HELPER(zend_jit_fetch_obj_is_dynamic);
REGISTER_HELPER(zend_jit_fetch_obj_is_dynamic_ex);
REGISTER_HELPER(zend_jit_check_array_promotion);
REGISTER_HELPER(zend_jit_create_typed_ref);
REGISTER_HELPER(zend_jit_invalid_property_write);
Expand Down Expand Up @@ -13662,20 +13666,21 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
bool delayed_fetch_this,
bool op1_avoid_refcounting,
zend_class_entry *trace_ce,
zend_jit_addr res_addr,
uint8_t prop_type,
int may_throw)
{
zval *member;
zend_property_info *prop_info;
bool may_be_dynamic = 1;
zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
zend_jit_addr prop_addr;
uint32_t res_info = RES_INFO();
ir_ref prop_type_ref = IR_UNUSED;
ir_ref obj_ref = IR_UNUSED;
ir_ref prop_ref = IR_UNUSED;
ir_ref end_inputs = IR_UNUSED;
ir_ref slow_inputs = IR_UNUSED;
ir_ref end_values = IR_UNUSED;

ZEND_ASSERT(opline->op2_type == IS_CONST);
ZEND_ASSERT(op1_info & MAY_BE_OBJECT);
Expand Down Expand Up @@ -13804,13 +13809,28 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
jit_SET_EX_OPLINE(jit, opline);

if (opline->opcode != ZEND_FETCH_OBJ_IS) {
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_r_dynamic),
obj_ref, offset_ref);
if (((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info)
|| Z_MODE(res_addr) == IS_REG) {
ir_ref val_addr = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_obj_r_dynamic_ex),
obj_ref, offset_ref);
ir_END_PHI_list(end_values, val_addr);
} else {
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_r_dynamic),
obj_ref, offset_ref);
ir_END_list(end_inputs);
}
} else {
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_is_dynamic),
obj_ref, offset_ref);
if (((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info)
|| Z_MODE(res_addr) == IS_REG) {
ir_ref val_addr = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_obj_is_dynamic_ex),
obj_ref, offset_ref);
ir_END_PHI_list(end_values, val_addr);
} else {
ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_is_dynamic),
obj_ref, offset_ref);
ir_END_list(end_inputs);
}
}
ir_END_list(end_inputs);
}
ir_IF_FALSE(if_dynamic);
}
Expand Down Expand Up @@ -13996,12 +14016,56 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
}
ir_END_list(end_inputs);
} else {
if (((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info)
|| Z_MODE(res_addr) == IS_REG) {
ir_END_PHI_list(end_values, jit_ZVAL_ADDR(jit, prop_addr));
} else {
prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr);

if (!zend_jit_zval_copy_deref(jit, res_addr, prop_addr, prop_type_ref)) {
return 0;
}
ir_END_list(end_inputs);
}
}

if (op1_avoid_refcounting) {
SET_STACK_REG(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
}

if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) {
ir_MERGE_list(slow_inputs);
jit_SET_EX_OPLINE(jit, opline);

if (opline->opcode == ZEND_FETCH_OBJ_W) {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_w_slow), obj_ref);
ir_END_list(end_inputs);
} else if (opline->opcode != ZEND_FETCH_OBJ_IS) {
if (Z_MODE(res_addr) == IS_REG) {
ir_ref val_ref = ir_CALL_1(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_obj_r_slow_ex), obj_ref);
ir_END_PHI_list(end_values, val_ref);
} else {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_r_slow), obj_ref);
ir_END_list(end_inputs);
}
} else {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_is_slow), obj_ref);
ir_END_list(end_inputs);
}
}

if (end_values) {
ir_ref val_ref = ir_PHI_list(end_values);
zend_jit_addr val_addr = ZEND_ADDR_REF_ZVAL(val_ref);
bool result_avoid_refcounting = 0;

if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame) && prop_info) {
ZEND_ASSERT(opline->opcode == ZEND_FETCH_OBJ_R
|| opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG
|| opline->opcode == ZEND_FETCH_OBJ_IS);
ZEND_ASSERT(end_inputs == IR_UNUSED);
if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
uint8_t type = concrete_type(res_info);
uint32_t flags = 0;
zend_jit_addr val_addr = prop_addr;

if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
&& !delayed_fetch_this
Expand All @@ -14026,39 +14090,14 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,

res_info &= ~MAY_BE_GUARD;
ssa->var_info[ssa_op->result_def].type &= ~MAY_BE_GUARD;

// ZVAL_COPY
jit_ZVAL_COPY(jit, res_addr, -1, val_addr, res_info, !result_avoid_refcounting);
} else {
prop_type_ref = jit_Z_TYPE_INFO(jit, prop_addr);

if (!zend_jit_zval_copy_deref(jit, res_addr, prop_addr, prop_type_ref)) {
return 0;
}
}
ir_END_list(end_inputs);
}

if (op1_avoid_refcounting) {
SET_STACK_REG(JIT_G(current_frame)->stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
}

if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || !prop_info) {
ir_MERGE_list(slow_inputs);
jit_SET_EX_OPLINE(jit, opline);

if (opline->opcode == ZEND_FETCH_OBJ_W) {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_w_slow), obj_ref);
} else if (opline->opcode != ZEND_FETCH_OBJ_IS) {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_r_slow), obj_ref);
} else {
ir_CALL_1(IR_VOID, ir_CONST_FC_FUNC(zend_jit_fetch_obj_is_slow), obj_ref);
}
ir_END_list(end_inputs);
// ZVAL_COPY
jit_ZVAL_COPY(jit, res_addr, -1, val_addr, res_info, !result_avoid_refcounting);
} else {
ir_MERGE_list(end_inputs);
}

ir_MERGE_list(end_inputs);

if (opline->op1_type != IS_UNUSED && !delayed_fetch_this && !op1_indirect) {
if (opline->op1_type == IS_VAR
&& opline->opcode == ZEND_FETCH_OBJ_W
Expand Down Expand Up @@ -16662,6 +16701,14 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa
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));
case ZEND_FETCH_OBJ_R:
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') {
return 0;
}
op1_info = OP1_INFO();
return opline->op1_type == IS_UNUSED || (op1_info & MAY_BE_OBJECT);
}
return 0;
}
Expand Down
4 changes: 3 additions & 1 deletion ext/opcache/jit/zend_jit_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2963,6 +2963,7 @@ static zend_jit_reg_var* zend_jit_trace_allocate_registers(zend_jit_trace_rec *t
|| opline->opcode == ZEND_SUB
|| opline->opcode == ZEND_MUL
|| opline->opcode == ZEND_FETCH_DIM_R
|| opline->opcode == ZEND_FETCH_OBJ_R
|| opline->opcode == ZEND_FETCH_CONSTANT) {
if (!(ssa->var_info[ssa_op->result_def].type & MAY_BE_DOUBLE)
|| (opline->opcode != ZEND_PRE_INC && opline->opcode != ZEND_PRE_DEC)) {
Expand Down Expand Up @@ -6004,7 +6005,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
}
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, val_type,
on_this, delayed_fetch_this, avoid_refcounting, op1_ce,
RES_REG_ADDR(), val_type,
zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, MAY_BE_STRING))) {
goto jit_failure;
}
Expand Down
Loading