Commit 57a90a3c030 for php

commit 57a90a3c030def70cafd214fee4e396f7b1adf1a
Author: Nora Dossche <7771979+ndossche@users.noreply.github.com>
Date:   Thu Oct 15 19:12:49 2026 +0200

    JIT: implement empty($cv) fast path in function & tracing JIT (#24239)

    We can simply defer to zend_jit_bool_jmpznz() which already generates
    optimized paths for many types.

    Example PHP:
    ```php
    function f(int $i) {
        if (empty($i)) return 1;
        return 2;
    }
    ```

    Before (function JIT):
    ```asm
    .L1:
            movl $0x414d88a0, %r15d          ; opline = ISSET_ISEMPTY_CV
            movabsq $0x55a80140fe00, %rax
            callq *%rax                      ; Call VM handler
            cmpl $0x414d88e0, %r15d          ; check if the handler branched
            jne .L2
            movq 0x10(%r14), %rax            ; return 1
            ...
    .L2:                                     ; return 2
            ...
    ```

    After (function JIT):
    ```asm
    .L1:
            cmpq $0, 0x50(%r14)              ; empty($i) check
            je .L3
            movq 0x10(%r14), %rax            ; return 2
            ...
    .L3:                                     ; return 1
            ...
    ```

diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index d6ce931fb97..2b71ce7a1fb 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -2305,7 +2305,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
 								OP1_INFO(), OP1_REG_ADDR(), RES_REG_ADDR(),
 								-1, -1,
 								zend_may_throw(opline, ssa_op, op_array, ssa),
-								opline->opcode, NULL)) {
+								opline->opcode, NULL, true)) {
 							goto jit_failure;
 						}
 						goto done;
@@ -2331,15 +2331,11 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
 								OP1_INFO(), OP1_REG_ADDR(), res_addr,
 								ssa->cfg.blocks[b].successors[0], ssa->cfg.blocks[b].successors[1],
 								zend_may_throw(opline, ssa_op, op_array, ssa),
-								opline->opcode, NULL)) {
+								opline->opcode, NULL, true)) {
 							goto jit_failure;
 						}
 						goto done;
 					case ZEND_ISSET_ISEMPTY_CV:
-						if ((opline->extended_value & ZEND_ISEMPTY)) {
-							// TODO: support for empty() ???
-							break;
-						}
 						if ((opline->result_type & IS_TMP_VAR)
 						 && (i + 1) <= end
 						 && ((opline+1)->opcode == ZEND_JMPZ
diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index 188144de495..9d85bdc9765 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -7519,7 +7519,7 @@ static int zend_jit_identical(zend_jit_ctx   *jit,
 	return 1;
 }

-static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr res_addr, uint32_t target_label, uint32_t target_label2, int may_throw, uint8_t branch_opcode, const void *exit_addr)
+static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info, zend_jit_addr op1_addr, zend_jit_addr res_addr, uint32_t target_label, uint32_t target_label2, int may_throw, uint8_t branch_opcode, const void *exit_addr, bool warn_undef)
 {
 	uint32_t true_label = -1;
 	uint32_t false_label = -1;
@@ -7570,7 +7570,7 @@ static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32
 		if (!(op1_info & ((MAY_BE_UNDEF|MAY_BE_ANY)-MAY_BE_TRUE))) {
 			always_true = true;
 		} else if (!(op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE)))) {
-			if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF)) {
+			if (opline->op1_type == IS_CV && (op1_info & MAY_BE_UNDEF) && warn_undef) {
 				ref = jit_ZVAL_ADDR(jit, op1_addr);
 				zend_jit_zval_check_undef(jit, ref, opline->op1.var, opline, false);
 			}
@@ -7615,7 +7615,7 @@ static int zend_jit_bool_jmpznz(zend_jit_ctx *jit, const zend_op *opline, uint32

 			ir_IF_TRUE_cold(if_type);

-			if (op1_info & MAY_BE_UNDEF) {
+			if ((op1_info & MAY_BE_UNDEF) && warn_undef) {
 				zend_jit_type_check_undef(jit,
 					type,
 					opline->op1.var,
@@ -8351,8 +8351,23 @@ static int zend_jit_isset_isempty_cv(zend_jit_ctx *jit, const zend_op *opline, u
 	uint32_t true_label = -1, false_label = -1;
 	ir_ref end_inputs = IR_UNUSED, true_inputs = IR_UNUSED, false_inputs = IR_UNUSED;

-	// TODO: support for empty() ???
-	ZEND_ASSERT(opline->extended_value != MAY_BE_RESOURCE);
+	if (opline->extended_value & ZEND_ISEMPTY) {
+		/* empty($cv) is !$cv, except that an undefined CV doesn't emit a warning. */
+		uint8_t branch_opcode;
+
+		if (!smart_branch_opcode) {
+			branch_opcode = ZEND_BOOL_NOT;
+		} else if (smart_branch_opcode == ZEND_JMPZ) {
+			branch_opcode = ZEND_JMPNZ;
+		} else if (smart_branch_opcode == ZEND_JMPNZ) {
+			branch_opcode = ZEND_JMPZ;
+		} else {
+			ZEND_UNREACHABLE();
+		}
+		/* zend_is_true() may call an object's cast handler. */
+		return zend_jit_bool_jmpznz(jit, opline, op1_info, op1_addr, res_addr,
+			target_label, target_label2, (op1_info & MAY_BE_OBJECT) != 0, branch_opcode, exit_addr, false);
+	}

 	if (smart_branch_opcode && !exit_addr) {
 		if (smart_branch_opcode == ZEND_JMPZ) {
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 755d8adbeb2..e1836b82610 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -2074,10 +2074,6 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
 					ADD_OP1_TRACE_GUARD();
 					break;
 				case ZEND_ISSET_ISEMPTY_CV:
-					if ((opline->extended_value & ZEND_ISEMPTY)) {
-						// TODO: support for empty() ???
-						break;
-					}
 					ADD_OP1_TRACE_GUARD();
 					break;
 				case ZEND_IN_ARRAY:
@@ -5682,7 +5678,7 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer,
 								op1_info, OP1_REG_ADDR(), RES_REG_ADDR(),
 								-1, -1,
 								zend_may_throw(opline, ssa_op, op_array, ssa),
-								opline->opcode, NULL)) {
+								opline->opcode, NULL, true)) {
 							goto jit_failure;
 						}
 						goto done;
@@ -5752,7 +5748,7 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer,
 								op1_info, OP1_REG_ADDR(), res_addr,
 								-1, -1,
 								zend_may_throw(opline, ssa_op, op_array, ssa),
-								smart_branch_opcode, exit_addr)) {
+								smart_branch_opcode, exit_addr, true)) {
 							goto jit_failure;
 						}
 						goto done;
@@ -5784,10 +5780,6 @@ static zend_vm_opcode_handler_t zend_jit_trace(zend_jit_trace_rec *trace_buffer,
 						}
 						goto done;
 					case ZEND_ISSET_ISEMPTY_CV:
-						if ((opline->extended_value & ZEND_ISEMPTY)) {
-							// TODO: support for empty() ???
-							break;
-						}
 						op1_info = OP1_INFO();
 						op1_addr = OP1_REG_ADDR();
 						if (orig_op1_type != IS_UNKNOWN