Summary
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
function build(): number[] { const arr: number[] = []; for (let i = 0; i < 250000; i++) arr.push(i); return arr; }
function run(): number {
const arr = build(); // <-- array arrives from a CALL
let checksum = 0;
for (let iter = 0; iter < 25; iter++) {
for (let i = 0; i < arr.length; i++) { arr[i] = arr[i] + 1; }
checksum = checksum + arr[0] + arr[arr.length - 1];
}
return checksum + arr.length;
}
const t0 = Date.now();
const r = 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:
| codegen |
time |
| v0.5.1255 |
190 ms |
main @ 0960415 |
1300 ms |
| node 26 |
19 ms |
Evidence
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.
Summary
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 genericjs_array_get_index_or_string/js_typed_feedback_array_set_index_or_stringpath, costing ~6.8x.This is a codegen-only regression: the lowered HIR is byte-identical between the good and bad compilers.
Repro
Measurement
Both binaries linked against the identical v0.5.1255 release runtime archive, so only codegen differs:
main@ 0960415Evidence
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:run)js_typed_feedback_numeric_array_index_get_guardjs_typed_feedback_numeric_array_index_set_guardjs_array_get_index_or_stringjs_typed_feedback_array_set_index_or_stringThe 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 anumber[]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-hirin83f4fcba5..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 tocrates/perry-codegen/src/collectors/pointer_locals.rswhich addedMAX_FIXPOINT_ITERS/MAX_FIXPOINT_LOCALScurtailment 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.