You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[regression] codegen: numeric array-index fast path lost when the array comes from a call — 6.8x slower (v0.5.1255 → main) #6299
Between v0.5.1255 and current main (0960415ad), codegen stopped emitting the guarded numeric fast path for array element access when the array arrives as the return value of a function call. The access falls back to the fully generic js_array_get_index_or_string / js_typed_feedback_array_set_index_or_string path, costing ~6.8x.
This is a codegen-only regression: the lowered HIR is byte-identical between the good and bad compilers.
Repro
functionbuild(): number[]{constarr: number[]=[];for(leti=0;i<250000;i++)arr.push(i);returnarr;}functionrun(): number{constarr=build();// <-- array arrives from a CALLletchecksum=0;for(letiter=0;iter<25;iter++){for(leti=0;i<arr.length;i++){arr[i]=arr[i]+1;}checksum=checksum+arr[0]+arr[arr.length-1];}returnchecksum+arr.length;}constt0=Date.now();constr=run();console.log("result",r,"ms",Date.now()-t0);
Measurement
Both binaries linked against the identical v0.5.1255 release runtime archive, so only codegen differs:
Lowered HIR for run() is identical between the two compilers (--trace hir --focus run), so this is not a lowering/type-inference change. The emitted LLVM differs:
helper (in run)
v0.5.1255
main
js_typed_feedback_numeric_array_index_get_guard
3
2
js_typed_feedback_numeric_array_index_set_guard
2
1
js_array_get_index_or_string
2
3
js_typed_feedback_array_set_index_or_string
1
2
The trigger is narrow: the array must arrive as a call's return value, even with an explicit : number[] annotation on both the local and the callee's return type. An array built inline in the same function, or passed in as a number[]parameter, still gets the fast path.
Decision site: crates/perry-codegen/src/expr/index_get.rs — expr_has_numeric_pointer_free_array_layout (guarded path, :1541) vs the generic fallback (:1483).
Window
11 commits touch perry-codegen/perry-hir in 83f4fcba5..0960415ad. HIR being identical rules out the HIR-only commits (#6270, #6245). The only commit in the window that touches the pointer-locals analysis feeding the predicate is ac5263d (#6253), a +155-line change to crates/perry-codegen/src/collectors/pointer_locals.rs which added MAX_FIXPOINT_ITERS/MAX_FIXPOINT_LOCALS curtailment and broadened the statement walk. That is the prime suspect but has not been confirmed by bisect.
This currently contaminates every array-heavy benchmark, including the ones tracked under #5094.
Correction to this issue's "Window" section — the suspect and the base were both wrong
Two premises in the original report do not hold. Recording them so nobody re-walks the dead end:
1. #6253 / pointer_locals.rs is innocent. Reverting pointer_locals.rs to its pre-#6253 content changes nothing. The suspicion came from it being the only commit in the assumed window touching the pointer-locals analysis — but the window itself was wrong.
2. The assumed-good base 83f4fcba5 is itself already bad. A bisect anchored on it is worthless: git trusts the good endpoint you hand it, so every rev tested was bad.
Why the base was wrong (the trap worth knowing): the "known-good v0.5.1255" reference measured against was a prebuilt binary sitting in a worktree's target/ — an artifact built from that worktree's work-in-progress tree, not from the commit the worktree currently points at. Building that same commit from source reproduces the bug. A prebuilt binary or .a in a worktree is not evidence about the commit that worktree is checked out to.
Real first bad commit (bisected on an IR signal, release builds): 95775b645 (#6258, "x++/x-- disqualifies the i32 fast path") — one commit before the base assumed good.
And #6258 is not itself a defect. It correctly removed an unsound overflow path (that is the #6072 fix). It merely unmasked a much older blind spot: collect_index_used_locals has no arm for Expr::PutValueSet, so a call-returned array's arr[i] = arr[i] + 1 subtree went opaque, the loop counter never joined index_used_locals, and it lost its i32 shadow slot.
The measured regression and everything in the Evidence section still stand. Fix in #6312.
Activity
proggeramlug commentedon Jul 12, 2026
Correction to this issue's "Window" section — the suspect and the base were both wrong
Two premises in the original report do not hold. Recording them so nobody re-walks the dead end:
1. #6253 /
pointer_locals.rsis innocent. Revertingpointer_locals.rsto its pre-#6253 content changes nothing. The suspicion came from it being the only commit in the assumed window touching the pointer-locals analysis — but the window itself was wrong.2. The assumed-good base
83f4fcba5is itself already bad. A bisect anchored on it is worthless: git trusts thegoodendpoint you hand it, so every rev tested was bad.Why the base was wrong (the trap worth knowing): the "known-good v0.5.1255" reference measured against was a prebuilt binary sitting in a worktree's
target/— an artifact built from that worktree's work-in-progress tree, not from the commit the worktree currently points at. Building that same commit from source reproduces the bug. A prebuilt binary or.ain a worktree is not evidence about the commit that worktree is checked out to.Real first bad commit (bisected on an IR signal, release builds):
95775b645(#6258, "x++/x-- disqualifies the i32 fast path") — one commit before the base assumed good.And #6258 is not itself a defect. It correctly removed an unsound overflow path (that is the #6072 fix). It merely unmasked a much older blind spot:
collect_index_used_localshas no arm forExpr::PutValueSet, so a call-returned array'sarr[i] = arr[i] + 1subtree went opaque, the loop counter never joinedindex_used_locals, and it lost its i32 shadow slot.The measured regression and everything in the Evidence section still stand. Fix in #6312.
fix(codegen): restore the numeric array-index fast path for call-retu…
fix(codegen): restore the numeric array-index fast path for call-retu…