1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Implement JIT for ZEND_FETCH_STATIC_PROP_* and improve interpretation (#16157)

* Implement JIT for ZEND_FETCH_STATIC_PROP_* and improve interpretation

* Revert incorrect change

* Use FASTCALL calling convention

* Use EMPTY_SWITCH_DEFAULT_CASE

* Move the loading of the property info into zend_jit_uninit_static_prop()
This commit is contained in:
Dmitry Stogov
2024-10-02 21:02:33 +03:00
committed by GitHub
parent 83bbf4b339
commit 3f913c123a
8 changed files with 354 additions and 55 deletions

View File

@@ -3532,7 +3532,8 @@ static zend_never_inline void zend_assign_to_property_reference_var_var(zval *co
OPLINE_CC EXECUTE_DATA_CC);
}
static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval **retval, zend_property_info **prop_info, uint32_t cache_slot, int fetch_type OPLINE_DC EXECUTE_DATA_DC) {
static zend_never_inline zval* zend_fetch_static_property_address_ex(zend_property_info **prop_info, uint32_t cache_slot, int fetch_type OPLINE_DC EXECUTE_DATA_DC) {
zval *result;
zend_string *name;
zend_class_entry *ce;
zend_property_info *property_info;
@@ -3548,7 +3549,7 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval
ce = zend_fetch_class_by_name(Z_STR_P(class_name), Z_STR_P(class_name + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
FREE_OP(op1_type, opline->op1.var);
return FAILURE;
return NULL;
}
if (UNEXPECTED(op1_type != IS_CONST)) {
CACHE_PTR(cache_slot, ce);
@@ -3559,21 +3560,21 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval
ce = zend_fetch_class(NULL, opline->op2.num);
if (UNEXPECTED(ce == NULL)) {
FREE_OP(op1_type, opline->op1.var);
return FAILURE;
return NULL;
}
} else {
ce = Z_CE_P(EX_VAR(opline->op2.var));
}
if (EXPECTED(op1_type == IS_CONST) && EXPECTED(CACHED_PTR(cache_slot) == ce)) {
*retval = CACHED_PTR(cache_slot + sizeof(void *));
result = CACHED_PTR(cache_slot + sizeof(void *));
*prop_info = CACHED_PTR(cache_slot + sizeof(void *) * 2);
return SUCCESS;
return result;
}
}
if (EXPECTED(op1_type == IS_CONST)) {
name = Z_STR_P(RT_CONSTANT(opline, opline->op1));
*retval = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
result = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
} else {
zend_string *tmp_name;
zval *varname = get_zval_ptr_undef(opline->op1_type, opline->op1, BP_VAR_R);
@@ -3586,62 +3587,109 @@ static zend_never_inline zend_result zend_fetch_static_property_address_ex(zval
}
name = zval_get_tmp_string(varname, &tmp_name);
}
*retval = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
result = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info);
zend_tmp_string_release(tmp_name);
FREE_OP(op1_type, opline->op1.var);
}
if (UNEXPECTED(*retval == NULL)) {
return FAILURE;
if (UNEXPECTED(result == NULL)) {
return NULL;
}
*prop_info = property_info;
if (EXPECTED(op1_type == IS_CONST)
&& EXPECTED(!(property_info->ce->ce_flags & ZEND_ACC_TRAIT))) {
CACHE_POLYMORPHIC_PTR(cache_slot, ce, *retval);
CACHE_POLYMORPHIC_PTR(cache_slot, ce, result);
CACHE_PTR(cache_slot + sizeof(void *) * 2, property_info);
}
return SUCCESS;
return result;
}
static zend_always_inline zend_result zend_fetch_static_property_address(zval **retval, zend_property_info **prop_info, uint32_t cache_slot, int fetch_type, int flags OPLINE_DC EXECUTE_DATA_DC) {
static zend_always_inline zval* zend_fetch_static_property_address(zend_property_info **prop_info, uint32_t cache_slot, int fetch_type, int flags OPLINE_DC EXECUTE_DATA_DC) {
zval *result;
zend_property_info *property_info;
if (opline->op1_type == IS_CONST && (opline->op2_type == IS_CONST || (opline->op2_type == IS_UNUSED && (opline->op2.num == ZEND_FETCH_CLASS_SELF || opline->op2.num == ZEND_FETCH_CLASS_PARENT))) && EXPECTED(CACHED_PTR(cache_slot) != NULL)) {
*retval = CACHED_PTR(cache_slot + sizeof(void *));
if (opline->op1_type == IS_CONST
&& (opline->op2_type == IS_CONST
|| (opline->op2_type == IS_UNUSED
&& (opline->op2.num == ZEND_FETCH_CLASS_SELF
|| opline->op2.num == ZEND_FETCH_CLASS_PARENT)))
&& EXPECTED(CACHED_PTR(cache_slot + sizeof(void *)) != NULL)) {
result = CACHED_PTR(cache_slot + sizeof(void *));
property_info = CACHED_PTR(cache_slot + sizeof(void *) * 2);
if ((fetch_type == BP_VAR_R || fetch_type == BP_VAR_RW)
&& UNEXPECTED(Z_TYPE_P(*retval) == IS_UNDEF)
&& UNEXPECTED(Z_TYPE_P(result) == IS_UNDEF)
&& ZEND_TYPE_IS_SET(property_info->type)) {
zend_throw_error(NULL, "Typed static property %s::$%s must not be accessed before initialization",
ZSTR_VAL(property_info->ce->name),
zend_get_unmangled_property_name(property_info->name));
return FAILURE;
return NULL;
}
} else {
zend_result success;
success = zend_fetch_static_property_address_ex(retval, &property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(success != SUCCESS)) {
return FAILURE;
result = zend_fetch_static_property_address_ex(&property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!result)) {
return NULL;
}
}
flags &= ZEND_FETCH_OBJ_FLAGS;
if (flags && ZEND_TYPE_IS_SET(property_info->type)) {
zend_handle_fetch_obj_flags(NULL, *retval, NULL, property_info, flags);
zend_handle_fetch_obj_flags(NULL, result, NULL, property_info, flags);
}
if (prop_info) {
*prop_info = property_info;
}
return SUCCESS;
return result;
}
ZEND_API zval* ZEND_FASTCALL zend_fetch_static_property(zend_execute_data *ex, int fetch_type) {
zval *result;
zend_property_info *property_info;
#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
zend_execute_data *orig_execute_data = execute_data;
#else
zend_execute_data *execute_data;
#endif
execute_data = ex;
#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
const zend_op *orig_opline = opline;
#else
const zend_op *opline;
#endif
opline = execute_data->opline;
uint32_t cache_slot = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
uint32_t flags = 0;
if (fetch_type == BP_VAR_W) {
flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
}
result = zend_fetch_static_property_address_ex(&property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC);
if (EXPECTED(result)) {
if (flags && ZEND_TYPE_IS_SET(property_info->type)) {
zend_handle_fetch_obj_flags(NULL, result, NULL, property_info, flags);
}
} else {
result = &EG(uninitialized_zval);
}
#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
EX(opline) = opline;
opline = orig_opline;
#endif
#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
execute_data = orig_execute_data;
#endif
return result;
}
ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(const zend_property_info *prop1, const zend_property_info *prop2, const zval *zv) {

View File

@@ -434,6 +434,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer);
ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield);
ZEND_API zval* ZEND_FASTCALL zend_fetch_static_property(zend_execute_data *ex, int fetch_type);
ZEND_API void zend_frameless_observed_call(zend_execute_data *execute_data);

View File

@@ -1109,7 +1109,8 @@ ZEND_VM_HANDLER(29, ZEND_ASSIGN_STATIC_PROP_OP, ANY, ANY, OP)
SAVE_OPLINE();
if (UNEXPECTED(zend_fetch_static_property_address(&prop, &prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
prop = zend_fetch_static_property_address(&prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
FREE_OP_DATA();
HANDLE_EXCEPTION();
@@ -1423,7 +1424,8 @@ ZEND_VM_HANDLER(38, ZEND_PRE_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT)
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -1449,7 +1451,8 @@ ZEND_VM_HANDLER(40, ZEND_POST_INC_STATIC_PROP, ANY, ANY, CACHE_SLOT)
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -1829,14 +1832,17 @@ ZEND_VM_HANDLER(89, ZEND_FETCH_IS, CONST|TMPVAR|CV, UNUSED, VAR_FETCH)
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
ZEND_VM_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type)
ZEND_VM_INLINE_HELPER(zend_fetch_static_prop_helper, ANY, ANY, int type)
{
USE_OPLINE
zval *prop;
SAVE_OPLINE();
if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
prop = zend_fetch_static_property_address(
NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
type == BP_VAR_W ? opline->extended_value : 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS));
prop = &EG(uninitialized_zval);
}
@@ -1870,10 +1876,11 @@ ZEND_VM_HANDLER(175, ZEND_FETCH_STATIC_PROP_RW, ANY, CLASS_FETCH, CACHE_SLOT)
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
ZEND_VM_HANDLER(177, ZEND_FETCH_STATIC_PROP_FUNC_ARG, ANY, CLASS_FETCH, FETCH_REF|CACHE_SLOT)
{
int fetch_type =
(UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
BP_VAR_W : BP_VAR_R;
ZEND_VM_DISPATCH_TO_HELPER(zend_fetch_static_prop_helper, type, fetch_type);
if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) {
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_FETCH_STATIC_PROP_W);
} else {
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_FETCH_STATIC_PROP_R);
}
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
@@ -2584,7 +2591,8 @@ ZEND_VM_HANDLER(25, ZEND_ASSIGN_STATIC_PROP, ANY, ANY, CACHE_SLOT, SPEC(OP_DATA=
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
FREE_OP_DATA();
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -2878,7 +2886,8 @@ ZEND_VM_HANDLER(33, ZEND_ASSIGN_STATIC_PROP_REF, ANY, ANY, CACHE_SLOT|SRC)
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
FREE_OP_DATA();
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -7430,18 +7439,17 @@ ZEND_VM_HANDLER(180, ZEND_ISSET_ISEMPTY_STATIC_PROP, ANY, CLASS_FETCH, ISSET|CAC
{
USE_OPLINE
zval *value;
zend_result fetch_result;
bool result;
SAVE_OPLINE();
fetch_result = zend_fetch_static_property_address(&value, NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
value = zend_fetch_static_property_address(NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
if (!(opline->extended_value & ZEND_ISEMPTY)) {
result = fetch_result == SUCCESS && Z_TYPE_P(value) > IS_NULL &&
result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
} else {
result = fetch_result != SUCCESS || !i_zend_is_true(value);
result = value == NULL || !i_zend_is_true(value);
}
ZEND_VM_SMART_BRANCH(result, 1);

47
Zend/zend_vm_execute.h generated
View File

@@ -776,7 +776,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_OP_SPEC_HAN
SAVE_OPLINE();
if (UNEXPECTED(zend_fetch_static_property_address(&prop, &prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
prop = zend_fetch_static_property_address(&prop_info, (opline+1)->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
HANDLE_EXCEPTION();
@@ -819,7 +820,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PRE_INC_STATIC_PROP_SPEC_HANDL
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -839,7 +841,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
}
@@ -851,14 +854,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_POST_INC_STATIC_PROP_SPEC_HAND
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CONST|VAR) */
static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_static_prop_helper_SPEC(int type ZEND_OPCODE_HANDLER_ARGS_DC)
static zend_always_inline ZEND_OPCODE_HANDLER_RET zend_fetch_static_prop_helper_SPEC(int type ZEND_OPCODE_HANDLER_ARGS_DC)
{
USE_OPLINE
zval *prop;
SAVE_OPLINE();
if (UNEXPECTED(zend_fetch_static_property_address(&prop, NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type, opline->extended_value OPLINE_CC EXECUTE_DATA_CC) != SUCCESS)) {
prop = zend_fetch_static_property_address(
NULL, opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS, type,
type == BP_VAR_W ? opline->extended_value : 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
ZEND_ASSERT(EG(exception) || (type == BP_VAR_IS));
prop = &EG(uninitialized_zval);
}
@@ -892,10 +898,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_RW_SPEC_HAND
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_STATIC_PROP_FUNC_ARG_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
int fetch_type =
(UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) ?
BP_VAR_W : BP_VAR_R;
ZEND_VM_TAIL_CALL(zend_fetch_static_prop_helper_SPEC(fetch_type ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) {
ZEND_VM_TAIL_CALL(ZEND_FETCH_STATIC_PROP_W_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
} else {
ZEND_VM_TAIL_CALL(ZEND_FETCH_STATIC_PROP_R_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
}
/* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */
@@ -943,7 +950,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -979,7 +987,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1015,7 +1024,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var));
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1051,7 +1061,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_SPEC_OP_DAT
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -1087,7 +1098,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_STATIC_PROP_REF_SPEC_HA
SAVE_OPLINE();
if (zend_fetch_static_property_address(&prop, &prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC) != SUCCESS) {
prop = zend_fetch_static_property_address(&prop_info, opline->extended_value & ~ZEND_RETURNS_FUNCTION, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC);
if (UNEXPECTED(!prop)) {
FREE_OP((opline+1)->op1_type, (opline+1)->op1.var);
UNDEF_RESULT();
HANDLE_EXCEPTION();
@@ -3008,18 +3020,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_STATIC_PROP_SPEC
{
USE_OPLINE
zval *value;
zend_result fetch_result;
bool result;
SAVE_OPLINE();
fetch_result = zend_fetch_static_property_address(&value, NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
value = zend_fetch_static_property_address(NULL, opline->extended_value & ~ZEND_ISEMPTY, BP_VAR_IS, 0 OPLINE_CC EXECUTE_DATA_CC);
if (!(opline->extended_value & ZEND_ISEMPTY)) {
result = fetch_result == SUCCESS && Z_TYPE_P(value) > IS_NULL &&
result = value != NULL && Z_TYPE_P(value) > IS_NULL &&
(!Z_ISREF_P(value) || Z_TYPE_P(Z_REFVAL_P(value)) != IS_NULL);
} else {
result = fetch_result != SUCCESS || !i_zend_is_true(value);
result = value == NULL || !i_zend_is_true(value);
}
ZEND_VM_SMART_BRANCH(result, 1);

View File

@@ -2349,6 +2349,22 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
goto jit_failure;
}
goto done;
case ZEND_FETCH_STATIC_PROP_R:
case ZEND_FETCH_STATIC_PROP_IS:
case ZEND_FETCH_STATIC_PROP_W:
case ZEND_FETCH_STATIC_PROP_RW:
case ZEND_FETCH_STATIC_PROP_UNSET:
if (!(opline->op1_type == IS_CONST
&& (opline->op2_type == IS_CONST
|| (opline->op2_type == IS_UNUSED
&& (opline->op2.num == ZEND_FETCH_CLASS_SELF
|| opline->op2.num == ZEND_FETCH_CLASS_PARENT))))) {
break;
}
if (!zend_jit_fetch_static_prop(&ctx, opline, op_array)) {
goto jit_failure;
}
goto done;
case ZEND_BIND_GLOBAL:
if (!ssa->ops || !ssa->var_info) {
op1_info = MAY_BE_ANY|MAY_BE_REF;

View File

@@ -3212,6 +3212,18 @@ static void ZEND_FASTCALL zend_jit_post_dec_obj_helper(zend_object *zobj, zend_s
}
}
static void ZEND_FASTCALL zend_jit_uninit_static_prop(void)
{
zend_execute_data *execute_data = EG(current_execute_data);
const zend_op *opline = EX(opline);
uint32_t cache_slot = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
const zend_property_info *property_info = CACHED_PTR(cache_slot + sizeof(void *) * 2);
zend_throw_error(NULL, "Typed static property %s::$%s must not be accessed before initialization",
ZSTR_VAL(property_info->ce->name),
zend_get_unmangled_property_name(property_info->name));
}
static void ZEND_FASTCALL zend_jit_free_trampoline_helper(zend_function *func)
{
ZEND_ASSERT(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE);

View File

@@ -3093,6 +3093,7 @@ static void zend_jit_setup_disasm(void)
REGISTER_HELPER(zend_jit_post_inc_obj_helper);
REGISTER_HELPER(zend_jit_pre_dec_obj_helper);
REGISTER_HELPER(zend_jit_post_dec_obj_helper);
REGISTER_HELPER(zend_jit_uninit_static_prop);
REGISTER_HELPER(zend_jit_rope_end);
REGISTER_HELPER(zend_fcall_interrupt);
@@ -15609,6 +15610,185 @@ static int zend_jit_incdec_obj(zend_jit_ctx *jit,
return 1;
}
static int zend_jit_fetch_static_prop(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array)
{
zend_jit_addr res_addr = RES_ADDR();
uint32_t cache_slot = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
uint32_t flags;
ir_ref ref, ref2, if_cached, fast_path, cold_path, prop_info_ref, if_typed, if_def;
int fetch_type;
zend_property_info *known_prop_info = NULL;
zend_class_entry *ce = NULL;
if (opline->op2_type == IS_CONST) {
zval *zv = RT_CONSTANT(opline, opline->op2);
zend_string *class_name;
ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
class_name = Z_STR_P(zv);
ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) {
ce = NULL;
}
} else {
ZEND_ASSERT(opline->op2_type == IS_UNUSED);
if (opline->op2.num == ZEND_FETCH_CLASS_SELF) {
ce = op_array->scope;
} else {
ZEND_ASSERT(opline->op2.num == ZEND_FETCH_CLASS_PARENT);
ce = op_array->scope;
if (ce) {
if (ce->parent) {
ce = ce->parent;
if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) {
ce = NULL;
}
} else {
ce = NULL;
}
}
}
}
if (ce) {
zval *zv = RT_CONSTANT(opline, opline->op1);
zend_string *prop_name;
ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING);
prop_name = Z_STR_P(zv);
zv = zend_hash_find(&ce->properties_info, prop_name);
if (zv) {
zend_property_info *prop_info = Z_PTR_P(zv);
if (prop_info->flags & ZEND_ACC_STATIC) {
if (prop_info->ce == op_array->scope
|| (prop_info->flags & ZEND_ACC_PUBLIC)
|| ((prop_info->flags & ZEND_ACC_PROTECTED)
&& instanceof_function_slow(op_array->scope, prop_info->ce))) {
known_prop_info = prop_info;
}
}
}
}
switch (opline->opcode) {
case ZEND_FETCH_STATIC_PROP_R:
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
fetch_type = BP_VAR_R;
break;
case ZEND_FETCH_STATIC_PROP_IS:
fetch_type = BP_VAR_IS;
break;
case ZEND_FETCH_STATIC_PROP_W:
fetch_type = BP_VAR_W;
break;
case ZEND_FETCH_STATIC_PROP_RW:
fetch_type = BP_VAR_RW;
break;
case ZEND_FETCH_STATIC_PROP_UNSET:
fetch_type = BP_VAR_UNSET;
break;
EMPTY_SWITCH_DEFAULT_CASE();
}
// JIT: result = CACHED_PTR(cache_slot + sizeof(void *));
ref = ir_LOAD_A(
ir_ADD_OFFSET(ir_LOAD_A(jit_EX(run_time_cache)), cache_slot + sizeof(void*)));
// JIT: if (result)
if_cached = ir_IF(ref);
ir_IF_TRUE(if_cached);
if (fetch_type == BP_VAR_R || fetch_type == BP_VAR_RW) {
if (!known_prop_info || ZEND_TYPE_IS_SET(known_prop_info->type)) {
ir_ref merge = IR_UNUSED;
// JIT: if (UNEXPECTED(Z_TYPE_P(result) == IS_UNDEF)
if_typed = IR_UNUSED;
if_def = ir_IF(jit_Z_TYPE_ref(jit, ref));
ir_IF_FALSE_cold(if_def);
if (!known_prop_info) {
// JIT: if (ZEND_TYPE_IS_SET(property_info->type))
prop_info_ref = ir_LOAD_L(
ir_ADD_OFFSET(ir_LOAD_A(jit_EX(run_time_cache)), cache_slot + sizeof(void*) * 2));
if_typed = ir_IF(ir_AND_U32(
ir_LOAD_U32(ir_ADD_OFFSET(prop_info_ref, offsetof(zend_property_info, type.type_mask))),
ir_CONST_U32(_ZEND_TYPE_MASK)));
ir_IF_FALSE(if_typed);
ir_END_list(merge);
ir_IF_TRUE(if_typed);
}
// JIT: zend_throw_error(NULL, "Typed static property %s::$%s must not be accessed before initialization",
// ZSTR_VAL(property_info->ce->name),
// zend_get_unmangled_property_name(property_info->name));
jit_SET_EX_OPLINE(jit, opline);
ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_jit_uninit_static_prop));
ir_IJMP(jit_STUB_ADDR(jit, jit_stub_exception_handler_undef));
ir_IF_TRUE(if_def);
if (!known_prop_info) {
ir_END_list(merge);
ir_MERGE_list(merge);
}
}
} else if (fetch_type == BP_VAR_W) {
flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
if (flags && (!known_prop_info || ZEND_TYPE_IS_SET(known_prop_info->type))) {
ir_ref merge = IR_UNUSED;
if (!known_prop_info) {
// JIT: if (ZEND_TYPE_IS_SET(property_info->type))
prop_info_ref = ir_LOAD_L(
ir_ADD_OFFSET(ir_LOAD_A(jit_EX(run_time_cache)), cache_slot + sizeof(void*) * 2));
if_typed = ir_IF(ir_AND_U32(
ir_LOAD_U32(ir_ADD_OFFSET(prop_info_ref, offsetof(zend_property_info, type.type_mask))),
ir_CONST_U32(_ZEND_TYPE_MASK)));
ir_IF_FALSE(if_typed);
ir_END_list(merge);
ir_IF_TRUE(if_typed);
} else {
prop_info_ref = ir_CONST_ADDR(known_prop_info);
}
// JIT: zend_handle_fetch_obj_flags(NULL, *retval, NULL, property_info, flags);
ir_ref if_ok = ir_IF(ir_CALL_5(IR_BOOL, ir_CONST_FUNC(zend_handle_fetch_obj_flags),
IR_NULL, ref, IR_NULL, prop_info_ref, ir_CONST_U32(flags)));
ir_IF_FALSE_cold(if_ok);
ir_IJMP(jit_STUB_ADDR(jit, jit_stub_exception_handler_undef));
ir_IF_TRUE(if_ok);
if (!known_prop_info) {
ir_END_list(merge);
ir_MERGE_list(merge);
}
}
}
fast_path = ir_END();
ir_IF_FALSE_cold(if_cached);
jit_SET_EX_OPLINE(jit, opline);
ref2 = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_fetch_static_property), jit_FP(jit), ir_CONST_I32(fetch_type));
zend_jit_check_exception_undef_result(jit, opline);
cold_path = ir_END();
ir_MERGE_2(fast_path, cold_path);
ref = ir_PHI_2(IR_ADDR, ref, ref2);
if (fetch_type == BP_VAR_R || fetch_type == BP_VAR_IS) {
// JIT: ZVAL_COPY_DEREF(EX_VAR(opline->result.var), result);
if (!zend_jit_zval_copy_deref(jit, res_addr, ZEND_ADDR_REF_ZVAL(ref),
jit_Z_TYPE_INFO_ref(jit, ref))) {
return 0;
}
} else {
// JIT: ZVAL_INDIRECT(EX_VAR(opline->result.var), result);
jit_set_Z_PTR(jit, res_addr, ref);
jit_set_Z_TYPE_INFO(jit, res_addr, IS_INDIRECT);
}
return 1;
}
static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, zend_jit_trace_rec *trace, zend_jit_trace_info *trace_info)
{
HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));

View File

@@ -6071,6 +6071,29 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto jit_failure;
}
goto done;
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
if (!JIT_G(current_frame)
|| !JIT_G(current_frame)->call
|| !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
break;
}
ZEND_FALLTHROUGH;
case ZEND_FETCH_STATIC_PROP_R:
case ZEND_FETCH_STATIC_PROP_IS:
case ZEND_FETCH_STATIC_PROP_W:
case ZEND_FETCH_STATIC_PROP_RW:
case ZEND_FETCH_STATIC_PROP_UNSET:
if (!(opline->op1_type == IS_CONST
&& (opline->op2_type == IS_CONST
|| (opline->op2_type == IS_UNUSED
&& (opline->op2.num == ZEND_FETCH_CLASS_SELF
|| opline->op2.num == ZEND_FETCH_CLASS_PARENT))))) {
break;
}
if (!zend_jit_fetch_static_prop(&ctx, opline, op_array)) {
goto jit_failure;
}
goto done;
case ZEND_BIND_GLOBAL:
orig_opline = opline;
orig_ssa_op = ssa_op;