Add IRPGO and CSIRPGO options to Swift (#84335)

This PR introduces three new instrumentation flags and plumbs them
through to IRGen:

1. `-ir-profile-generate` - enable IR-level instrumentation.
2. `-cs-profile-generate` - enable context-sensitive IR-level
instrumentation.
3. `-ir-profile-use` - IR-level PGO input profdata file to enable
profile-guided optimization (both IRPGO and CSIRPGO)

**Context:**
https://forums.swift.org/t/ir-level-pgo-instrumentation-in-swift/82123

**Swift-driver PR:** https://github.com/swiftlang/swift-driver/pull/1992

**Tests and validation:**
This PR includes ir level verification tests, also checks few edge-cases
when `-ir-profile-use` supplied profile is either missing or is an
invalid IR profile.

However, for argument validation, linking, and generating IR profiles
that can later be consumed by -cs-profile-generate, I’ll need
corresponding swift-driver changes. Those changes are being tracked in
https://github.com/swiftlang/swift-driver/pull/1992
This commit is contained in:
Chirag Ramani
2025-10-09 17:41:47 -07:00
committed by GitHub
parent 8458226347
commit 5179bc9609
15 changed files with 409 additions and 11 deletions

View File

@@ -176,19 +176,92 @@ static void validateWarningControlArgs(DiagnosticEngine &diags,
}
}
/// Validates only *generate* profiling flags and their mutual conflicts.
static void validateProfilingGenerateArgs(DiagnosticEngine &diags,
const ArgList &args) {
const Arg *ProfileGenerate = args.getLastArg(options::OPT_profile_generate);
const Arg *IRProfileGenerate =
args.getLastArg(options::OPT_ir_profile_generate);
const Arg *CSProfileGenerate =
args.getLastArg(options::OPT_cs_profile_generate);
const Arg *CSProfileGenerateEQ =
args.getLastArg(options::OPT_cs_profile_generate_EQ);
const Arg *IRProfileGenerateEQ =
args.getLastArg(options::OPT_ir_profile_generate_EQ);
// If both CS Profile forms were specified, report a clear conflict.
if (CSProfileGenerate && CSProfileGenerateEQ) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-cs-profile-generate", "-cs-profile-generate=");
CSProfileGenerateEQ = nullptr;
}
// If both IR Profile forms were specified, report a clear conflict.
if (IRProfileGenerate && IRProfileGenerateEQ) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-ir-profile-generate", "-ir-profile-generate=");
IRProfileGenerateEQ = nullptr;
}
llvm::SmallVector<std::pair<const Arg *, const char *>, 3> gens;
if (ProfileGenerate)
gens.push_back({ProfileGenerate, "-profile-generate"});
if (IRProfileGenerate)
gens.push_back({IRProfileGenerate, "-ir-profile-generate"});
if (IRProfileGenerateEQ)
gens.push_back({IRProfileGenerateEQ, "-ir-profile-generate="});
if (CSProfileGenerate)
gens.push_back({CSProfileGenerate, "-cs-profile-generate"});
else if (CSProfileGenerateEQ)
gens.push_back({CSProfileGenerateEQ, "-cs-profile-generate="});
// Emit pairwise conflicts if more than one generate-mode was selected
for (size_t i = 0; i + 1 < gens.size(); ++i) {
for (size_t j = i + 1; j < gens.size(); ++j) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
gens[i].second, gens[j].second);
}
}
}
static void validateProfilingArgs(DiagnosticEngine &diags,
const ArgList &args) {
validateProfilingGenerateArgs(diags, args);
const Arg *ProfileGenerate = args.getLastArg(options::OPT_profile_generate);
const Arg *ProfileUse = args.getLastArg(options::OPT_profile_use);
const Arg *IRProfileGenerate =
args.getLastArg(options::OPT_ir_profile_generate);
const Arg *IRProfileGenerateEQ =
args.getLastArg(options::OPT_ir_profile_generate_EQ);
const Arg *IRProfileUse = args.getLastArg(options::OPT_ir_profile_use);
if (ProfileGenerate && ProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-profile-generate", "-profile-use");
}
if (ProfileGenerate && IRProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-profile-generate", "-ir-profile-use");
}
if (IRProfileGenerate && ProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-ir-profile-generate", "-profile-use");
}
if (IRProfileGenerate && IRProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-ir-profile-generate", "-ir-profile-use");
}
if (IRProfileGenerateEQ && ProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-ir-profile-generate=", "-profile-use");
}
if (IRProfileGenerateEQ && IRProfileUse) {
diags.diagnose(SourceLoc(), diag::error_conflicting_options,
"-ir-profile-generate=", "-ir-profile-use");
}
// Check if the profdata is missing
if (ProfileUse && !llvm::sys::fs::exists(ProfileUse->getValue())) {
diags.diagnose(SourceLoc(), diag::error_profile_missing,
ProfileUse->getValue());
for (const Arg *use : {ProfileUse, IRProfileUse}) {
if (use && !llvm::sys::fs::exists(use->getValue())) {
diags.diagnose(SourceLoc(), diag::error_profile_missing, use->getValue());
}
}
}