diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 10459da7a9c65..4596a14ba7224 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -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; } diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index d0a24aacc2e56..de59349e5f250 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -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); diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 019887b587a13..44296464feead 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -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); @@ -13662,13 +13666,13 @@ 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; @@ -13676,6 +13680,7 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit, 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); @@ -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); } @@ -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 @@ -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 @@ -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; } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index cba20edb6a560..df10a93bcb837 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -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)) { @@ -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; }