Files
scriptc/packages/compiler/test/array-length-range-emission.test.ts
T
Chris Tate 2bb7e7ec7a Load array lengths with the range of a JS array length
An array's length is at most 2^32 - 1 (the runtime's SCR_ARR_MAX_LENGTH).
On 64-bit targets the length loads now carry that range (`!range`), so
LLVM folds a length converted to double and back to an integer, and
simplifies the comparisons that use it. A length stored into an int32
local or used as an index no longer goes through double: tsc-ts has 18%
fewer int/double conversion instructions.
2026-10-16 07:33:26 -05:00

56 lines
2.0 KiB
TypeScript

import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { compile } from "../src/index.js";
async function emit(source: string, outputKind: "llvm" | "asm"): Promise<string> {
const dir = await mkdtemp(join(tmpdir(), "scriptc-array-length-range-"));
try {
const entry = join(dir, "main.ts");
const outPath = join(dir, outputKind === "llvm" ? "main.ll" : "main.s");
await writeFile(entry, source);
const result = await compile(entry, { outDir: dir, outPath, outputKind });
expect(result.ok, JSON.stringify(result.diagnostics)).toBe(true);
return await readFile(outPath, "utf8");
} finally {
await rm(dir, { recursive: true, force: true });
}
}
const source = `
class Signature {
parameters: string[];
rest: boolean;
constructor(parameters: string[], rest: boolean) {
this.parameters = parameters;
this.rest = rest;
}
}
function count(signature: Signature): number {
const length = signature.parameters.length;
if (signature.rest) return length + signature.parameters[length - 1]!.length;
return length;
}
const signature = new Signature(["a", "bb"], process.argv.length > 1);
console.log(count(signature), count(new Signature(["x"], false)));
`;
test.runIf(process.arch === "x64" || process.arch === "arm64")(
"array length loads carry the range of a JS array length",
async () => {
const llvm = await emit(source, "llvm");
const body = /^define internal double @sc_bf_count\([^]*?^}/m.exec(llvm)?.[0] ?? "";
expect(body).toMatch(/= load i64, ptr %\S+(, !tbaa !\d+)?, !range !\{i64 0, i64 4294967296\}/);
},
);
test.runIf(process.arch === "x64")(
"a length used as an index is not converted to double and back",
async () => {
const asm = await emit(source, "asm");
const body = /^sc_bf_count:[^]*?\.cfi_endproc/m.exec(asm)?.[0] ?? "";
expect(body).not.toMatch(/cvtsi2sd|cvttsd2si/);
},
);