From 2a4309646cc17806e5285325ebd6fc5cde6c78ae Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:13:42 +0200 Subject: [PATCH] JIT: Avoid exception check on function leave when possible --- ext/opcache/jit/zend_jit.c | 20 +++++++++++++++++++- ext/opcache/jit/zend_jit_ir.c | 6 ++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index e5d26096984b..cab2e9cd7498 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1432,6 +1432,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op int call_level = 0; void *checkpoint = NULL; bool recv_emitted = false; /* emitted at least one RECV opcode */ + bool leave_may_throw = false; /* a destructor may run on the common return path */ uint8_t smart_branch_opcode; uint32_t target_label, target_label2; uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_mem_info; @@ -2297,6 +2298,12 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op op1_info, OP1_REG_ADDR())) { goto jit_failure; } + /* An unused return value is destroyed by zend_jit_return() */ + if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) + && (op1_info & MAY_BE_RC1) + && (op1_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { + leave_may_throw = true; + } } goto done; case ZEND_BOOL: @@ -2941,6 +2948,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op goto jit_failure; } left_frame = true; + leave_may_throw = true; } if (!left_frame) { int j; @@ -2958,11 +2966,21 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (!zend_jit_free_cv(&ctx, info, j)) { goto jit_failure; } + if ((info & MAY_BE_REF) + || ((info & MAY_BE_RC1) + && (info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_RESOURCE)))) { + leave_may_throw = true; + } } } } + if (ZEND_OBSERVER_ENABLED + /* releasing the closure may destroy its bound $this or used variables */ + || (op_array->fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) == ZEND_ACC_CLOSURE) { + leave_may_throw = true; + } if (!zend_jit_leave_func(&ctx, op_array, NULL, MAY_BE_ANY, left_frame, - NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) { + NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, leave_may_throw)) { goto jit_failure; } } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index ee852fd7db56..dc3b8a983841 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -11332,8 +11332,10 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, return 1; } else { - // JIT: if (EG(exception)) - ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); + if (may_throw) { + // JIT: if (EG(exception)) + ir_GUARD_NOT(ir_LOAD_A(jit_EG(exception)), jit_STUB_ADDR(jit, jit_stub_leave_throw)); + } // JIT: opline = EX(opline) + 1 jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline))); jit_STORE_IP(jit, ir_ADD_OFFSET(jit_IP(jit), sizeof(zend_op)));