Skip to content

Commit 380a03b

Browse files
committed
Implement single "last matched CE" cache slot for RECV and VERIFY_RETURN_TYPE
1 parent a32f491 commit 380a03b

File tree

9 files changed

+73
-38
lines changed

9 files changed

+73
-38
lines changed

Zend/Optimizer/compact_literals.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,18 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
460460
opline->op2.constant = map[opline->op2.constant];
461461
}
462462
switch (opline->opcode) {
463+
case ZEND_RECV:
464+
case ZEND_RECV_INIT:
465+
case ZEND_RECV_VARIADIC:
466+
if (1) { // TODO: only do this if we have a type AST!
467+
opline->extended_value = cache_size;
468+
cache_size += sizeof(void *);
469+
}
470+
break;
471+
case ZEND_VERIFY_RETURN_TYPE:
472+
opline->op2.num = cache_size;
473+
cache_size += sizeof(void *);
474+
break;
463475
case ZEND_ASSIGN_STATIC_PROP_OP:
464476
if (opline->op1_type == IS_CONST) {
465477
// op1 static property

Zend/zend_compile.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,6 +2681,8 @@ static void zend_emit_return_type_check(
26812681
opline->result_type = expr->op_type = IS_TMP_VAR;
26822682
opline->result.var = expr->u.op.var = get_temporary_variable();
26832683
}
2684+
2685+
opline->op2.num = zend_alloc_cache_slot();
26842686
}
26852687
}
26862688
/* }}} */
@@ -7725,6 +7727,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
77257727
SET_NODE(opline->result, &var_node);
77267728
opline->op1.num = i + 1;
77277729

7730+
if (type_ast) {
7731+
/* Allocate cache slot for last successful type check */
7732+
opline->extended_value = zend_alloc_cache_slot();
7733+
}
7734+
77287735
uint32_t arg_info_flags = _ZEND_ARG_INFO_FLAGS(is_ref, is_variadic, /* is_tentative */ 0)
77297736
| (is_promoted ? _ZEND_IS_PROMOTED_BIT : 0);
77307737
ZEND_TYPE_FULL_MASK(arg_info->type) |= arg_info_flags;

Zend/zend_execute.c

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,10 +1141,13 @@ static bool zend_check_intersection_type_from_list(
11411141
}
11421142

11431143
static zend_always_inline bool zend_check_type_slow(
1144-
const zend_type *type, zval *arg, const zend_reference *ref,
1144+
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot,
11451145
bool is_return_type, bool is_internal)
11461146
{
11471147
if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) {
1148+
if (*cache_slot == Z_OBJCE_P(arg)) {
1149+
return true;
1150+
}
11481151
zend_class_entry *ce;
11491152
if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) {
11501153
if (ZEND_TYPE_IS_INTERSECTION(*type)) {
@@ -1154,13 +1157,15 @@ static zend_always_inline bool zend_check_type_slow(
11541157
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) {
11551158
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
11561159
if (zend_check_intersection_type_from_list(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg))) {
1160+
*cache_slot = (void *) Z_OBJCE_P(arg);
11571161
return true;
11581162
}
11591163
} else {
11601164
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
11611165
ce = zend_fetch_ce_from_type(list_type);
11621166
/* Instance of a single type part of a union is sufficient to pass the type check */
11631167
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
1168+
*cache_slot = (void *) Z_OBJCE_P(arg);
11641169
return true;
11651170
}
11661171
}
@@ -1171,9 +1176,10 @@ static zend_always_inline bool zend_check_type_slow(
11711176
/* If we have a CE we check if it satisfies the type constraint,
11721177
* otherwise it will check if a standard type satisfies it. */
11731178
if (ce && instanceof_function(Z_OBJCE_P(arg), ce)) {
1179+
*cache_slot = (void *) Z_OBJCE_P(arg);
11741180
return true;
11751181
}
1176-
}
1182+
};
11771183
}
11781184

11791185
const uint32_t type_mask = ZEND_TYPE_FULL_MASK(*type);
@@ -1204,7 +1210,7 @@ static zend_always_inline bool zend_check_type_slow(
12041210
}
12051211

12061212
static zend_always_inline bool zend_check_type(
1207-
const zend_type *type, zval *arg, zend_class_entry *scope,
1213+
const zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope,
12081214
bool is_return_type, bool is_internal)
12091215
{
12101216
const zend_reference *ref = NULL;
@@ -1219,25 +1225,25 @@ static zend_always_inline bool zend_check_type(
12191225
return 1;
12201226
}
12211227

1222-
return zend_check_type_slow(type, arg, ref, is_return_type, is_internal);
1228+
return zend_check_type_slow(type, arg, ref, cache_slot, is_return_type, is_internal);
12231229
}
12241230

12251231
ZEND_API bool zend_check_user_type_slow(
1226-
const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type)
1232+
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type)
12271233
{
12281234
return zend_check_type_slow(
1229-
type, arg, ref, is_return_type, /* is_internal */ false);
1235+
type, arg, ref, cache_slot, is_return_type, /* is_internal */ false);
12301236
}
12311237

1232-
static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg)
1238+
static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot)
12331239
{
12341240
const zend_arg_info *cur_arg_info;
12351241

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

12391245
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
1240-
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, zf->common.scope, 0, 0))) {
1246+
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
12411247
zend_verify_arg_error(zf, cur_arg_info, arg_num, arg);
12421248
return 0;
12431249
}
@@ -1246,10 +1252,10 @@ static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf
12461252
}
12471253

12481254
static zend_always_inline bool zend_verify_variadic_arg_type(
1249-
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg)
1255+
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot)
12501256
{
12511257
ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type));
1252-
if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, zf->common.scope, 0, 0))) {
1258+
if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) {
12531259
zend_verify_arg_error(zf, arg_info, arg_num, arg);
12541260
return 0;
12551261
}
@@ -1273,8 +1279,9 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ
12731279
break;
12741280
}
12751281

1282+
void *cache_slot = NULL;
12761283
if (ZEND_TYPE_IS_SET(cur_arg_info->type)
1277-
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, fbc->common.scope, 0, /* is_internal */ 1))) {
1284+
&& UNEXPECTED(!zend_check_type(&cur_arg_info->type, arg, &cache_slot, fbc->common.scope, 0, /* is_internal */ 1))) {
12781285
return 0;
12791286
}
12801287
arg++;
@@ -1480,7 +1487,8 @@ ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *re
14801487
return 1;
14811488
}
14821489

1483-
if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, NULL, 1, /* is_internal */ 1))) {
1490+
void *cache_slot = NULL;
1491+
if (UNEXPECTED(!zend_check_type(&ret_info->type, ret, &cache_slot, NULL, 1, /* is_internal */ 1))) {
14841492
zend_verify_internal_return_error(zf, ret);
14851493
return 0;
14861494
}

Zend/zend_execute.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ ZEND_API ZEND_COLD void zend_verify_never_error(
106106
const zend_function *zf);
107107
ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref);
108108
ZEND_API bool zend_check_user_type_slow(
109-
const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type);
109+
const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type);
110110

111111
#if ZEND_DEBUG
112112
ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call);

Zend/zend_vm_def.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,7 +4423,7 @@ ZEND_VM_C_LABEL(fcall_end):
44234423
ZEND_VM_CONTINUE();
44244424
}
44254425

4426-
ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
4426+
ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED|CACHE_SLOT)
44274427
{
44284428
if (OP1_TYPE == IS_UNUSED) {
44294429
SAVE_OPLINE();
@@ -4465,6 +4465,7 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
44654465
}
44664466

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

44834484
SAVE_OPLINE();
4484-
if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, 1, 0))) {
4485+
if (UNEXPECTED(!zend_check_type_slow(&ret_info->type, retval_ptr, ref, cache_slot, 1, 0))) {
44854486
zend_verify_return_error(EX(func), retval_ptr);
44864487
HANDLE_EXCEPTION();
44874488
}
@@ -5670,7 +5671,7 @@ ZEND_VM_HELPER(zend_verify_recv_arg_type_helper, ANY, ANY, zval *op_1)
56705671
USE_OPLINE
56715672

56725673
SAVE_OPLINE();
5673-
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1))) {
5674+
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), opline->op1.num, op_1, CACHE_ADDR(opline->extended_value)))) {
56745675
HANDLE_EXCEPTION();
56755676
}
56765677

@@ -5708,7 +5709,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_RECV, op->op2.num == MAY_BE_ANY, ZEND_RECV_NO
57085709
ZEND_VM_NEXT_OPCODE();
57095710
}
57105711

5711-
ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
5712+
ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST, CACHE_SLOT)
57125713
{
57135714
USE_OPLINE
57145715
uint32_t arg_num;
@@ -5748,7 +5749,7 @@ ZEND_VM_HOT_HANDLER(64, ZEND_RECV_INIT, NUM, CONST)
57485749
ZEND_VM_C_LABEL(recv_init_check_type):
57495750
if ((EX(func)->op_array.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0) {
57505751
SAVE_OPLINE();
5751-
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param))) {
5752+
if (UNEXPECTED(!zend_verify_recv_arg_type(EX(func), arg_num, param, CACHE_ADDR(opline->extended_value)))) {
57525753
HANDLE_EXCEPTION();
57535754
}
57545755
}
@@ -5758,7 +5759,7 @@ ZEND_VM_C_LABEL(recv_init_check_type):
57585759
ZEND_VM_NEXT_OPCODE();
57595760
}
57605761

5761-
ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
5762+
ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED, CACHE_SLOT)
57625763
{
57635764
USE_OPLINE
57645765
uint32_t arg_num = opline->op1.num;
@@ -5781,7 +5782,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
57815782
if (ZEND_TYPE_IS_SET(arg_info->type)) {
57825783
ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS);
57835784
do {
5784-
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
5785+
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
57855786
ZEND_HASH_FILL_FINISH();
57865787
HANDLE_EXCEPTION();
57875788
}
@@ -5809,7 +5810,7 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED)
58095810
if (ZEND_TYPE_IS_SET(arg_info->type)) {
58105811
SEPARATE_ARRAY(params);
58115812
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, param) {
5812-
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param))) {
5813+
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
58135814
HANDLE_EXCEPTION();
58145815
}
58155816
Z_TRY_ADDREF_P(param);

Zend/zend_vm_execute.h

Lines changed: 14 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_vm_opcodes.c

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)