Fix GH-21368 follow-up: escape_if_undef on CALL VM#22028
Draft
iliaal wants to merge 1 commit into
Draft
Conversation
Two issues reachable on debug Clang/CALL builds of PHP-8.4. ir_CONST_ADDR for orig_handler trips jit_CONST_FUNC_PROTO's FUNC_ADDR assertion when another site reuses the same handler via ir_CONST_FC_FUNC in the same trace compile (Symfony HttpClient test suite repro). The ir_TAILCALL_1 to orig_handler leaves execute_ex with stale execute_data after a frame-changing handler. PHP-8.4's CALL VM needs a non-zero return to reload from EG(current_execute_data); the tail-call discards that signal. gh21267.phpt with aggressive jit_hot_* ini reproduces a crash in ZEND_DO_FCALL. Use ir_CONST_FC_FUNC to register the handler as FUNC_ADDR, and mirror zend_jit_tail_handler's ZEND_FUNC_RECURSIVE_DIRECTLY branch (ir_CALL_1 + ir_RETURN(1)) so execute_ex reloads. The ir_CAST_FC_FUNC on x86 becomes dead.
iliaal
referenced
this pull request
May 12, 2026
…#21368) When the JIT defers the IS_UNDEF check for FETCH_OBJ_R to the result type guard, the deoptimization escape path dispatches to opline->handler via the trace_escape stub. If opline->handler has been overwritten with JIT code (e.g. a function entry trace), this creates an infinite loop. Fix by dispatching to the original VM handler (orig_handler from the trace extension) instead of going through the trace_escape stub. This avoids the extra IS_UNDEF guard on every property read while correctly handling the rare IS_UNDEF case during deoptimization. Also set current_op_array in zend_jit_trace_exit_to_vm so that the blacklisted exit deoptimizer can resolve orig_handler, covering the case where side trace compilation is exhausted. Closes GH-21368.
arnaud-lb
approved these changes
May 13, 2026
Member
arnaud-lb
left a comment
There was a problem hiding this comment.
This looks good to me.
Thank you!
Comment on lines
8100
to
+8104
| if (GCC_GLOBAL_REGS) { | ||
| ir_TAILCALL(IR_VOID, ref); | ||
| } else { | ||
| #if defined(IR_TARGET_X86) | ||
| ref = ir_CAST_FC_FUNC(ref); | ||
| #endif | ||
| ir_TAILCALL_1(IR_I32, ref, jit_FP(jit)); | ||
| ir_CALL_1(IR_I32, ref, jit_FP(jit)); | ||
| ir_RETURN(ir_CONST_I32(1)); |
Member
There was a problem hiding this comment.
When merging up, you can do something like this:
if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) {
ir_TAILCALL(IR_OPCODE_HANDLER_RET, ref);
} else {
ir_ref opline_ref = ir_CALL_2(IR_OPCODE_HANDLER_RET, ref, jit_FP(jit), jit_IP(jit));
zend_jit_vm_enter(jit, opline_ref);
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #21368, kept separate from #21710 per arnaud-lb's review.
Two fixes in
zend_jit_escape_if_undefon PHP-8.4 CALL VM (full mechanism in the commit message): registerorig_handlerasIR_FUNC_ADDRinstead ofIR_ADDR(was tripping theFUNC_ADDRassertion in side-trace compile), and replaceir_TAILCALL_1withir_CALL_1 + ir_RETURN(1)soexecute_exreloadsexecute_dataafter the deopt.gh21368_call_vm.phptcovers the CALL-VM crash. TheFUNC_ADDRassertion case reproduces via Symfony HttpClient'stestIgnoreErrors/testDnsError/testInlineAuth/testBadRequestBodysequence (side trace fromprepareRequest():100); smaller subsets don't fire. COMMUNITY CI catches regressions.With fix: Symfony runs 1170/1171 (1 DNS env failure), new test passes. Without: Symfony aborts at test 14 on the assertion, new test SEGVs in
ZEND_DO_FCALL./cc @arnaud-lb: you offered to handle this; I took a first stab. Please reshape if needed.