diff --git a/docs/content/docs/cli.mdx b/docs/content/docs/cli.mdx index f2d91f1bd..3c6340bdb 100644 --- a/docs/content/docs/cli.mdx +++ b/docs/content/docs/cli.mdx @@ -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: diff --git a/native/llvm-codegen/src/emit.cpp b/native/llvm-codegen/src/emit.cpp index b7e0014a5..3df2e2561 100644 --- a/native/llvm-codegen/src/emit.cpp +++ b/native/llvm-codegen/src/emit.cpp @@ -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 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 +// (";"), 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> 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 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 Machine = diff --git a/packages/compiler/test/native-profile.test.ts b/packages/compiler/test/native-profile.test.ts index a6bdfe241..ffeab1b77 100644 --- a/packages/compiler/test/native-profile.test.ts +++ b/packages/compiler/test/native-profile.test.ts @@ -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-"));