mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Skip the arena clear for record literals that write every field (#2470)
This commit is contained in:
@@ -67,6 +67,38 @@ console.log(total);
|
||||
expect(body).not.toContain("ScrArena.closed");
|
||||
expect(body).not.toContain("@scr_arena_disposal");
|
||||
}
|
||||
// A record literal stores every field, and no dispose entry reads them:
|
||||
// the record's constructor does not clear the object. A class constructor
|
||||
// may leave fields unset, so the class's does.
|
||||
const record = constructors.find((body) => body.includes("@sc_rnew_"))!;
|
||||
const cls = constructors.find((body) => !body.includes("@sc_rnew_"))!;
|
||||
expect(record).not.toContain("@llvm.memset");
|
||||
expect(cls).toContain("@llvm.memset");
|
||||
});
|
||||
|
||||
test("record literals in an arena that is not closed keep the clear their dispose entries need", async () => {
|
||||
// The items hold outside strings, so their groups are not closed.
|
||||
const llvm = await llvmFor(`
|
||||
interface Item {
|
||||
label: string;
|
||||
next: Item | null;
|
||||
}
|
||||
function chain(labels: string[], n: number): Item {
|
||||
return n === 0 ? { label: labels[0]!, next: null } : { label: labels[n % labels.length]!, next: chain(labels, n - 1) };
|
||||
}
|
||||
function length(item: Item): number {
|
||||
return item.next === null ? item.label.length : item.label.length + length(item.next);
|
||||
}
|
||||
const labels = ["a", "bb", "ccc"];
|
||||
let total = 0;
|
||||
for (let round = 0; round < 4; round++) total += length(chain(labels, 10 + round));
|
||||
console.log(total);
|
||||
`);
|
||||
expect(llvm).toMatch(/@scr_arena_open\(ptr %[\w.]+, i32 0\)/);
|
||||
const items = arenaConstructors(llvm).filter((body) => body.includes("@sc_rnew_"));
|
||||
expect(items.length).toBe(1);
|
||||
expect(items[0]).toContain(".dispose");
|
||||
expect(items[0]).toContain("@llvm.memset");
|
||||
});
|
||||
|
||||
test("an arena that is not closed keeps the dispose list in every arena-aware constructor", async () => {
|
||||
|
||||
@@ -275,6 +275,12 @@ export function arenaCurrentAddress(host: ArenaHost, dest: string): string[] | n
|
||||
* the arena is full. The bump, the clear, the header and the dispose entry
|
||||
* are inline (scr_arena.c's ScrArena ABI); scr_arena_obj takes a new chunk
|
||||
* and scr_arena_disposal grows the list.
|
||||
*
|
||||
* `clear` is what the bump path zeroes: the whole object, or only the
|
||||
* collector header, for callers that store every word of the object before
|
||||
* anything reads it (a record literal of a shape with no dispose entries:
|
||||
* `init` stores the count and the shape number, and the literal every
|
||||
* field). scr_arena_obj and the heap constructor return zeroed objects.
|
||||
*/
|
||||
export function arenaNewLines(
|
||||
host: ArenaHost,
|
||||
@@ -284,6 +290,7 @@ export function arenaNewLines(
|
||||
dispose: string | null,
|
||||
init: string[],
|
||||
result: string,
|
||||
clear: "object" | "header" = "object",
|
||||
): string[] {
|
||||
const S = host.sizeType;
|
||||
// ScrArena's fields and dispose entries are pointer-sized (scr_arena.c).
|
||||
@@ -328,8 +335,13 @@ export function arenaNewLines(
|
||||
`carve:`,
|
||||
` %end = getelementptr inbounds i8, ptr %bp, ${S} %n`,
|
||||
` store ptr %end, ptr %a`,
|
||||
` call void @llvm.memset.p0.${S}(ptr align 8 %bp, i8 0, ${S} %n, i1 false)`,
|
||||
);
|
||||
if (clear === "object")
|
||||
lines.push(` call void @llvm.memset.p0.${S}(ptr align 8 %bp, i8 0, ${S} %n, i1 false)`);
|
||||
else if (cyc !== null)
|
||||
lines.push(
|
||||
` call void @llvm.memset.p0.${S}(ptr align 8 %bp, i8 0, ${S} ${CYC_HDR}, i1 false)`,
|
||||
);
|
||||
if (cyc !== null)
|
||||
lines.push(
|
||||
` %id = trunc i32 %tid to i16`,
|
||||
|
||||
@@ -2387,8 +2387,20 @@ export function emitRecordShapes(
|
||||
` %shp = getelementptr inbounds %${struct}, ptr %o, i64 0, i32 ${SHAPE_WORD_INDEX}`,
|
||||
` store i32 ${shapeNumber}, ptr %shp ; shape number`,
|
||||
);
|
||||
// A record literal covers every field of its shape (ir.ts recordLit),
|
||||
// so without dispose entries, which read the fields at the drop, no
|
||||
// field is read before the literal stores it.
|
||||
defs.push(
|
||||
...arenaNewLines(host, newSym, sizeOf, traced ? cycType : null, dispose, init, "%o"),
|
||||
...arenaNewLines(
|
||||
host,
|
||||
newSym,
|
||||
sizeOf,
|
||||
traced ? cycType : null,
|
||||
dispose,
|
||||
init,
|
||||
"%o",
|
||||
dispose === null ? "header" : "object",
|
||||
),
|
||||
);
|
||||
if (dispose !== null)
|
||||
defs.push(
|
||||
|
||||
Reference in New Issue
Block a user