mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
Excuse only reproduced differences as nondeterministic, and read vendor archive symbols
A program is nondeterministic only when every artifact that differs also differs between two baseline builds; a deterministic difference in another artifact still fails the gate. The runtime-only check now reads the symbols of changed vendor archives as well as runtime objects.
This commit is contained in:
@@ -523,6 +523,14 @@ export function runtimePackDiff(baseline, candidate) {
|
||||
};
|
||||
}
|
||||
|
||||
/** The changed runtime pack files whose symbols executables link: runtime
|
||||
* objects and vendor archives. */
|
||||
export function linkedRuntimeFiles(diff) {
|
||||
return [...diff.changed, ...diff.added, ...diff.removed].filter(
|
||||
(path) => path.endsWith(".o") || path.endsWith(".a"),
|
||||
);
|
||||
}
|
||||
|
||||
/** Defined functions of textual LLVM IR (llvm-dis output) as name → hash of
|
||||
* the body. Attribute group and metadata numbers are dropped first, since
|
||||
* an unrelated change elsewhere in the unit renumbers them. */
|
||||
@@ -638,7 +646,9 @@ export function programLines(entry) {
|
||||
* LLVM, is "runtime-only" when `runtimeOnly` says the changed runtime
|
||||
* objects explain it (the gate checks the runtime packs and the symbols).
|
||||
* A difference the baseline also shows when it builds the program again is
|
||||
* "nondeterministic". Neither fails the gate unless `strict` is set. */
|
||||
* "nondeterministic": `nondeterministic` lists the artifact kinds whose
|
||||
* baseline rebuild differs, and every differing artifact must be among
|
||||
* them. Neither fails the gate unless `strict` is set. */
|
||||
export function programVerdict({
|
||||
baseline,
|
||||
candidate,
|
||||
@@ -646,7 +656,7 @@ export function programVerdict({
|
||||
allowed,
|
||||
required = false,
|
||||
runtimeOnly = false,
|
||||
nondeterministic = false,
|
||||
nondeterministic = [],
|
||||
strict = false,
|
||||
}) {
|
||||
if (!baseline.ok || !candidate.ok) {
|
||||
@@ -659,7 +669,8 @@ export function programVerdict({
|
||||
if (allowed) return { verdict: "allowed", fails: false };
|
||||
if (runtimeOnly && differing.every(([kind]) => kind === "exe"))
|
||||
return { verdict: "runtime-only", fails: strict };
|
||||
if (nondeterministic) return { verdict: "nondeterministic", fails: strict };
|
||||
if (differing.every(([kind]) => nondeterministic.includes(kind)))
|
||||
return { verdict: "nondeterministic", fails: strict };
|
||||
return { verdict: "differs", fails: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
DYNAMIC_WORKLOADS,
|
||||
definedFunctionNames,
|
||||
dynamicTierSymbols,
|
||||
linkedRuntimeFiles,
|
||||
llvmFunctionHashes,
|
||||
median,
|
||||
nmOutput,
|
||||
@@ -502,7 +503,7 @@ function runtimeChanges() {
|
||||
const objects = [...diff.changed, ...diff.added, ...diff.removed].filter((path) =>
|
||||
path.endsWith(".o"),
|
||||
);
|
||||
for (const path of objects)
|
||||
for (const path of linkedRuntimeFiles(diff))
|
||||
for (const root of [trees.baseline, trees.candidate]) {
|
||||
const file = join(packDir(root), path);
|
||||
if (!existsSync(file)) continue;
|
||||
@@ -808,7 +809,7 @@ await pool(
|
||||
nondeterministic,
|
||||
strict: values.strict,
|
||||
});
|
||||
let { verdict, fails } = judge(false);
|
||||
let { verdict, fails } = judge([]);
|
||||
// Build a difference the change cannot explain once more with the
|
||||
// baseline: when the baseline's two builds differ in the same
|
||||
// artifacts, the difference is the build's, not the change's.
|
||||
@@ -822,7 +823,7 @@ await pool(
|
||||
);
|
||||
if (again.dir && !values.keep) rmSync(again.dir, { recursive: true, force: true });
|
||||
}
|
||||
if (recheck?.length) ({ verdict, fails } = judge(true));
|
||||
if (recheck?.length) ({ verdict, fails } = judge(recheck));
|
||||
}
|
||||
const entry = {
|
||||
id: program.id,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
confinedToRuntime,
|
||||
definedFunctionNames,
|
||||
dynamicTierSymbols,
|
||||
linkedRuntimeFiles,
|
||||
llvmFunctionHashes,
|
||||
mergeReports,
|
||||
nmOutput,
|
||||
@@ -222,14 +223,35 @@ test("the zero-cost gate classifies runtime-only and nondeterministic difference
|
||||
});
|
||||
// A changed runtime cannot explain different LLVM.
|
||||
expect(verdict(llvmToo, { runtimeOnly: true })).toEqual({ verdict: "differs", fails: true });
|
||||
expect(verdict(llvmToo, { nondeterministic: true })).toEqual({
|
||||
expect(verdict(llvmToo, { nondeterministic: ["llvm", "exe"] })).toEqual({
|
||||
verdict: "nondeterministic",
|
||||
fails: false,
|
||||
});
|
||||
expect(verdict(llvmToo, { nondeterministic: true, strict: true })).toEqual({
|
||||
expect(verdict(llvmToo, { nondeterministic: ["llvm", "exe"], strict: true })).toEqual({
|
||||
verdict: "nondeterministic",
|
||||
fails: true,
|
||||
});
|
||||
// A nondeterministic executable does not excuse a deterministic LLVM difference.
|
||||
expect(verdict(llvmToo, { nondeterministic: ["exe"] })).toEqual({
|
||||
verdict: "differs",
|
||||
fails: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("the zero-cost gate reads the symbols of changed runtime objects and vendor archives", () => {
|
||||
expect(
|
||||
linkedRuntimeFiles({
|
||||
changed: [
|
||||
"artifacts/release/runtime/default/scr_a.o",
|
||||
"artifacts/release/runtime/default/scr_a.bc",
|
||||
],
|
||||
added: ["artifacts/vendor/libz/libscriptc-zlib.a"],
|
||||
removed: [],
|
||||
}),
|
||||
).toEqual([
|
||||
"artifacts/release/runtime/default/scr_a.o",
|
||||
"artifacts/vendor/libz/libscriptc-zlib.a",
|
||||
]);
|
||||
});
|
||||
|
||||
test("the zero-cost gate names the runtime pack files and functions that changed", () => {
|
||||
|
||||
Reference in New Issue
Block a user