Match macOS objects and paths in the native install, loop poll and profile tests

The nightly macOS lanes failed these tests on Linux-specific expectations:

- native-install: installNativeCli resolves the installation directory,
  and on macOS /tmp is a link to /private/tmp. The fixture root is now
  resolved too.
- counted-loop-emission: two tests counted polls of @scr_exc_flag, the
  thread's pending word that ELF executables read. Other targets poll the
  process-wide alert word. The tests now emit for x86_64 Linux, as the
  file's other ELF tests do.
- native-partitions: Mach-O objects define an ltmp<N> label at the start
  of each section, which the function lists now leave out.
- native-profile: profile temperature appears in ELF section names
  (.text.hot.); Mach-O sections carry no such prefix. The section name
  check, and the check that recovered block counts change the objects (they
  move f4 into the hot section), now run on ELF objects only.
This commit is contained in:
Chris Tate
2026-10-29 06:36:29 -05:00
parent 02d7230c66
commit ea0cdd0418
4 changed files with 33 additions and 8 deletions
+5 -2
View File
@@ -4,6 +4,7 @@ import {
mkdtempSync,
mkdirSync,
readFileSync,
realpathSync,
renameSync,
rmSync,
statSync,
@@ -22,8 +23,10 @@ afterEach(() => {
});
function fixture() {
const root = mkdtempSync(
join(process.platform === "win32" ? tmpdir() : "/tmp", "scriptc-install-"),
// installNativeCli resolves the installation directory, and on macOS /tmp
// is a link to /private/tmp, so the fixture's paths start resolved.
const root = realpathSync(
mkdtempSync(join(process.platform === "win32" ? tmpdir() : "/tmp", "scriptc-install-")),
);
roots.push(root);
const directory = join(root, "installation");
@@ -85,7 +85,11 @@ test("worker programs poll counted loops over typed arrays between blocks of ite
const outPath = join(dir, `${workers ? "worker" : "plain"}.ir.json`);
const result = await compile(entry, { outPath, outDir: dir, outputKind: "ir" });
if (!result.ok) throw new Error(result.diagnostics.map((d) => d.message).join("\n"));
return emitLlvmModule(deserializeModule(await readFile(outPath, "utf8")));
// ELF executables poll the thread's pending word (@scr_exc_flag);
// other targets read the process-wide alert word.
return emitLlvmModule(deserializeModule(await readFile(outPath, "utf8")), {
targetTriple: "x86_64-unknown-linux-gnu",
});
};
// Every emitted version of a function (the number and int32 entries).
const bodies = (ll: string, name: string): string =>
@@ -254,7 +258,10 @@ test("worker programs poll no loop whose proven ranges bound its runs inside ano
const outPath = join(dir, "bounded.ir.json");
const result = await compile(entry, { outPath, outDir: dir, outputKind: "ir" });
if (!result.ok) throw new Error(result.diagnostics.map((d) => d.message).join("\n"));
const ll = emitLlvmModule(deserializeModule(await readFile(outPath, "utf8")));
// The polls counted below read the thread's pending word of ELF executables.
const ll = emitLlvmModule(deserializeModule(await readFile(outPath, "utf8")), {
targetTriple: "x86_64-unknown-linux-gnu",
});
const body = (name: string): string =>
[
...ll.matchAll(
@@ -8,6 +8,9 @@ import { afterEach, describe, expect, test } from "vitest";
import { nativeCodegenTarget, nativeHelperForTarget } from "../src/backend/targets.js";
const execFileAsync = promisify(execFile);
// Mach-O objects define a local label (ltmp0, ltmp1, ...) at the start of
// each section; it names no function.
const machoSectionLabel = /^ltmp\d+$/;
const target = nativeCodegenTarget();
const helperSpec = target === null ? null : nativeHelperForTarget(target);
const helper = (() => {
@@ -264,7 +267,7 @@ describe.runIf(helper !== null)("LLVM program partitions", () => {
return stdout
.split("\n")
.map((line) => /^[0-9a-fA-F]+ [Tt] _?(\w+)$/.exec(line.trim())?.[1])
.filter((name): name is string => name !== undefined);
.filter((name): name is string => name !== undefined && !machoSectionLabel.test(name));
};
const whole = await defined(
(await emit(dir, program(), { outputs: 1, optLevel: "2", name: "whole" }))[0]!,
@@ -390,7 +393,12 @@ describe.runIf(helper !== null)("LLVM program partitions", () => {
expect(stdout).toMatch(/ t _?heavy$/m);
for (const part of parts.slice(1)) {
const listed = await execFileAsync("nm", ["--defined-only", part]);
expect(listed.stdout.trim()).toBe("");
const symbols = listed.stdout
.split("\n")
.filter(
(line) => line.trim() !== "" && !machoSectionLabel.test(line.trim().split(" ").at(-1)!),
);
expect(symbols).toEqual([]);
}
// The first output is the object that a single output produces.
const first = await readFile(parts[0]!);
@@ -8,6 +8,7 @@ import { afterEach, describe, expect, test } from "vitest";
import { nativeCodegenTarget, nativeHelperForTarget } from "../src/backend/targets.js";
const execFileAsync = promisify(execFile);
const isElf = (object: Buffer) => object.subarray(0, 4).equals(Buffer.from("\x7fELF", "latin1"));
const target = nativeCodegenTarget();
const helperSpec = target === null ? null : nativeHelperForTarget(target);
const helper = (() => {
@@ -327,7 +328,9 @@ describe.runIf(helper !== null)("LLVM profile-guided optimization", () => {
use: merged,
});
const bytes = Buffer.concat(await Promise.all(objects.map((object) => readFile(object))));
expect(bytes.includes(".text.hot.main")).toBe(true);
// ELF objects name each function's section by its profile temperature;
// Mach-O sections carry no such prefix.
if (isElf(bytes)) expect(bytes.includes(".text.hot.main")).toBe(true);
const optimized = await link(dir, objects, "laidout");
expect(await run(optimized, 1)).toBe(inlinedExit(1));
expect(await run(optimized, 3)).toBe(inlinedExit(3));
@@ -497,8 +500,12 @@ describe.runIf(helper !== null)("LLVM profile-guided optimization", () => {
recovered: 1,
});
expect(await run(await link(dir, recovered.outputs, "recovered"), 2)).toBe(expectedExit(2));
// The recovered counts move f4 into the hot section. ELF objects name
// it (.text.hot.); Mach-O sections carry no such prefix, so there the
// two builds are the same.
const bytes = (paths: string[]) => Promise.all(paths.map((path) => readFile(path)));
expect(await bytes(recovered.outputs)).not.toEqual(await bytes(stale.outputs));
const recoveredBytes = await bytes(recovered.outputs);
if (isElf(recoveredBytes[0]!)) expect(recoveredBytes).not.toEqual(await bytes(stale.outputs));
// The block counts key the partition cache.
const before = await cacheEntries(cache);