mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-23 06:10:02 +08:00
- Share dense array storage and move owned ranges without rebuilding containers. - Batch array construction and insertion while preserving evaluation order and bounded temporary storage. - Lower indexed mutations natively and preserve typed-array reversal across numeric element types.
69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
import { execFileSync, spawnSync } from "node:child_process";
|
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { expect, test } from "vitest";
|
|
import { compile, deserializeModule, serializeModule, validateModule } from "../src/index.js";
|
|
import { emitLlvmModule } from "../src/backend/llvm/emitter.js";
|
|
|
|
test("batched literals and insertions preserve values across buffer boundaries", async () => {
|
|
const dir = mkdtempSync(join(tmpdir(), "scriptc-array-batch-"));
|
|
try {
|
|
const entry = join(dir, "main.ts");
|
|
const output = join(dir, "main.ir.json");
|
|
// Cross two full buffers and a partial tail, for each native slot ABI.
|
|
const values = Array.from({ length: 131 }, (_, i) => i);
|
|
const lists = [
|
|
values.map((i) => String(i)),
|
|
values.map((i) => String(i % 3 === 0)),
|
|
values.map((i) => `"item-${i}"`),
|
|
];
|
|
const source = lists
|
|
.map(
|
|
(list, i) => `
|
|
const a${i} = [${list.join(",")}];
|
|
console.log(a${i}.unshift(${list.join(",")}));
|
|
console.log(a${i}.push(${list.join(",")}));
|
|
console.log(a${i}.join(":"));
|
|
`,
|
|
)
|
|
.join("\n");
|
|
writeFileSync(entry, source);
|
|
const lowered = await compile(entry, { outputKind: "ir", outDir: dir, outPath: output });
|
|
if (!lowered.ok) throw new Error(JSON.stringify(lowered.diagnostics));
|
|
const module = deserializeModule(readFileSync(output, "utf8"));
|
|
expect(validateModule(module)).toEqual([]);
|
|
for (const pointerBits of [32, 64] as const) {
|
|
const llvm = emitLlvmModule(deserializeModule(serializeModule(module)), { pointerBits });
|
|
expect(llvm).toContain("alloca [64 x i64]");
|
|
expect(llvm).not.toMatch(/alloca \[(?:131|128) x i64\]/);
|
|
const stackSlots = [...llvm.matchAll(/alloca \[(\d+) x i64\]/g)].reduce(
|
|
(total, match) => total + Number(match[1]),
|
|
0,
|
|
);
|
|
expect(stackSlots).toBeLessThanOrEqual(lists.length * 3 * 64);
|
|
expect(llvm).toContain(`@scr_arr_unshift_many(ptr`);
|
|
expect(llvm).toContain(`declare double @scr_arr_push_many(ptr, i${pointerBits}, ptr)`);
|
|
expect(llvm).toMatch(/zext i1 .* to i64/);
|
|
expect(llvm).toMatch(/ptrtoint ptr .* to i64/);
|
|
}
|
|
const result = await compile(entry, {
|
|
outDir: dir,
|
|
outPath: join(dir, "program"),
|
|
sanitize: process.env["SCRIPTC_SAN"] === "1",
|
|
});
|
|
if (!result.ok) throw new Error(JSON.stringify(result.diagnostics));
|
|
const expected = execFileSync(process.execPath, [entry], { encoding: "utf8" });
|
|
const actual = spawnSync(result.binaryPath, [], { encoding: "utf8", timeout: 30_000 });
|
|
expect(actual.error).toBeUndefined();
|
|
expect({
|
|
status: actual.status,
|
|
signal: actual.signal,
|
|
stdout: actual.stdout,
|
|
stderr: actual.stderr,
|
|
}).toEqual({ status: 0, signal: null, stdout: expected, stderr: "" });
|
|
} finally {
|
|
rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|