[php-src] master: Add stateless closure cache
Ilija Tovilo <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Ilija Tovilo (iluuu1994)
Date: 2026-08-11T00:30:29+02:00
Commit: https://github.com/php/php-src/commit/f47cd216e9537301471d6af770998fe5fe5c784c
Raw diff: https://github.com/php/php-src/commit/f47cd216e9537301471d6af770998fe5fe5c784c.diff
Add stateless closure cache
RFC: https://wiki.php.net/rfc/closure-optimizations#stateless_closure_caching
Extracted from GH-19941. Sadly, @DanielEScherzer discovered an edge-case I did
not consider:
class Foo {
public function instanceCall() {
return $this;
}
public function test($c) {
return array_map($c, [1]);
}
}
$foo = new Foo();
var_dump($foo->test('Foo::instanceCall'));
Here, the internal function array_map() can perform instance calls through a
previous stack frame with just a named closure. Same with ['Foo',
'instanceCall']. That's quite esoteric, but this behavior should first be
deprecated and removed before assuming it isn't being used.
For the time being, merge only the latter half of the RFC. Code with all
relevant functions properly annotated as static will get the full performance
benefit.
Closes GH-23203
Changed paths:
M NEWS
M UPGRADING
M Zend/Optimizer/compact_literals.c
M Zend/tests/partial_application/pipe_optimization_004.phpt
M Zend/tests/partial_application/pipe_optimization_007.phpt
M Zend/tests/partial_application/pipe_optimization_008.phpt
M Zend/tests/partial_application/pipe_optimization_013.phpt
M Zend/zend_compile.c
M Zend/zend_execute_API.c
M Zend/zend_globals.h
M Zend/zend_vm_def.h
M Zend/zend_vm_execute.h
M Zend/zend_vm_handlers.h
M Zend/zend_vm_opcodes.c
M ext/opcache/tests/array_map_foreach_optimization_008.phpt
M ext/opcache/tests/gh19867.phpt
Diff:
diff --git a/NEWS b/NEWS
index e5aa1098ffe9..f60af7c1b769 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,7 @@ PHP NEWS
handler). (David Carlier)
. Passing a 3rd argument to define() is now deprecated. (Girgias)
. Naming a function readonly is now deprecated. (Girgias)
+ . Added stateless closure cache. (ilutov)
- BZ2:
. Passing an object for the Bzip2 {de}compression stream filter is now
diff --git a/UPGRADING b/UPGRADING
index 0d32f455f32f..779c95ae9530 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -792,6 +792,8 @@ PHP 8.6 UPGRADE NOTES
. The TAILCALL VM is now enabled on Windows when compiling with Clang >= 19
x86_64.
. The performance of ZTS builds has been improved.
+ . Added stateless closure cache.
+ RFC: https://wiki.php.net/rfc/closure-optimizations#stateless_closure_caching
- DOM:
. Made splitText() faster and consume less memory.
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c
index e43da5892b9f..8277092d76b3 100644
--- a/Zend/Optimizer/compact_literals.c
+++ b/Zend/Optimizer/compact_literals.c
@@ -756,6 +756,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
}
break;
case ZEND_CALLABLE_CONVERT:
+ case ZEND_DECLARE_LAMBDA_FUNCTION:
if (opline->extended_value != (uint32_t)-1) {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt
index 194f08a9c143..2d70493f9d69 100644
--- a/Zend/tests/partial_application/pipe_optimization_004.phpt
+++ b/Zend/tests/partial_application/pipe_optimization_004.phpt
@@ -70,7 +70,7 @@ $_main:
; (lines=3, args=0, vars=0, tmps=%d)
; (after optimizer)
; %s:1-10
-0000 T0 = DECLARE_LAMBDA_FUNCTION 0
+0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0
0001 FREE T0
0002 RETURN int(1)
diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt
index be8d773c44ce..03c98e26423c 100644
--- a/Zend/tests/partial_application/pipe_optimization_007.phpt
+++ b/Zend/tests/partial_application/pipe_optimization_007.phpt
@@ -70,7 +70,7 @@ $_main:
; (lines=3, args=0, vars=0, tmps=%d)
; (after optimizer)
; %s:1-10
-0000 T0 = DECLARE_LAMBDA_FUNCTION 0
+0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0
0001 FREE T0
0002 RETURN int(1)
diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt
index 96f8d88815ca..baa0f3d8d4ca 100644
--- a/Zend/tests/partial_application/pipe_optimization_008.phpt
+++ b/Zend/tests/partial_application/pipe_optimization_008.phpt
@@ -66,7 +66,7 @@ $_main:
; (lines=4, args=0, vars=1, tmps=%d)
; (after optimizer)
; %s:1-10
-0000 T1 = DECLARE_LAMBDA_FUNCTION 0
+0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0
0001 BIND_LEXICAL T1 CV0($a)
0002 FREE T1
0003 RETURN int(1)
diff --git a/Zend/tests/partial_application/pipe_optimization_013.phpt b/Zend/tests/partial_application/pipe_optimization_013.phpt
index 6c2be6728e73..e4fb797657de 100644
--- a/Zend/tests/partial_application/pipe_optimization_013.phpt
+++ b/Zend/tests/partial_application/pipe_optimization_013.phpt
@@ -54,7 +54,7 @@ $_main:
; (lines=4, args=0, vars=1, tmps=%d)
; (after optimizer)
; %s:1-9
-0000 T1 = DECLARE_LAMBDA_FUNCTION 0
+0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0
0001 BIND_LEXICAL T1 CV0($b)
0002 FREE T1
0003 RETURN int(1)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index cac60319a0af..6fd4df023520 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -8941,6 +8941,7 @@ static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array,
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL);
opline->op2.num = func_ref;
+ opline->extended_value = (uint32_t)-1;
} else {
opline = get_next_op();
opline->opcode = ZEND_DECLARE_FUNCTION;
@@ -9130,6 +9131,23 @@ static zend_op_array *zend_compile_func_decl_ex(
zend_compile_stmt(stmt_ast);
+ if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) {
+ zend_op_array *declaring_op_array = orig_oparray_context.op_array;
+
+ if ((op_array->fn_flags & ZEND_ACC_STATIC)
+ && !op_array->static_variables
+ /* Don't cache closures in main, as those would leak without a proper
+ * cleanup mechanism. */
+ && declaring_op_array->function_name
+ && declaring_op_array->last) {
+ zend_op *declare_lambda_op = &declaring_op_array->opcodes[declaring_op_array->last - 1];
+ if (declare_lambda_op->opcode == ZEND_DECLARE_LAMBDA_FUNCTION) {
+ declare_lambda_op->extended_value = declaring_op_array->cache_size;
+ declaring_op_array->cache_size += sizeof(void *);
+ }
+ }
+ }
+
if (is_method) {
CG(zend_lineno) = decl->start_lineno;
zend_check_magic_method_implementation(
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 83ba5e7490cf..cc99a75a6ab9 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -203,6 +203,7 @@ void init_executor(void) /* {{{ */
zend_hash_init(&EG(callable_convert_cache), 8, NULL, ZVAL_PTR_DTOR, 0);
zend_hash_init(&EG(partial_function_application_cache), 8, NULL, zend_partial_op_array_dtor, 0);
+ zend_stack_init(&EG(lambda_cache), sizeof(zend_object *));
EG(active) = 1;
}
@@ -268,6 +269,14 @@ void shutdown_destructors(void) /* {{{ */
}
/* }}} */
+static void lambda_dtor(zend_object **closure_ptr)
+{
+ zend_object *closure = *closure_ptr;
+ if (GC_DELREF(closure) == 0) {
+ zend_objects_store_del(closure);
+ }
+}
+
/* Free values held by the executor. */
ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
{
@@ -421,6 +430,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
zend_hash_clean(&EG(callable_convert_cache));
zend_hash_clean(&EG(partial_function_application_cache));
+ zend_stack_clean(&EG(lambda_cache), (void (*)(void *)) lambda_dtor, 1);
#if ZEND_DEBUG
if (!CG(unclean_shutdown)) {
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index 83360a2c96d2..4d5e300e2859 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -326,6 +326,7 @@ struct _zend_executor_globals {
HashTable callable_convert_cache;
HashTable partial_function_application_cache;
+ zend_stack lambda_cache;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index d89d460d9d9d..cf8072646ee4 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -8409,13 +8409,21 @@ ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST)
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
-ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
+ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT)
{
USE_OPLINE
zend_function *func;
zval *object;
zend_class_entry *called_scope;
+ if (opline->extended_value != (uint32_t)-1) {
+ zend_object *closure = CACHED_PTR(opline->extended_value);
+ if (closure) {
+ ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure);
+ ZEND_VM_NEXT_OPCODE();
+ }
+ }
+
func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
if (Z_TYPE(EX(This)) == IS_OBJECT) {
called_scope = Z_OBJCE(EX(This));
@@ -8432,7 +8440,12 @@ ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM)
SAVE_OPLINE();
zend_create_closure(EX_VAR(opline->result.var), func,
EX(func)->op_array.scope, called_scope, object);
-
+ if (opline->extended_value != (uint32_t)-1) {
+ zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var));
+ GC_ADDREF(closure);
+ CACHE_PTR(opline->extended_value, closure);
+ zend_stack_push(&EG(lambda_cache), &closure);
+ }
ZEND_VM_NEXT_OPCODE();
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 4c830f203bfb..58cd0ddf916e 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -5916,33 +5916,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_CLASS
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
- zend_function *func;
- zval *object;
- zend_class_entry *called_scope;
-
- func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
- if (Z_TYPE(EX(This)) == IS_OBJECT) {
- called_scope = Z_OBJCE(EX(This));
- if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
- (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
- object = NULL;
- } else {
- object = &EX(This);
- }
- } else {
- called_scope = Z_CE(EX(This));
- object = NULL;
- }
- SAVE_OPLINE();
- zend_create_closure(EX_VAR(opline->result.var), func,
- EX(func)->op_array.scope, called_scope, object);
-
- ZEND_VM_NEXT_OPCODE();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -32928,6 +32901,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CLONE_SPEC_UN
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_function *func;
+ zval *object;
+ zend_class_entry *called_scope;
+
+ if (opline->extended_value != (uint32_t)-1) {
+ zend_object *closure = CACHED_PTR(opline->extended_value);
+ if (closure) {
+ ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure);
+ ZEND_VM_NEXT_OPCODE();
+ }
+ }
+
+ func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
+ (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
+ object = NULL;
+ } else {
+ object = &EX(This);
+ }
+ } else {
+ called_scope = Z_CE(EX(This));
+ object = NULL;
+ }
+ SAVE_OPLINE();
+ zend_create_closure(EX_VAR(opline->result.var), func,
+ EX(func)->op_array.scope, called_scope, object);
+ if (opline->extended_value != (uint32_t)-1) {
+ zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var));
+ GC_ADDREF(closure);
+ CACHE_PTR(opline->extended_value, closure);
+ zend_stack_push(&EG(lambda_cache), &closure);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
uint32_t fetch_type;
@@ -58748,33 +58761,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_CLASS_SPEC
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
-static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-{
- USE_OPLINE
- zend_function *func;
- zval *object;
- zend_class_entry *called_scope;
-
- func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
- if (Z_TYPE(EX(This)) == IS_OBJECT) {
- called_scope = Z_OBJCE(EX(This));
- if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
- (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
- object = NULL;
- } else {
- object = &EX(This);
- }
- } else {
- called_scope = Z_CE(EX(This));
- object = NULL;
- }
- SAVE_OPLINE();
- zend_create_closure(EX_VAR(opline->result.var), func,
- EX(func)->op_array.scope, called_scope, object);
-
- ZEND_VM_NEXT_OPCODE();
-}
-
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_FROM_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -85558,6 +85544,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CLONE_SPEC_UNUSED_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
+static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+ USE_OPLINE
+ zend_function *func;
+ zval *object;
+ zend_class_entry *called_scope;
+
+ if (opline->extended_value != (uint32_t)-1) {
+ zend_object *closure = CACHED_PTR(opline->extended_value);
+ if (closure) {
+ ZVAL_OBJ_COPY(EX_VAR(opline->result.var), closure);
+ ZEND_VM_NEXT_OPCODE();
+ }
+ }
+
+ func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num];
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ called_scope = Z_OBJCE(EX(This));
+ if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) ||
+ (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
+ object = NULL;
+ } else {
+ object = &EX(This);
+ }
+ } else {
+ called_scope = Z_CE(EX(This));
+ object = NULL;
+ }
+ SAVE_OPLINE();
+ zend_create_closure(EX_VAR(opline->result.var), func,
+ EX(func)->op_array.scope, called_scope, object);
+ if (opline->extended_value != (uint32_t)-1) {
+ zend_object *closure = Z_OBJ_P(EX_VAR(opline->result.var));
+ GC_ADDREF(closure);
+ CACHE_PTR(opline->extended_value, closure);
+ zend_stack_push(&EG(lambda_cache), &closure);
+ }
+ ZEND_VM_NEXT_OPCODE();
+}
+
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
uint32_t fetch_type;
@@ -109287,7 +109313,7 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_MAKE_REF_SPEC_CV_UNUSED_LABEL,
(void*)&&ZEND_DECLARE_FUNCTION_SPEC_LABEL,
- (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_LABEL,
+ (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_LABEL,
(void*)&&ZEND_DECLARE_CONST_SPEC_CONST_CONST_LABEL,
(void*)&&ZEND_DECLARE_CLASS_SPEC_CONST_LABEL,
(void*)&&ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_LABEL,
@@ -111298,11 +111324,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_DECLARE_CLASS_SPEC_CONST)
HYBRID_BREAK();
- HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST):
- VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST)
- ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
- VM_TRACE_OP_END(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST)
- HYBRID_BREAK();
HYBRID_CASE(ZEND_YIELD_FROM_SPEC_CONST):
VM_TRACE(ZEND_YIELD_FROM_SPEC_CONST)
ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -114122,6 +114143,11 @@ ZEND_API void execute_ex(zend_execute_data *ex)
ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_CLONE_SPEC_UNUSED)
HYBRID_BREAK();
+ HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED):
+ VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED)
+ ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+ VM_TRACE_OP_END(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED)
+ HYBRID_BREAK();
HYBRID_CASE(ZEND_FETCH_CLASS_NAME_SPEC_UNUSED):
VM_TRACE(ZEND_FETCH_CLASS_NAME_SPEC_UNUSED)
ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -118255,7 +118281,7 @@ void zend_vm_init(void)
ZEND_NULL_HANDLER,
ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER,
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
- ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER,
+ ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_HANDLER,
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER,
ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER,
ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_HANDLER,
@@ -121743,7 +121769,7 @@ void zend_vm_init(void)
ZEND_NULL_TAILCALL_HANDLER,
ZEND_MAKE_REF_SPEC_CV_UNUSED_TAILCALL_HANDLER,
ZEND_DECLARE_FUNCTION_SPEC_TAILCALL_HANDLER,
- ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_TAILCALL_HANDLER,
+ ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED_TAILCALL_HANDLER,
ZEND_DECLARE_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER,
ZEND_DECLARE_CLASS_SPEC_CONST_TAILCALL_HANDLER,
ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_TAILCALL_HANDLER,
diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h
index 503a6d25634b..4586fd861021 100644
--- a/Zend/zend_vm_handlers.h
+++ b/Zend/zend_vm_handlers.h
@@ -910,7 +910,7 @@
_(2282, ZEND_MAKE_REF_SPEC_VAR_UNUSED) \
_(2284, ZEND_MAKE_REF_SPEC_CV_UNUSED) \
_(2285, ZEND_DECLARE_FUNCTION_SPEC) \
- _(2286, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) \
+ _(2286, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_UNUSED) \
_(2287, ZEND_DECLARE_CONST_SPEC_CONST_CONST) \
_(2288, ZEND_DECLARE_CLASS_SPEC_CONST) \
_(2289, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST) \
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index f9b30edb5e9b..cc14862ebedd 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -381,7 +381,7 @@ static uint32_t zend_vm_opcodes_flags[214] = {
0x00000000,
0x00000101,
0x00001000,
- 0x00001003,
+ 0x01041001,
0x00000303,
0x00000003,
0x00000303,
diff --git a/ext/opcache/tests/array_map_foreach_optimization_008.phpt b/ext/opcache/tests/array_map_foreach_optimization_008.phpt
index 36c80b460e88..e90980922cab 100644
--- a/ext/opcache/tests/array_map_foreach_optimization_008.phpt
+++ b/ext/opcache/tests/array_map_foreach_optimization_008.phpt
@@ -56,7 +56,7 @@ $_main:
; (lines=4, args=0, vars=1, tmps=%d)
; (after optimizer)
; %s:1-9
-0000 T1 = DECLARE_LAMBDA_FUNCTION 0
+0000 T1 = DECLARE_LAMBDA_FUNCTION %d 0
0001 BIND_LEXICAL T1 CV0($n)
0002 FREE T1
0003 RETURN int(1)
diff --git a/ext/opcache/tests/gh19867.phpt b/ext/opcache/tests/gh19867.phpt
index 486a366722da..51b2aaeaf459 100644
--- a/ext/opcache/tests/gh19867.phpt
+++ b/ext/opcache/tests/gh19867.phpt
@@ -15,7 +15,7 @@ $_main:
; (lines=%d, args=0, vars=%d, tmps=%d)
; (after optimizer)
; %s
-0000 T0 = DECLARE_LAMBDA_FUNCTION 0
+0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0
0001 FREE T0
0002 RETURN int(1)
@@ -23,7 +23,7 @@ $_main:
; (lines=%d, args=0, vars=%d, tmps=%d)
; (after optimizer)
; %s
-0000 T0 = DECLARE_LAMBDA_FUNCTION 0
+0000 T0 = DECLARE_LAMBDA_FUNCTION %d 0
0001 RETURN T0
{closure:%s:%d}: