From 30f0cf6e6b3a0995c80e51b7a5bb64fae889e6b7 Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:08:35 +0200 Subject: [PATCH] JIT: Avoid object type check if the object is known to be a type Before: ```asm mov %r15,%r14 lea 0x50(%r14),%rdi ; &CV0($o) cmpb $0x8,0x58(%r14) ; Check IS_OBJECT jne not_an_object ; ... mov (%rdi),%rax ; Z_OBJ_P($o) lea -0x77280f9(%rip),%rcx ; ce to compare against cmp 0x10(%rax),%rcx ; Z_OBJ_P($o)->ce == ce je wrong_ce ... ``` After: ```asm mov %r15,%r14 lea 0x50(%r14),%rdi ; &CV0($o) mov (%rdi),%rax ; Z_OBJ_P($o) (known to be an object) lea -0x77280f9(%rip),%rcx ; ce to compare against cmp 0x10(%rax),%rcx ; Z_OBJ_P($o)->ce == ce je wrong_ce ... ``` This also helps with an `?Foo` type check: it can skip the IS_OBJECT and IS_NULL check. This also simplifies `zend_jit_verify_arg_type`: the `(type == IS_UNKNOWN || type == IS_OBJECT)` check becomes always-true. --- ext/opcache/jit/zend_jit_ir.c | 4 ++-- ext/opcache/jit/zend_jit_trace.c | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 9ea95627907b..9174faa6b46d 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10831,7 +10831,6 @@ static int zend_jit_verify_arg_type(zend_jit_ctx *jit, const zend_op *opline, ze } if (!ZEND_ARG_SEND_MODE(arg_info) - && (type == IS_UNKNOWN || type == IS_OBJECT) && zend_jit_class_satisfies_type(known_ce, arg_info->type)) { zend_jit_known_class_type_fast_path(jit, ref, type == IS_OBJECT, known_ce, &end_inputs); } @@ -10845,7 +10844,8 @@ static int zend_jit_verify_arg_type(zend_jit_ctx *jit, const zend_op *opline, ze } } - if (type_mask != 0) { + /* A known type is not in type_mask here (see above), so the mask check would always fail */ + if (type_mask != 0 && type == IS_UNKNOWN) { if (is_power_of_two(type_mask)) { uint32_t type_code = concrete_type(type_mask); ir_ref if_ok = jit_if_Z_TYPE_ref(jit, ref, ir_CONST_U8(type_code)); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 68461973f3f2..833939024381 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -481,10 +481,12 @@ static void zend_jit_trace_send_type(const zend_op *opline, zend_jit_trace_stack ZEND_ASSERT(arg_num <= op_array->num_args); arg_info = &op_array->arg_info[arg_num-1]; - if (ZEND_TYPE_IS_SET(arg_info->type)) { - if (!(ZEND_TYPE_FULL_MASK(arg_info->type) & (1u << type))) { - return; - } + /* Record scalars and object class types, even if there's no "object" type declaration. + * This allows RECV to record and skip object type checks. */ + if (ZEND_TYPE_IS_SET(arg_info->type) + && !(ZEND_TYPE_FULL_MASK(arg_info->type) & (1u << type)) + && !(type == IS_OBJECT && call->func->type == ZEND_USER_FUNCTION)) { + return; } } SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), type, 1);