Skip to content

Implement single "last matched CE" cache slot for RECV and VERIFY_RETURN_TYPE #123

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,18 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
opline->op2.constant = map[opline->op2.constant];
}
switch (opline->opcode) {
case ZEND_RECV:
case ZEND_RECV_INIT:
case ZEND_RECV_VARIADIC:
if (1) { // TODO: only do this if we have a type AST!
opline->extended_value = cache_size;
cache_size += sizeof(void *);
}
break;
case ZEND_VERIFY_RETURN_TYPE:
opline->op2.num = cache_size;
cache_size += sizeof(void *);
break;
case ZEND_ASSIGN_STATIC_PROP_OP:
if (opline->op1_type == IS_CONST) {
// op1 static property
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2681,6 +2681,8 @@ static void zend_emit_return_type_check(
opline->result_type = expr->op_type = IS_TMP_VAR;
opline->result.var = expr->u.op.var = get_temporary_variable();
}

opline->op2.num = zend_alloc_cache_slot();
}
}
/* }}} */
Expand Down Expand Up @@ -7725,6 +7727,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
SET_NODE(opline->result, &var_node);
opline->op1.num = i + 1;

if (type_ast) {
/* Allocate cache slot for last successful type check */
opline->extended_value = zend_alloc_cache_slot();
}

uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0)
| (is_promoted ? _ZEND_IS_PROMOTED_BIT : 0);
ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags;
Expand Down
36 changes: 24 additions & 12 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -1141,10 +1141,13 @@ static bool zend_check_intersection_type_from_list(
}

static zend_always_inline bool zend_check_type_slow(
const zend_type *type, zval *arg, const zend_reference *ref,
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot,
bool is_return_type, bool is_internal)
{
if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
// if (*cache_slot == Z_OBJCE_P(arg)) {
// return true;
// }
zend_class_entry *ce;
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
Expand All @@ -1154,13 +1157,15 @@ static zend_always_inline bool zend_check_type_slow(
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
if (zend_check_intersection_type_from_list(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg))) {
*cache_slot = (void *) Z_OBJCE_P(arg);
return true;
}
} else {
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
ce = zend_fetch_ce_from_type(list_type);
/* Instance of a single type part of a union is sufficient to pass the type check */
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
*cache_slot = (void *) Z_OBJCE_P(arg);
return true;
}
}
Expand All @@ -1171,9 +1176,10 @@ static zend_always_inline bool zend_check_type_slow(
/* If we have a CE we check if it satisfies the type constraint,
* otherwise it will check if a standard type satisfies it. */
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
*cache_slot = (void *) Z_OBJCE_P(arg);
return true;
}
}
};
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: remove this accident

}

const uint32_t type_mask = ZEND_TYPE_FULL_MASK(*type);
Expand Down Expand Up @@ -1204,7 +1210,7 @@ static zend_always_inline bool zend_check_type_slow(
}

static zend_always_inline bool zend_check_type(
const zend_type *type, zval *arg, zend_class_entry *scope,
const zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope,
bool is_return_type, bool is_internal)
{
const zend_reference *ref = NULL;
Expand All @@ -1219,25 +1225,29 @@ static zend_always_inline bool zend_check_type(
return 1;
}

return zend_check_type_slow(type, arg, ref, is_return_type, is_internal);
return zend_check_type_slow(type, arg, ref, cache_slot, is_return_type, is_internal);
}

ZEND_API bool zend_check_user_type_slow(
const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type)
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type)
{
return zend_check_type_slow(
type, arg, ref, is_return_type, /* is_internal */ false);
type, arg, ref, cache_slot, is_return_type, /* is_internal */ false);
}

static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg)
static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
{
const zend_arg_info *cur_arg_info;

ZEND_ASSERT(arg_num <= zf->common.num_args);
cur_arg_info = &zf->common.arg_info[arg_num-1];

if (Z_TYPE_P(arg) == IS_OBJECT && *cache_slot == Z_OBJCE_P(arg)) {
return 1;
}

if (ZEND_TYPE_IS_SET(cur_arg_info->type)
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, zf->common.scope, 0, 0))) {
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
zend_verify_arg_error(zf, cur_arg_info, arg_num, arg);
return 0;
}
Expand All @@ -1246,10 +1256,10 @@ static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf
}

static zend_always_inline bool zend_verify_variadic_arg_type(
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg)
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot)
{
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, zf->common.scope, 0, 0))) {
if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
zend_verify_arg_error(zf, arg_info, arg_num, arg);
return 0;
}
Expand All @@ -1273,8 +1283,9 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
break;
}

void *cache_slot = NULL;
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, fbc->common.scope, 0, /* is_internal */ 1))) {
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, &cache_slot, fbc->common.scope, 0, /* is_internal */ 1))) {
return 0;
}
arg++;
Expand Down Expand Up @@ -1480,7 +1491,8 @@ ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *re
return 1;
}

if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, NULL, 1, /* is_internal */ 1))) {
void *cache_slot = NULL;
if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, &cache_slot, NULL, 1, /* is_internal */ 1))) {
zend_verify_internal_return_error(zf, ret);
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ ZEND_API ZEND_COLD void zend_verify_never_error(
const zend_function *zf);
ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref);
ZEND_API bool zend_check_user_type_slow(
const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type);
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type);

#if ZEND_DEBUG
ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call);
Expand Down
17 changes: 9 additions & 8 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -4423,7 +4423,7 @@ ZEND_VM_C_LABEL(fcall_end):
ZEND_VM_CONTINUE();
}

ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED|CACHE_SLOT)
{
if (OP1_TYPE == IS_UNUSED) {
SAVE_OPLINE();
Expand Down Expand Up @@ -4465,6 +4465,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}

zend_reference *ref = NULL;
void *cache_slot = CACHE_ADDR(opline->op2.num);
if (UNEXPECTED(retval_ref != retval_ptr)) {
if (UNEXPECTED(EX(func)->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
ref = Z_REF_P(retval_ref);
Expand All @@ -4481,7 +4482,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
}

SAVE_OPLINE();
if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
zend_verify_return_error(EX(func), retval_ptr);
HANDLE_EXCEPTION();
}
Expand Down Expand Up @@ -5670,7 +5671,7 @@ ZEND_VM_HELPER(zend_verify_recv_arg_type_helper, ANY, ANY, zval *op_1)
USE_OPLINE

SAVE_OPLINE();
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1))) {
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1, CACHE_ADDR(opline->extended_value)))) {
HANDLE_EXCEPTION();
}

Expand Down Expand Up @@ -5708,7 +5709,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_RECV, op->op2.num == MAY_BE_ANY, ZEND_RECV_NO
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST, CACHE_SLOT)
{
USE_OPLINE
uint32_t arg_num;
Expand Down Expand Up @@ -5748,7 +5749,7 @@ ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
ZEND_VM_C_LABEL(recv_init_check_type):
if ((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
SAVE_OPLINE();
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param))) {
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->extended_value)))) {
HANDLE_EXCEPTION();
}
}
Expand All @@ -5758,7 +5759,7 @@ ZEND_VM_C_LABEL(recv_init_check_type):
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED, CACHE_SLOT)
{
USE_OPLINE
uint32_t arg_num = opline->op1.num;
Expand All @@ -5781,7 +5782,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
if (ZEND_TYPE_IS_SET(arg_info->type)) {
ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS);
do {
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
ZEND_HASH_FILL_FINISH();
HANDLE_EXCEPTION();
}
Expand Down Expand Up @@ -5809,7 +5810,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
if (ZEND_TYPE_IS_SET(arg_info->type)) {
SEPARATE_ARRAY(params);
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, param) {
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
HANDLE_EXCEPTION();
}
Z_TRY_ADDREF_P(param);
Expand Down
23 changes: 14 additions & 9 deletions Zend/zend_vm_execute.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Zend/zend_vm_opcodes.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading