Skip to content

JIT: Add CPU registers support for ASSIGN_OBJ and ASSIGN_OBJ_OP #14303

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 4 commits into from
May 23, 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
5 changes: 3 additions & 2 deletions ext/opcache/jit/zend_jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
}
}
if (!zend_jit_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op,
op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_RANGE(),
op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(),
0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) {
goto jit_failure;
}
Expand Down Expand Up @@ -1861,7 +1861,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
}
}
if (!zend_jit_assign_obj(&ctx, opline, op_array, ssa, ssa_op,
op1_info, op1_addr, OP1_DATA_INFO(),
op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(),
(opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0,
0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN,
zend_may_throw(opline, ssa_op, op_array, ssa))) {
goto jit_failure;
Expand Down
165 changes: 144 additions & 21 deletions ext/opcache/jit/zend_jit_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -6254,7 +6254,7 @@ static int zend_jit_assign_to_variable(zend_jit_ctx *jit,
if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr;

if (opline->opcode == ZEND_ASSIGN_DIM) {
if (opline->opcode == ZEND_ASSIGN_DIM || opline->opcode == ZEND_ASSIGN_OBJ) {
real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
} else {
ZEND_ASSERT(opline->opcode == ZEND_ASSIGN);
Expand Down Expand Up @@ -14143,6 +14143,9 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
uint32_t op1_info,
zend_jit_addr op1_addr,
uint32_t val_info,
zend_jit_addr val_addr,
zend_jit_addr val_def_addr,
zend_jit_addr res_addr,
bool op1_indirect,
zend_class_entry *ce,
bool ce_is_instanceof,
Expand All @@ -14155,17 +14158,21 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
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 prop_addr;
ir_ref obj_ref = IR_UNUSED;
ir_ref prop_ref = IR_UNUSED;
ir_ref delayed_end_input = IR_UNUSED;
ir_ref end_inputs = IR_UNUSED;
ir_ref slow_inputs = IR_UNUSED;
uint32_t res_info = RES_INFO();

if (RETURN_VALUE_USED(opline)) {
res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
if (val_addr != val_def_addr && val_def_addr) {
if (!zend_jit_update_regs(jit, (opline+1)->op1.var, val_addr, val_def_addr, val_info)) {
return 0;
}
if (Z_MODE(val_def_addr) == IS_REG && Z_MODE(val_addr) != IS_REG) {
val_addr = val_def_addr;
}
}

ZEND_ASSERT(opline->op2_type == IS_CONST);
Expand Down Expand Up @@ -14206,7 +14213,7 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
jit_ZVAL_ADDR(jit, op1_addr),
ir_CONST_ADDR(ZSTR_VAL(name)));

if (RETURN_VALUE_USED(opline)) {
if (RETURN_VALUE_USED(opline) && Z_MODE(res_addr) != IS_REG) {
jit_set_Z_TYPE_INFO(jit, res_addr, IS_NULL);
}

Expand Down Expand Up @@ -14276,25 +14283,51 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
prop_addr = ZEND_ADDR_REF_ZVAL(prop_ref);

if (!ce || ce_is_instanceof || (ce->ce_flags & (ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_TRAIT))) {
ir_ref arg3, arg4;
ir_ref prop_info_ref = ir_LOAD_A(
ir_ADD_OFFSET(run_time_cache, (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) * 2));
ir_ref if_has_prop_info = ir_IF(prop_info_ref);
ir_IF_TRUE_cold(if_has_prop_info);

if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
if (!zend_jit_spill_store_inv(jit, val_addr, real_addr, val_info)) {
return 0;
}
arg3 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg3 = jit_ZVAL_ADDR(jit, val_addr);
}

if (!RETURN_VALUE_USED(opline)) {
arg4 = IR_NULL;
} else if (Z_MODE(res_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
arg4 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg4 = jit_ZVAL_ADDR(jit, res_addr);
}
// JIT: value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
jit_SET_EX_OPLINE(jit, opline);
ir_CALL_4(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_to_typed_prop),
prop_ref,
prop_info_ref,
jit_ZVAL_ADDR(jit, val_addr),
RETURN_VALUE_USED(opline) ? jit_ZVAL_ADDR(jit, res_addr) : IR_NULL);
arg3,
arg4);

if ((opline+1)->op1_type == IS_CONST) {
// TODO: ???
// if (Z_TYPE_P(value) == orig_type) {
// CACHE_PTR_EX(cache_slot + 2, NULL);
}

if (RETURN_VALUE_USED(opline) && Z_MODE(res_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
if (!zend_jit_load_reg(jit, real_addr, res_addr, res_info)) {
return 0;
}
}

ir_END_list(end_inputs);
ir_IF_FALSE(if_has_prop_info);
}
Expand All @@ -14319,7 +14352,7 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
}
}
if (ZEND_TYPE_IS_SET(prop_info->type)) {
ir_ref ref;
ir_ref ref, arg3, arg4;

// JIT: value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
jit_SET_EX_OPLINE(jit, opline);
Expand All @@ -14333,18 +14366,43 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
ref = ir_LOAD_A(ir_ADD_OFFSET(ref, offsetof(zend_class_entry, properties_info_table)));
ref = ir_LOAD_A(ir_ADD_OFFSET(ref, prop_info_offset));
}
if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
if (!zend_jit_spill_store_inv(jit, val_addr, real_addr, val_info)) {
return 0;
}
arg3 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg3 = jit_ZVAL_ADDR(jit, val_addr);
}
if (!RETURN_VALUE_USED(opline)) {
arg4 = IR_NULL;
} else if (Z_MODE(res_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
arg4 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg4 = jit_ZVAL_ADDR(jit, res_addr);
}
ir_CALL_4(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_to_typed_prop),
prop_ref,
ref,
jit_ZVAL_ADDR(jit, val_addr),
RETURN_VALUE_USED(opline) ? jit_ZVAL_ADDR(jit, res_addr) : IR_NULL);
arg3,
arg4);

if (RETURN_VALUE_USED(opline) && Z_MODE(res_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
if (!zend_jit_load_reg(jit, real_addr, res_addr, res_info)) {
return 0;
}
}
ir_END_list(end_inputs);
}
}

if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) {
if (opline->result_type == IS_UNUSED) {
if (Z_MODE(val_addr) != IS_REG
&& (res_addr == 0 || Z_MODE(res_addr) != IS_REG)
&& opline->result_type == IS_UNUSED) {
if (!zend_jit_assign_to_variable_call(jit, opline, prop_addr, prop_addr, -1, -1, (opline+1)->op1_type, val_addr, val_info, res_addr, 0)) {
return 0;
}
Expand All @@ -14365,18 +14423,44 @@ static int zend_jit_assign_obj(zend_jit_ctx *jit,
}

if (slow_inputs) {
ir_ref arg3, arg5;

ir_MERGE_list(slow_inputs);
jit_SET_EX_OPLINE(jit, opline);

if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
if (!zend_jit_spill_store_inv(jit, val_addr, real_addr, val_info)) {
return 0;
}
arg3 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg3 = jit_ZVAL_ADDR(jit, val_addr);
}
if (!RETURN_VALUE_USED(opline)) {
arg5 = IR_NULL;
} else if (Z_MODE(res_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
arg5 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg5 = jit_ZVAL_ADDR(jit, res_addr);
}

// JIT: value = zobj->handlers->write_property(zobj, name, value, CACHE_ADDR(opline->extended_value));
ir_ref run_time_cache = ir_LOAD_A(jit_EX(run_time_cache));
ir_CALL_5(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_obj_helper),
obj_ref,
ir_CONST_ADDR(name),
jit_ZVAL_ADDR(jit, val_addr),
arg3,
ir_ADD_OFFSET(run_time_cache, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS),
RETURN_VALUE_USED(opline) ? jit_ZVAL_ADDR(jit, res_addr) : IR_NULL);
arg5);

if (RETURN_VALUE_USED(opline) && Z_MODE(res_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
if (!zend_jit_load_reg(jit, real_addr, res_addr, res_info)) {
return 0;
}
}
ir_END_list(end_inputs);
}

Expand Down Expand Up @@ -14412,6 +14496,7 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,
uint32_t op1_info,
zend_jit_addr op1_addr,
uint32_t val_info,
zend_jit_addr val_addr,
zend_ssa_range *val_range,
bool op1_indirect,
zend_class_entry *ce,
Expand All @@ -14424,7 +14509,6 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,
zval *member;
zend_string *name;
zend_property_info *prop_info;
zend_jit_addr val_addr = OP1_DATA_ADDR();
zend_jit_addr prop_addr;
bool use_prop_guard = 0;
bool may_throw = 0;
Expand Down Expand Up @@ -14579,12 +14663,22 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,
}
}
if (ZEND_TYPE_IS_SET(prop_info->type)) {
ir_ref if_ref, if_typed, noref_path, ref_path, reference, ref;
ir_ref if_ref, if_typed, noref_path, ref_path, reference, ref, arg2;

may_throw = 1;

jit_SET_EX_OPLINE(jit, opline);

if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
if (!zend_jit_spill_store_inv(jit, val_addr, real_addr, val_info)) {
return 0;
}
arg2 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg2 = jit_ZVAL_ADDR(jit, val_addr);
}

if_ref = jit_if_Z_TYPE(jit, prop_addr, IS_REFERENCE);
ir_IF_FALSE(if_ref);
noref_path = ir_END();
Expand All @@ -14599,7 +14693,7 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,

ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_op_to_typed_ref),
reference,
jit_ZVAL_ADDR(jit, val_addr),
arg2,
ir_CONST_FC_FUNC(binary_op));

ir_END_list(end_inputs);
Expand All @@ -14623,7 +14717,7 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,
ir_CALL_4(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_op_to_typed_prop),
prop_ref,
ref,
jit_ZVAL_ADDR(jit, val_addr),
arg2,
ir_CONST_FC_FUNC(binary_op));

ir_END_list(end_inputs);
Expand All @@ -14647,7 +14741,7 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,
}

if (var_info & MAY_BE_REF) {
ir_ref if_ref, if_typed, noref_path, ref_path, reference, ref;
ir_ref if_ref, if_typed, noref_path, ref_path, reference, ref, arg2;

may_throw = 1;

Expand All @@ -14665,9 +14759,18 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,

jit_SET_EX_OPLINE(jit, opline);

if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
if (!zend_jit_spill_store_inv(jit, val_addr, real_addr, val_info)) {
return 0;
}
arg2 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg2 = jit_ZVAL_ADDR(jit, val_addr);
}
ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_op_to_typed_ref),
reference,
jit_ZVAL_ADDR(jit, val_addr),
arg2,
ir_CONST_FC_FUNC(binary_op));

ir_END_list(end_inputs);
Expand Down Expand Up @@ -14760,16 +14863,27 @@ static int zend_jit_assign_obj_op(zend_jit_ctx *jit,
}

if (slow_inputs) {
ir_ref arg3;

ir_MERGE_list(slow_inputs);

may_throw = 1;

if (Z_MODE(val_addr) == IS_REG) {
zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var);
if (!zend_jit_spill_store_inv(jit, val_addr, real_addr, val_info)) {
return 0;
}
arg3 = jit_ZVAL_ADDR(jit, real_addr);
} else {
arg3 = jit_ZVAL_ADDR(jit, val_addr);
}
jit_SET_EX_OPLINE(jit, opline);
ir_ref run_time_cache = ir_LOAD_A(jit_EX(run_time_cache));
ir_CALL_5(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_obj_op_helper),
obj_ref,
ir_CONST_ADDR(name),
jit_ZVAL_ADDR(jit, val_addr),
arg3,
ir_ADD_OFFSET(run_time_cache, (opline+1)->extended_value & ~ZEND_FETCH_OBJ_FLAGS),
ir_CONST_FC_FUNC(binary_op));

Expand Down Expand Up @@ -16692,7 +16806,16 @@ 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_ASSIGN_OBJ_OP:
if (opline->result_type != IS_UNUSED) {
return 0;
}
if (!zend_jit_supported_binary_op(opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
return 0;
}
ZEND_FALLTHROUGH;
case ZEND_FETCH_OBJ_R:
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') {
Expand Down
8 changes: 5 additions & 3 deletions ext/opcache/jit/zend_jit_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1843,7 +1843,8 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
|| Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
break;
}
if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
if (opline->opcode == ZEND_ASSIGN_OBJ
|| opline->opcode == ZEND_ASSIGN_OBJ_OP) {
if (opline->op1_type == IS_CV
&& (opline+1)->op1_type == IS_CV
&& (opline+1)->op1.var == opline->op1.var) {
Expand Down Expand Up @@ -4851,7 +4852,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
op1_data_info = OP1_DATA_INFO();
CHECK_OP1_DATA_TRACE_TYPE();
if (!zend_jit_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op,
op1_info, op1_addr, op1_data_info, OP1_DATA_RANGE(),
op1_info, op1_addr, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(),
op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce,
val_type)) {
goto jit_failure;
Expand Down Expand Up @@ -4934,7 +4935,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
op1_data_info = OP1_DATA_INFO();
CHECK_OP1_DATA_TRACE_TYPE();
if (!zend_jit_assign_obj(&ctx, opline, op_array, ssa, ssa_op,
op1_info, op1_addr, op1_data_info,
op1_info, op1_addr, op1_data_info, OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(),
(opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0,
op1_indirect, ce, ce_is_instanceof, on_this, delayed_fetch_this, op1_ce,
val_type,
zend_may_throw(opline, ssa_op, op_array, ssa))) {
Expand Down
Loading