Skip to content

Fix GH-25164: Function JIT keeps a loop variable in a register across a call - #453

Closed
iliaal wants to merge 1 commit into
PHP-8.4from
fix/gh-25164-jit-entry-phi-liveness
Closed

iliaal wants to merge 1 commit into
PHP-8.4from
fix/gh-25164-jit-entry-phi-liveness

Conversation

@iliaal

@iliaal iliaal commented Nov 3, 2026

Copy link
Copy Markdown
Owner

zend_jit_may_be_in_reg() keeps a value in a register only if its live range doesn't cross an ENTRY block, but the phi-use walk skipped a phi located in the ENTRY block itself and a phi in the value's own definition block. A user call leaves the JIT code and comes back through an ENTRY block, so $i in while ($i++ < 5) leaf(); stayed in a clobbered register and the loop ran once or forever under opcache.jit=function and 1203.

For a Pi in the definition block the walk follows only its real incoming edge (p->pi), since SSA copies a Pi's source into every predecessor slot. Register allocation over the ext/opcache/tests/jit bodies is unchanged except in the new test.

Reproduced on PHP-8.4 and master debug builds (x86-64, hybrid VM). php#18392 doesn't fix it.

Fixes phpGH-25164

Check the Phi use's ENTRY block and walk incoming edges for uses in the
definition block. This keeps loop-carried values in memory across user
calls, while preserving zero-length Pi intervals. DO_UCALL, DO_FCALL, and
DO_FCALL_BY_NAME reproduce the bug; yield and include do not.

Fixes phpGH-25164
@iliaal
iliaal force-pushed the fix/gh-25164-jit-entry-phi-liveness branch from b0d9797 to 658d9a8 Compare November 3, 2026 18:26
@iliaal

iliaal commented Nov 3, 2026

Copy link
Copy Markdown
Owner Author

Promoted to php#25178.

@iliaal iliaal closed this Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant