From 398930e7de45a94bb1fab957404e1053312da825 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Fri, 23 Oct 2026 17:48:22 -0500 Subject: [PATCH] Store the frontend cache's LLVM placeholder as an empty array A native frontend cache entry stores the input's LLVM text after its metadata, with an empty placeholder in the input itself. The placeholder was an empty string where NativeExecutableInput declares an array of parts. Under Node the reader's cast of the parsed entry is not checked, but the native compiler checks it, so every read threw "expected array at $.input.llvm, got string". The reader treated that as a miss: no native build ever reused its frontend output, and an unchanged rebuild ran the frontend, lowering and LLVM emission again. The placeholder is now an empty array, and the cache test checks the stored member types. --- packages/compiler/src/native/prepare.test.ts | 4 +++- packages/compiler/src/native/prepare.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/compiler/src/native/prepare.test.ts b/packages/compiler/src/native/prepare.test.ts index 112484f4a..c03bfdd26 100644 --- a/packages/compiler/src/native/prepare.test.ts +++ b/packages/compiler/src/native/prepare.test.ts @@ -176,7 +176,9 @@ test("serialized IR travels as bounded parts through the frontend cache", () => const key = readdirSync(family).find((name) => /^[0-9a-f]{64}$/.test(name))!; const payload = JSON.parse(readFileSync(join(family, key), "utf8")); expect(payload.schema).toBe("scriptc.native-frontend.v3"); - expect(payload.input.llvm).toBe(""); + // The placeholders keep NativeExecutableInput's member types: the native + // compiler checks the parsed entry against them when it reads it back. + expect(payload.input.llvm).toEqual([]); expect(payload.input.ir).toBeNull(); expect(payload.llvm).toBe(first.llvm.join("")); expect(payload.ir).toEqual(first.ir); diff --git a/packages/compiler/src/native/prepare.ts b/packages/compiler/src/native/prepare.ts index a238029dc..ffd5bfd28 100644 --- a/packages/compiler/src/native/prepare.ts +++ b/packages/compiler/src/native/prepare.ts @@ -59,7 +59,7 @@ export interface NativeExecutableInput { /** The two large artifacts follow the input metadata as separate members so * an entry can be written as parts instead of one JSON string; the stored - * input carries empty placeholders (`""`, `null`) for them. */ + * input carries empty placeholders (`[]`, `null`) for them. */ interface FrontendCacheEntry { schema: "scriptc.native-frontend.v3"; probes: FrontendInputSnapshot; @@ -78,7 +78,7 @@ function* frontendCacheParts( const metadata = JSON.stringify({ schema: "scriptc.native-frontend.v3", probes, - input: { ...input, llvm: "", ir: null }, + input: { ...input, llvm: [], ir: null }, }); yield metadata.slice(0, metadata.length - 1) + ',"llvm":"'; for (const part of joinedParts(input.llvm))