Name program modules by the entry's file name so profiles match in every checkout

Each partition's source file name was the program entry's absolute path,
and profiles name a module's local functions after it. Since programs keep
partition-local functions internal (#2449), a profile collected in one
directory missed those functions in builds from any other: a native
compiler profile collected on main named 27% of its records, holding 2.2%
of its counts, after the checkout's path. Modules now carry the entry's
file name. The partition cache key still includes the full path.
This commit is contained in:
Chris Tate
2026-10-28 05:44:38 -05:00
parent d6bb226f48
commit 23d95a99fd
3 changed files with 22 additions and 3 deletions
+1 -1
View File
@@ -266,7 +266,7 @@ $ scriptc merge-profiles -o app.profdata profiles/*.profraw
$ scriptc build app.ts --optimization=speed --profile-use app.profdata
```
Train on workloads like the ones the program will run: code the training never reached is optimized as without a profile. The profile can be committed and reused as the program changes. Functions whose control flow changed since the profile was collected lose their profile, and the rest keep theirs, so regenerate it when much of the program has changed. [Build parallelism](/docs/how-it-works#build-parallelism) describes what a profile adds to build time.
Train on workloads like the ones the program will run: code the training never reached is optimized as without a profile. The profile can be committed and reused as the program changes, and in other checkouts and on other machines: it names functions by the entry's file name, not its directory. Functions whose control flow changed since the profile was collected lose their profile, and the rest keep theirs, so regenerate it when much of the program has changed. [Build parallelism](/docs/how-it-works#build-parallelism) describes what a profile adds to build time.
`scriptc build --profile-use` prints one line on stderr with how the profile matched the program's functions:
+11 -2
View File
@@ -42,6 +42,7 @@
#include "llvm/Support/Caching.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SHA256.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/ThreadPool.h"
@@ -381,6 +382,14 @@ std::optional<EmitFailure> inspectProfile(const EmitOptions &Options) {
return std::nullopt;
}
// The source file name a program's modules carry: the entry's file name,
// without its directory. Profiles name a module's local functions after it
// ("<name>;<function>"), so a profile collected in one checkout matches the
// builds of every other one.
std::string moduleSourceName(StringRef SourcePath) {
return sys::path::filename(SourcePath).str();
}
// Owns the analysis managers a pipeline needs for one module.
struct Pipeline {
PassInstrumentationCallbacks Callbacks;
@@ -703,7 +712,7 @@ Expected<std::unique_ptr<Module>> loadPartition(const EmitOptions &Options,
return createStringError(inconvertibleErrorCode(), Stream.str());
}
if (!Options.SourcePath.empty())
Mod->setSourceFileName(Options.SourcePath);
Mod->setSourceFileName(moduleSourceName(Options.SourcePath));
Mod->setTargetTriple(Triple(Options.Target));
Mod->setDataLayout(Machine.createDataLayout());
if (std::optional<EmitFailure> Failure = verify(*Mod, "verification_failed"))
@@ -1555,7 +1564,7 @@ int emit(const EmitOptions &Options) {
return reportError("invalid_ir", Stream.str(), Options.DiagnosticFormat);
}
if (!Options.SourcePath.empty())
Mod->setSourceFileName(Options.SourcePath);
Mod->setSourceFileName(moduleSourceName(Options.SourcePath));
std::string LookupError;
std::unique_ptr<TargetMachine> Machine =
@@ -352,6 +352,16 @@ describe.runIf(helper !== null)("LLVM profile-guided optimization", () => {
});
describe.runIf(profileLinker !== null)("with the profile runtime", () => {
test("profiles name local functions after the entry's file name, not its directory", async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-profile-names-"));
dirs.push(dir);
const { profile } = await collect(dir, program(), 1, "names");
const { stdout } = await execFileAsync(helper!, ["profile-functions", "--input", profile]);
const { functions } = JSON.parse(stdout) as { functions: string[] };
expect(functions.filter((name) => name.startsWith("program.ts;")).length).toBeGreaterThan(0);
expect(functions.filter((name) => name.includes("/"))).toEqual([]);
});
for (const outputs of [1, 4]) {
test(`${outputs === 1 ? "one module" : "partitions"}: the merged profile guides emission`, async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-profile-use-"));