From cd04e38cec99f698d3a7203d3c171aa5cbc54c68 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Wed, 28 Oct 2026 21:38:18 -0500 Subject: [PATCH] Mark the borrowed array read fallback as read-only A borrowed element read of a reference array falls back to scr_arr_borrow_ref when the element is outside the dense fast path. The call carried no memory attributes, so LLVM assumed it could write any memory: a loop holding such a read reloaded the array's header on every iteration, after inlining too. The fallback only reads the array (a hole or an out-of-range index traps), so it now carries the attributes of the other array read fallbacks, and the header loads stay hoisted. --- packages/compiler/src/backend/llvm/local-array-reads.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/backend/llvm/local-array-reads.ts b/packages/compiler/src/backend/llvm/local-array-reads.ts index 8e9b1434d..f2cc49cb0 100644 --- a/packages/compiler/src/backend/llvm/local-array-reads.ts +++ b/packages/compiler/src/backend/llvm/local-array-reads.ts @@ -682,7 +682,11 @@ export function emitBorrowedArrayRead( B.startBlock(slow); host.declare("declare ptr @scr_arr_borrow_ref(ptr, double)"); const value = B.tmp(); - B.line(`${value} = call ptr @scr_arr_borrow_ref(ptr ${array.name}, double ${index.name})`); + // The fallback only reads the array (a hole or an out-of-range index + // traps), so loops keep the array's header loads hoisted past it. + B.line( + `${value} = call ptr @scr_arr_borrow_ref(ptr ${array.name}, double ${index.name})${READ_FALLBACK}`, + ); B.line(`store ptr ${value}, ptr ${slot}`); B.br(join); B.startBlock(join);