Skip to content

JIT: Record fake init calls for subtraces (fixes array_map optimization) - #23450

Merged
arnaud-lb merged 1 commit into
php:masterfrom
arnaud-lb:array-map-opt-loop-call
Sep 4, 2026
Merged

JIT: Record fake init calls for subtraces (fixes array_map optimization)#23450
arnaud-lb merged 1 commit into
php:masterfrom
arnaud-lb:array-map-opt-loop-call

Conversation

@arnaud-lb

@arnaud-lb arnaud-lb commented Aug 25, 2026

Copy link
Copy Markdown
Member

The array_map optimization may emit loops inside an INIT_FCALL-DO_FCALL sequence, which was not possible before. JIT doesn't expect that and forgets about pending calls when starting a subtrace for the loop.

Fix by recording fake init calls in zend_jit_trace_subtrace().

Blocks #23254

The array_map optimization may emit loops inside an INIT_FCALL-DO_FCALL
sequence, which was not possible before. JIT doesn't expect that and forgets
about pending calls when starting a subtrace for the loop.

Fix by recording fake init calls in zend_jit_trace_subtrace().
@arnaud-lb
arnaud-lb marked this pull request as ready for review August 25, 2026 14:07
@arnaud-lb
arnaud-lb requested a review from dstogov as a code owner August 25, 2026 14:07
@arnaud-lb
arnaud-lb requested a review from iliaal September 4, 2026 12:23

@iliaal iliaal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

One thing beyond this patch: zend_jit_trace_record_fake_init_call_ex() returns idx on every path, including when TRACE_RECORD hits the limit and only sets its function-local stop. So the if (idx < 0) here, like the three at the existing callers, cannot fire, and the recursion writes one record per level as it unwinds before the new aggregate check runs. With opcache.jit_max_trace_length defaulting to 1024 and trace_buffer[] sized ZEND_JIT_TRACE_MAX_LENGTH = 1024, that overshoot spends the two-slot headroom instead of stopping with TOO_LONG. Propagating the overflow out of the helper seems better than a fourth dead check.

Separately: with several chained fake INIT_CALL records, zend_jit_trace_find_init_fcall_op() walks backward and returns NULL, so num_args falls back to -1. Looks harmless, but is #23449 meant to land before this one?

@arnaud-lb

Copy link
Copy Markdown
Member Author

So the if (idx < 0) here, like the three at the existing callers, cannot fire

Good catch, I didn't notice this. I added this check since every call to zend_jit_trace_record_fake_init_call() did it, but apparently all these checks are useless now. I will address this in a separate PR.

Separately: with several chained fake INIT_CALL records, zend_jit_trace_find_init_fcall_op() walks backward and returns NULL, so num_args falls back to -1. Looks harmless, but is #23449 meant to land before this one?

I confirm it's harmless: zend_jit_trace_find_init_fcall_op() enables some optimizations when it can find the init opline, but otherwise the effect is just lost optimization opportunities.

@arnaud-lb
arnaud-lb merged commit 1053403 into php:master Sep 4, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants