From 08bc2e18b91e4a46d2a7bca5d4ea26e197e98e89 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Fri, 12 Sep 2025 16:53:19 -0700 Subject: [PATCH 1/9] Add IRPGO and CSIRPGO options to Swift Updated config --- include/swift/AST/IRGenOptions.h | 10 +++++++ include/swift/Option/Options.td | 13 ++++++++ lib/Driver/DarwinToolChains.cpp | 3 +- lib/Driver/Driver.cpp | 44 ++++++++++++++++++++++++++++ lib/Driver/ToolChains.cpp | 11 +++++++ lib/Driver/ToolChains.h | 5 ++++ lib/Driver/UnixToolChains.cpp | 3 +- lib/Driver/WebAssemblyToolChains.cpp | 3 +- lib/Driver/WindowsToolChains.cpp | 5 ++-- lib/Frontend/CompilerInvocation.cpp | 6 ++++ lib/IRGen/IRGen.cpp | 31 ++++++++++++++++++++ 11 files changed, 129 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index baa4709dc05e1..20cdb4a4a224e 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -552,6 +552,16 @@ class IRGenOptions { /// or the empty string. std::string UseSampleProfile = ""; + /// Path to the context-sensitive profile file to be used for CS-IR PGO, + /// or the default string. + std::string CSProfileGenFile = "default_%m.profraw"; + + /// Whether to enable context-sensitive IR PGO generation. + bool EnableCSIRProfileGen = false; + + /// Whether to enable IR level instrumentation. + bool EnableIRProfileGen = false; + /// Controls whether DWARF discriminators are added to the IR. unsigned DebugInfoForProfiling : 1; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index ff0d0abf1a279..eb95f71070281 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1608,6 +1608,19 @@ def profile_sample_use : Joined<["-"], "profile-sample-use=">, MetaVarName<"">, HelpText<"Supply sampling-based profiling data from llvm-profdata to enable profile-guided optimization">; +def cs_profile_generate : Flag<["-"], "cs-profile-generate">, + Flags<[FrontendOption, NoInteractiveOption]>, + HelpText<"Generate instrumented code to collect context sensitive execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">; + +def cs_profile_generate_EQ : Joined<["-"], "cs-profile-generate=">, + Flags<[FrontendOption, NoInteractiveOption]>, + MetaVarName<"">, + HelpText<"Generate instrumented code to collect context sensitive execution counts into /default.profraw (overridden by LLVM_PROFILE_FILE env var)">; + +def ir_profile_generate: Flag<["-"], "ir-profile-generate">, + Flags<[FrontendOption, NoInteractiveOption]>, + HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">; + def embed_bitcode : Flag<["-"], "embed-bitcode">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Embed LLVM IR bitcode as data">; diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index 1ca6696397c3f..9d2050e0a9464 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -42,6 +42,7 @@ using namespace swift; using namespace swift::driver; using namespace llvm::opt; +using namespace swift::driver::toolchains; std::string toolchains::Darwin::findProgramRelativeToSwiftImpl(StringRef name) const { @@ -470,7 +471,7 @@ void toolchains::Darwin::addProfileGenerationArgs(ArgStringList &Arguments, const JobContext &context) const { const llvm::Triple &Triple = getTriple(); - if (context.Args.hasArg(options::OPT_profile_generate)) { + if (needsInstrProfileRuntime(context.Args)) { SmallString<128> LibProfile; getClangLibraryPath(context.Args, LibProfile); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 6c42ca0c8a64c..d435920e4c556 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -175,14 +175,58 @@ 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); + + // 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; + } + + llvm::SmallVector, 3> gens; + if (ProfileGenerate) + gens.push_back({ProfileGenerate, "-profile-generate"}); + if (IRProfileGenerate) + gens.push_back({IRProfileGenerate, "-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); if (ProfileGenerate && ProfileUse) { diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-profile-generate", "-profile-use"); } + if (IRProfileGenerate && ProfileUse) { + diags.diagnose(SourceLoc(), diag::error_conflicting_options, + "-ir-profile-generate", "-profile-use"); + } // Check if the profdata is missing if (ProfileUse && !llvm::sys::fs::exists(ProfileUse->getValue())) { diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 8011dd7d02b62..eabaa0a0a546e 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -160,6 +160,15 @@ bool containsValue( } +namespace swift::driver::toolchains { +bool needsInstrProfileRuntime(const llvm::opt::ArgList &Args) { + return Args.hasArg(options::OPT_profile_generate) || + Args.hasArg(options::OPT_cs_profile_generate) || + Args.hasArg(options::OPT_cs_profile_generate_EQ) || + Args.hasArg(options::OPT_ir_profile_generate); +} +} // namespace swift::driver::toolchains + void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, const CommandOutput &output, const ArgList &inputArgs, @@ -323,6 +332,8 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_PackageCMO); inputArgs.AddLastArg(arguments, options::OPT_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_profile_use); + inputArgs.AddLastArg(arguments, options::OPT_ir_profile_generate); + inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping); inputArgs.AddAllArgs(arguments, options::OPT_warning_treating_Group); inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ); diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index 52adec85b49de..018913ab8d6ea 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -25,6 +25,11 @@ class DiagnosticEngine; namespace driver { namespace toolchains { +/// True if any *generation* mode of instrumentation-based profile is enabled. +/// +/// This is used to determine if the profiler runtime should be linked. +bool needsInstrProfileRuntime(const llvm::opt::ArgList &Args); + class LLVM_LIBRARY_VISIBILITY Darwin : public ToolChain { protected: diff --git a/lib/Driver/UnixToolChains.cpp b/lib/Driver/UnixToolChains.cpp index 12b0a895812ec..3f328e2a26754 100644 --- a/lib/Driver/UnixToolChains.cpp +++ b/lib/Driver/UnixToolChains.cpp @@ -40,6 +40,7 @@ using namespace swift; using namespace swift::driver; using namespace llvm::opt; +using namespace swift::driver::toolchains; std::string toolchains::GenericUnix::sanitizerRuntimeLibName(StringRef Sanitizer, @@ -330,7 +331,7 @@ toolchains::GenericUnix::constructInvocation(const DynamicLinkJobAction &job, } } - if (context.Args.hasArg(options::OPT_profile_generate)) { + if (needsInstrProfileRuntime(context.Args)) { SmallString<128> LibProfile(SharedResourceDirPath); llvm::sys::path::remove_filename(LibProfile); // remove platform name llvm::sys::path::append(LibProfile, "clang", "lib"); diff --git a/lib/Driver/WebAssemblyToolChains.cpp b/lib/Driver/WebAssemblyToolChains.cpp index eb257f1ddbffe..ddc48511780a1 100644 --- a/lib/Driver/WebAssemblyToolChains.cpp +++ b/lib/Driver/WebAssemblyToolChains.cpp @@ -38,6 +38,7 @@ using namespace swift; using namespace swift::driver; using namespace llvm::opt; +using namespace swift::driver::toolchains; std::string toolchains::WebAssembly::sanitizerRuntimeLibName(StringRef Sanitizer, @@ -168,7 +169,7 @@ toolchains::WebAssembly::constructInvocation(const DynamicLinkJobAction &job, "-fsanitize=" + getSanitizerList(context.OI.SelectedSanitizers))); } - if (context.Args.hasArg(options::OPT_profile_generate)) { + if (needsInstrProfileRuntime(context.Args)) { SmallString<128> LibProfile(SharedResourceDirPath); llvm::sys::path::remove_filename(LibProfile); // remove platform name llvm::sys::path::append(LibProfile, "clang", "lib"); diff --git a/lib/Driver/WindowsToolChains.cpp b/lib/Driver/WindowsToolChains.cpp index d50f4a0dfcf8f..a3c58e8e444f9 100644 --- a/lib/Driver/WindowsToolChains.cpp +++ b/lib/Driver/WindowsToolChains.cpp @@ -36,6 +36,7 @@ using namespace swift; using namespace swift::driver; using namespace llvm::opt; +using namespace swift::driver::toolchains; std::string toolchains::Windows::sanitizerRuntimeLibName(StringRef Sanitizer, bool shared) const { @@ -89,7 +90,7 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job, // for now, which supports the behavior via a flag. // TODO: Once we've changed coverage to no longer rely on emitting // duplicate weak symbols (rdar://131295678), we can remove this. - if (context.Args.getLastArg(options::OPT_profile_generate)) { + if (swift::driver::toolchains::needsInstrProfileRuntime(context.Args)) { return true; } return false; @@ -186,7 +187,7 @@ toolchains::Windows::constructInvocation(const DynamicLinkJobAction &job, sanitizerRuntimeLibName("ubsan")); } - if (context.Args.hasArg(options::OPT_profile_generate)) { + if (needsInstrProfileRuntime(context.Args)) { Arguments.push_back(context.Args.MakeArgString("-Xlinker")); Arguments.push_back(context.Args.MakeArgString( Twine({"-include:", llvm::getInstrProfRuntimeHookVarName()}))); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 8b8f4b419cba3..23a17f353f4b8 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -3497,6 +3497,12 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, const Arg *ProfileSampleUse = Args.getLastArg(OPT_profile_sample_use); Opts.UseSampleProfile = ProfileSampleUse ? ProfileSampleUse->getValue() : ""; + Opts.EnableIRProfileGen = Args.hasArg(OPT_ir_profile_generate); + Opts.EnableCSIRProfileGen = Args.hasArg(OPT_cs_profile_generate) || + Args.hasArg(OPT_cs_profile_generate_EQ); + if (auto V = Args.getLastArgValue(OPT_cs_profile_generate_EQ); !V.empty()) + Opts.CSProfileGenFile = V.str(); + Opts.DebugInfoForProfiling |= Args.hasArg(OPT_debug_info_for_profiling); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 659d936641064..fb6ea8856d8d0 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -231,6 +231,37 @@ static void populatePGOOptions(std::optional &Out, return; } + if (Opts.EnableCSIRProfileGen) { + const bool hasUse = !Opts.UseProfile.empty(); + Out = PGOOptions( + /*ProfileFile=*/ hasUse ? Opts.UseProfile : "", + /*CSProfileGenFile=*/ Opts.CSProfileGenFile, + /*ProfileRemappingFile=*/ "", + /*MemoryProfile=*/ "", + /*FS=*/ llvm::vfs::getRealFileSystem(), + /*Action=*/ hasUse ? PGOOptions::IRUse : PGOOptions::NoAction, + /*CSPGOAction=*/ PGOOptions::CSIRInstr, + /*ColdType=*/ PGOOptions::ColdFuncOpt::Default, + /*DebugInfoForProfiling=*/ Opts.DebugInfoForProfiling + ); + return; + } + + if (Opts.EnableIRProfileGen) { + Out = PGOOptions( + /*ProfileFile=*/ "", + /*CSProfileGenFile=*/ "", + /*ProfileRemappingFile=*/ "", + /*MemoryProfile=*/ "", + /*FS=*/ llvm::vfs::getRealFileSystem(), + /*Action=*/ PGOOptions::IRInstr, + /*CSPGOAction=*/ PGOOptions::NoCSAction, + /*ColdType=*/ PGOOptions::ColdFuncOpt::Default, + /*DebugInfoForProfiling=*/ Opts.DebugInfoForProfiling + ); + return; + } + if (Opts.DebugInfoForProfiling) { Out = PGOOptions( /*ProfileFile=*/ "", From fb55618ccbb311e7969de240e435080107b274a8 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Wed, 17 Sep 2025 17:26:15 -0700 Subject: [PATCH 2/9] Address suggestions Added tests --- include/swift/AST/IRGenOptions.h | 6 +-- lib/Driver/ToolChains.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 2 +- lib/IRGen/IRGen.cpp | 6 +-- test/IRGen/cs_profile_generate.sil | 65 +++++++++++++++++++++++++++++ test/IRGen/ir_profile_generate.sil | 65 +++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 test/IRGen/cs_profile_generate.sil create mode 100644 test/IRGen/ir_profile_generate.sil diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 20cdb4a4a224e..4857cea0069ea 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -552,9 +552,9 @@ class IRGenOptions { /// or the empty string. std::string UseSampleProfile = ""; - /// Path to the context-sensitive profile file to be used for CS-IR PGO, - /// or the default string. - std::string CSProfileGenFile = "default_%m.profraw"; + /// Name of the profile file to use as output for -ir-profile-generate, + /// and -cs-profile-generate, or the default string. + std::string InstrProfileOutput = "default_%m.profraw"; /// Whether to enable context-sensitive IR PGO generation. bool EnableCSIRProfileGen = false; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index eabaa0a0a546e..314b8ffba1296 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -334,6 +334,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_profile_use); inputArgs.AddLastArg(arguments, options::OPT_ir_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate); + inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate_EQ); inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping); inputArgs.AddAllArgs(arguments, options::OPT_warning_treating_Group); inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 23a17f353f4b8..0bbcab2eacfc5 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -3501,7 +3501,7 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Opts.EnableCSIRProfileGen = Args.hasArg(OPT_cs_profile_generate) || Args.hasArg(OPT_cs_profile_generate_EQ); if (auto V = Args.getLastArgValue(OPT_cs_profile_generate_EQ); !V.empty()) - Opts.CSProfileGenFile = V.str(); + Opts.InstrProfileOutput = V.str(); Opts.DebugInfoForProfiling |= Args.hasArg(OPT_debug_info_for_profiling); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index fb6ea8856d8d0..51f4c05e5c230 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -234,8 +234,8 @@ static void populatePGOOptions(std::optional &Out, if (Opts.EnableCSIRProfileGen) { const bool hasUse = !Opts.UseProfile.empty(); Out = PGOOptions( - /*ProfileFile=*/ hasUse ? Opts.UseProfile : "", - /*CSProfileGenFile=*/ Opts.CSProfileGenFile, + /*ProfileFile=*/ Opts.UseProfile, + /*CSProfileGenFile=*/ Opts.InstrProfileOutput, /*ProfileRemappingFile=*/ "", /*MemoryProfile=*/ "", /*FS=*/ llvm::vfs::getRealFileSystem(), @@ -249,7 +249,7 @@ static void populatePGOOptions(std::optional &Out, if (Opts.EnableIRProfileGen) { Out = PGOOptions( - /*ProfileFile=*/ "", + /*ProfileFile=*/ Opts.InstrProfileOutput, /*CSProfileGenFile=*/ "", /*ProfileRemappingFile=*/ "", /*MemoryProfile=*/ "", diff --git a/test/IRGen/cs_profile_generate.sil b/test/IRGen/cs_profile_generate.sil new file mode 100644 index 0000000000000..11c72cdc78c8d --- /dev/null +++ b/test/IRGen/cs_profile_generate.sil @@ -0,0 +1,65 @@ +// RUN: %target-swift-frontend -cs-profile-generate -O -emit-ir %s | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +sil @b : $@convention(thin) () -> () + +sil @c : $@convention(thin) () -> () + + +// CHECK: @__llvm_profile_runtime = external hidden global + +// counter array (2 counters for the then/else) +// CHECK: @__profc_a = {{.*}} global [2 x i64] +// data record pointing at the counter array and the function +// CHECK: @__profd_a = {{.*}} global {{.*}} ptr @a.local + + +// CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] +// THEN: increment counter[0] and call b() +// CHECK: [[THEN]]: +// CHECK: %[[L0:.*]] = load i64, ptr @__profc_a, align 8 +// CHECK: %[[A0:.*]] = add i64 %[[L0]], 1 +// CHECK: store i64 %[[A0]], ptr @__profc_a, align 8 +// CHECK: call swiftcc void @b() +// CHECK: br label %[[MERGE:[0-9]+]] + +// ELSE: increment counter[1] and call c() +// CHECK: [[ELSE]]: +// CHECK: %[[L1:.*]] = load i64, ptr getelementptr inbounds (i8, ptr @__profc_a, i64 8), align 8 +// CHECK: %[[A1:.*]] = add i64 %[[L1]], 1 +// CHECK: store i64 %[[A1]], ptr getelementptr inbounds (i8, ptr @__profc_a, i64 8), align 8 +// CHECK: call swiftcc void @c() +// CHECK: br label %[[MERGE]] + +// CHECK: [[MERGE]]: +// CHECK: ret void + +// CHECK: declare swiftcc void @c() +// CHECK: declare swiftcc void @b() + +sil @a : $@convention(thin) (Bool) -> () { +bb0(%0 : $Bool): + %2 = struct_extract %0, #Bool._value + cond_br %2, bb1, bb2 + +bb1: + // function_ref b() + %4 = function_ref @b : $@convention(thin) () -> () + %5 = apply %4() : $@convention(thin) () -> () + br bb3 + +bb2: + // function_ref c() + %7 = function_ref @c: $@convention(thin) () -> () + %8 = apply %7() : $@convention(thin) () -> () + br bb3 + +bb3: + %10 = tuple () + return %10 +} diff --git a/test/IRGen/ir_profile_generate.sil b/test/IRGen/ir_profile_generate.sil new file mode 100644 index 0000000000000..1120f380c5276 --- /dev/null +++ b/test/IRGen/ir_profile_generate.sil @@ -0,0 +1,65 @@ +// RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +sil @b : $@convention(thin) () -> () + +sil @c : $@convention(thin) () -> () + +// CHECK: @__llvm_profile_runtime = external hidden global + +// counter array (2 counters for the then/else) +// CHECK: @__profc_a = {{.*}} global [2 x i64] +// data record pointing at the counter array and the function +// CHECK: @__profd_a = {{.*}} global {{.*}} ptr @a.local + +// CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] + +// THEN: increment counter[0] and call b() +// CHECK: [[THEN]]: +// CHECK: %[[L0:.*]] = load i64, ptr @__profc_a, align 8 +// CHECK: %[[A0:.*]] = add i64 %[[L0]], 1 +// CHECK: store i64 %[[A0]], ptr @__profc_a, align 8 +// CHECK: call swiftcc void @b() +// CHECK: br label %[[MERGE:[0-9]+]] + +// ELSE: increment counter[1] and call c() +// CHECK: [[ELSE]]: +// CHECK: %[[L1:.*]] = load i64, ptr getelementptr inbounds ([2 x i64], ptr @__profc_a, i32 0, i32 1), align 8 +// CHECK: %[[A1:.*]] = add i64 %[[L1]], 1 +// CHECK: store i64 %[[A1]], ptr getelementptr inbounds ([2 x i64], ptr @__profc_a, i32 0, i32 1), align 8 +// CHECK: call swiftcc void @c() +// CHECK: br label %[[MERGE]] + +// CHECK: [[MERGE]]: +// CHECK: ret void + +// CHECK: declare swiftcc void @c() +// CHECK: declare swiftcc void @b() +// CHECK: declare void @llvm.instrprof.increment + +sil @a : $@convention(thin) (Bool) -> () { +bb0(%0 : $Bool): + %2 = struct_extract %0, #Bool._value + cond_br %2, bb1, bb2 + +bb1: + // function_ref b() + %4 = function_ref @b : $@convention(thin) () -> () + %5 = apply %4() : $@convention(thin) () -> () + br bb3 + +bb2: + // function_ref c() + %7 = function_ref @c: $@convention(thin) () -> () + %8 = apply %7() : $@convention(thin) () -> () + br bb3 + +bb3: + %10 = tuple () + return %10 +} From 7a01d0cdeb26e9dcaa0db2fd0204e77da6cd70cb Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Wed, 17 Sep 2025 20:35:34 -0700 Subject: [PATCH 3/9] Adds ir_profile_use Option dedicated for IR level PGO uses --- include/swift/AST/IRGenOptions.h | 3 +++ include/swift/Option/Options.td | 5 +++++ lib/Driver/Driver.cpp | 17 +++++++++++++---- lib/Driver/ToolChains.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 2 ++ lib/IRGen/IRGen.cpp | 4 ++-- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 4857cea0069ea..d156c13bd5502 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -548,6 +548,9 @@ class IRGenOptions { /// Path to the profdata file to be used for PGO, or the empty string. std::string UseProfile = ""; + /// Path to the profdata file to be used for IR/CS-IR PGO, or the empty string. + std::string UseIRProfile = ""; + /// Path to the data file to be used for sampling-based PGO, /// or the empty string. std::string UseSampleProfile = ""; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index eb95f71070281..2470cb7b5ed4c 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1621,6 +1621,11 @@ def ir_profile_generate: Flag<["-"], "ir-profile-generate">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">; +def ir_profile_use : CommaJoined<["-"], "ir-profile-use=">, + Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath]>, + MetaVarName<"">, + HelpText<"Supply an IR-level PGO profdata file to enable profile-guided optimization">; + def embed_bitcode : Flag<["-"], "embed-bitcode">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Embed LLVM IR bitcode as data">; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index d435920e4c556..1b196f617b420 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -219,19 +219,28 @@ static void validateProfilingArgs(DiagnosticEngine &diags, const Arg *ProfileUse = args.getLastArg(options::OPT_profile_use); const Arg *IRProfileGenerate = args.getLastArg(options::OPT_ir_profile_generate); + 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"); + } // 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()); + } } } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 314b8ffba1296..cb07383f54fc9 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -333,6 +333,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_profile_use); inputArgs.AddLastArg(arguments, options::OPT_ir_profile_generate); + inputArgs.AddLastArg(arguments, options::OPT_ir_profile_use); inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate_EQ); inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 0bbcab2eacfc5..d0e41d5f59b01 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -3502,6 +3502,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, Args.hasArg(OPT_cs_profile_generate_EQ); if (auto V = Args.getLastArgValue(OPT_cs_profile_generate_EQ); !V.empty()) Opts.InstrProfileOutput = V.str(); + const Arg *IRProfileUse = Args.getLastArg(OPT_ir_profile_use); + Opts.UseIRProfile = IRProfileUse ? IRProfileUse->getValue() : ""; Opts.DebugInfoForProfiling |= Args.hasArg(OPT_debug_info_for_profiling); diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index 51f4c05e5c230..ae0ac4ac096ae 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -232,9 +232,9 @@ static void populatePGOOptions(std::optional &Out, } if (Opts.EnableCSIRProfileGen) { - const bool hasUse = !Opts.UseProfile.empty(); + const bool hasUse = !Opts.UseIRProfile.empty(); Out = PGOOptions( - /*ProfileFile=*/ Opts.UseProfile, + /*ProfileFile=*/ Opts.UseIRProfile, /*CSProfileGenFile=*/ Opts.InstrProfileOutput, /*ProfileRemappingFile=*/ "", /*MemoryProfile=*/ "", From 83f4c14e2342fcf126a0a98ae53697d05d08e6b1 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Wed, 17 Sep 2025 21:53:35 -0700 Subject: [PATCH 4/9] Add supporting for remaining UseIRProfile cases --- include/swift/AST/DiagnosticsIRGen.def | 5 ++ lib/IRGen/IRGen.cpp | 97 ++++++++++++++++++++------ 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/include/swift/AST/DiagnosticsIRGen.def b/include/swift/AST/DiagnosticsIRGen.def index 4f93def59a63f..81b8fb9a0726d 100644 --- a/include/swift/AST/DiagnosticsIRGen.def +++ b/include/swift/AST/DiagnosticsIRGen.def @@ -41,6 +41,11 @@ ERROR(too_few_output_filenames,none, ERROR(no_input_files_for_mt,none, "no swift input files for multi-threaded compilation", ()) +ERROR(ir_profile_read_failed, none, + "error reading profile '%0': %1", (StringRef, StringRef)) +ERROR(ir_profile_invalid, none, + "invalid ir profile '%0'", (StringRef)) + ERROR(alignment_dynamic_type_layout_unsupported,none, "'@_alignment' is not supported on types with dynamic layout", ()) ERROR(alignment_less_than_natural,none, diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index ae0ac4ac096ae..7e49598eaac5c 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -72,6 +72,7 @@ #include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassPlugin.h" #include "llvm/Passes/StandardInstrumentations.h" +#include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Remarks/Remark.h" #include "llvm/Remarks/RemarkStreamer.h" #include "llvm/Support/CommandLine.h" @@ -214,8 +215,57 @@ static void align(llvm::Module *Module) { } } +static std::unique_ptr +getProfileReader(const Twine &ProfileName, llvm::vfs::FileSystem &FS, + DiagnosticEngine &Diags) { + auto ReaderOrErr = llvm::IndexedInstrProfReader::create(ProfileName, FS); + if (auto E = ReaderOrErr.takeError()) { + llvm::handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EI) { + Diags.diagnose(SourceLoc(), diag::ir_profile_read_failed, + ProfileName.str(), EI.message()); + }); + return nullptr; + } + return std::move(*ReaderOrErr); +} + +static std::optional buildIRUseOptions(const IRGenOptions &Opts, + DiagnosticEngine &Diags) { + if (Opts.UseIRProfile.empty()) + return std::nullopt; + + auto FS = llvm::vfs::getRealFileSystem(); + std::unique_ptr Reader = + getProfileReader(Opts.UseIRProfile.c_str(), *FS, Diags); + if (!Reader) + return std::nullopt; + + // Currently memprof profiles are only added at the IR level. Mark the + // profile type as IR in that case as well and the subsequent matching + // needs to detect which is available (might be one or both). + const bool IsIR = Reader->isIRLevelProfile() || Reader->hasMemoryProfile(); + if (!IsIR) { + Diags.diagnose(SourceLoc(), diag::ir_profile_invalid, + Opts.UseIRProfile.c_str()); + return std::nullopt; + } + + const bool IsCS = Reader->hasCSIRLevelProfile(); + return PGOOptions( + /*ProfileFile=*/Opts.UseIRProfile, + /*CSProfileGenFile=*/"", + /*ProfileRemappingFile=*/"", + /*MemoryProfile=*/"", + /*FS=*/FS, + /*Action=*/PGOOptions::IRUse, + /*CSPGOAction=*/IsCS ? PGOOptions::CSIRUse : PGOOptions::NoCSAction, + /*ColdType=*/PGOOptions::ColdFuncOpt::Default, + /*DebugInfoForProfiling=*/Opts.DebugInfoForProfiling); +} + static void populatePGOOptions(std::optional &Out, - const IRGenOptions &Opts) { + const IRGenOptions &Opts, + DiagnosticEngine &Diags) { if (!Opts.UseSampleProfile.empty()) { Out = PGOOptions( /*ProfileFile=*/ Opts.UseSampleProfile, @@ -234,31 +284,34 @@ static void populatePGOOptions(std::optional &Out, if (Opts.EnableCSIRProfileGen) { const bool hasUse = !Opts.UseIRProfile.empty(); Out = PGOOptions( - /*ProfileFile=*/ Opts.UseIRProfile, - /*CSProfileGenFile=*/ Opts.InstrProfileOutput, - /*ProfileRemappingFile=*/ "", - /*MemoryProfile=*/ "", - /*FS=*/ llvm::vfs::getRealFileSystem(), - /*Action=*/ hasUse ? PGOOptions::IRUse : PGOOptions::NoAction, - /*CSPGOAction=*/ PGOOptions::CSIRInstr, - /*ColdType=*/ PGOOptions::ColdFuncOpt::Default, - /*DebugInfoForProfiling=*/ Opts.DebugInfoForProfiling - ); + /*ProfileFile=*/Opts.UseIRProfile, + /*CSProfileGenFile=*/Opts.InstrProfileOutput, + /*ProfileRemappingFile=*/"", + /*MemoryProfile=*/"", + /*FS=*/llvm::vfs::getRealFileSystem(), + /*Action=*/hasUse ? PGOOptions::IRUse : PGOOptions::NoAction, + /*CSPGOAction=*/PGOOptions::CSIRInstr, + /*ColdType=*/PGOOptions::ColdFuncOpt::Default, + /*DebugInfoForProfiling=*/Opts.DebugInfoForProfiling); return; } if (Opts.EnableIRProfileGen) { Out = PGOOptions( - /*ProfileFile=*/ Opts.InstrProfileOutput, - /*CSProfileGenFile=*/ "", - /*ProfileRemappingFile=*/ "", - /*MemoryProfile=*/ "", - /*FS=*/ llvm::vfs::getRealFileSystem(), - /*Action=*/ PGOOptions::IRInstr, - /*CSPGOAction=*/ PGOOptions::NoCSAction, - /*ColdType=*/ PGOOptions::ColdFuncOpt::Default, - /*DebugInfoForProfiling=*/ Opts.DebugInfoForProfiling - ); + /*ProfileFile=*/Opts.InstrProfileOutput, + /*CSProfileGenFile=*/"", + /*ProfileRemappingFile=*/"", + /*MemoryProfile=*/"", + /*FS=*/llvm::vfs::getRealFileSystem(), + /*Action=*/PGOOptions::IRInstr, + /*CSPGOAction=*/PGOOptions::NoCSAction, + /*ColdType=*/PGOOptions::ColdFuncOpt::Default, + /*DebugInfoForProfiling=*/Opts.DebugInfoForProfiling); + return; + } + + if (auto IRUseOptions = buildIRUseOptions(Opts, Diags)) { + Out = *IRUseOptions; return; } @@ -297,7 +350,7 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts, llvm::TargetMachine *TargetMachine, llvm::raw_pwrite_stream *out) { std::optional PGOOpt; - populatePGOOptions(PGOOpt, Opts); + populatePGOOptions(PGOOpt, Opts, Diags); PipelineTuningOptions PTO; From 108ca288ff4e197deb5361f2a58c8ee59f8ec281 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Thu, 18 Sep 2025 11:36:55 -0700 Subject: [PATCH 5/9] Add tests for -ir-profile-use --- test/IRGen/cs_profile_generate.sil | 7 ++++++- test/IRGen/ir_profile_use.sil | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/IRGen/ir_profile_use.sil diff --git a/test/IRGen/cs_profile_generate.sil b/test/IRGen/cs_profile_generate.sil index 11c72cdc78c8d..f42893b8676ff 100644 --- a/test/IRGen/cs_profile_generate.sil +++ b/test/IRGen/cs_profile_generate.sil @@ -1,4 +1,9 @@ -// RUN: %target-swift-frontend -cs-profile-generate -O -emit-ir %s | %FileCheck %s +// Without directory +// RUN: %target-swift-frontend -O -cs-profile-generate -emit-ir %s | %FileCheck %s + +// With directory +// RUN: %empty-directory(%t.dir) +// RUN: %target-swift-frontend -O -cs-profile-generate=%t.dir -emit-ir %s | %FileCheck %s sil_stage canonical diff --git a/test/IRGen/ir_profile_use.sil b/test/IRGen/ir_profile_use.sil new file mode 100644 index 0000000000000..1a2739f66871d --- /dev/null +++ b/test/IRGen/ir_profile_use.sil @@ -0,0 +1,23 @@ +// Missing file +// RUN: %target-swift-frontend -ir-profile-use=missing.profdata -emit-ir %s 2>&1 | %FileCheck --check-prefix=CHECK-NOFILE %s +// CHECK-NOFILE: error reading profile +// CHECK-NOFILE: .profdata +// CHECK-NOFILE: No such file or directory + + +// Invalid IR file +// RUN: %empty-directory(%t) +// RUN: echo "" > %t.invalid.profdata +// RUN: %target-swift-frontend -ir-profile-use=%t.invalid.profdata -emit-ir %s 2>&1 | %FileCheck --check-prefix=CHECK-INVALIDFILE %s +// CHECK-INVALIDFILE: error reading profile +// CHECK-INVALIDFILE: invalid.profdata +// CHECK-INVALIDFILE: invalid instrumentation profile data (bad magic) + + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + +public func foo() {} From a96b390205f3e97e8b017105ceb92765bf33ebe4 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Fri, 19 Sep 2025 12:51:55 -0700 Subject: [PATCH 6/9] Address suggestions and relax IR checks --- include/swift/Option/Options.td | 5 +++++ lib/Driver/Driver.cpp | 20 ++++++++++++++++++++ lib/Driver/ToolChains.cpp | 4 +++- lib/Frontend/CompilerInvocation.cpp | 5 ++++- test/IRGen/cs_profile_generate.sil | 17 ++++++++++------- test/IRGen/ir_profile_generate.sil | 25 +++++++++++++++++-------- 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 2470cb7b5ed4c..9837d312b9b67 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -1621,6 +1621,11 @@ def ir_profile_generate: Flag<["-"], "ir-profile-generate">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">; +def ir_profile_generate_EQ : Joined<["-"], "ir-profile-generate=">, + Flags<[FrontendOption, NoInteractiveOption]>, + MetaVarName<"">, + HelpText<"Generate instrumented code to collect execution counts into /default.profraw (overridden by LLVM_PROFILE_FILE env var)">; + def ir_profile_use : CommaJoined<["-"], "ir-profile-use=">, Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath]>, MetaVarName<"">, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 1b196f617b420..ac59c09a3ef7b 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -185,6 +185,8 @@ static void validateProfilingGenerateArgs(DiagnosticEngine &diags, 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) { @@ -192,12 +194,20 @@ static void validateProfilingGenerateArgs(DiagnosticEngine &diags, "-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, 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) @@ -219,6 +229,8 @@ static void validateProfilingArgs(DiagnosticEngine &diags, 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, @@ -236,6 +248,14 @@ static void validateProfilingArgs(DiagnosticEngine &diags, 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 for (const Arg *use : {ProfileUse, IRProfileUse}) { if (use && !llvm::sys::fs::exists(use->getValue())) { diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index cb07383f54fc9..852d4dbd36bad 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -165,7 +165,8 @@ bool needsInstrProfileRuntime(const llvm::opt::ArgList &Args) { return Args.hasArg(options::OPT_profile_generate) || Args.hasArg(options::OPT_cs_profile_generate) || Args.hasArg(options::OPT_cs_profile_generate_EQ) || - Args.hasArg(options::OPT_ir_profile_generate); + Args.hasArg(options::OPT_ir_profile_generate) || + Args.hasArg(options::OPT_ir_profile_generate_EQ); } } // namespace swift::driver::toolchains @@ -333,6 +334,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_profile_use); inputArgs.AddLastArg(arguments, options::OPT_ir_profile_generate); + inputArgs.AddLastArg(arguments, options::OPT_ir_profile_generate_EQ); inputArgs.AddLastArg(arguments, options::OPT_ir_profile_use); inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_cs_profile_generate_EQ); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d0e41d5f59b01..54facd7b3198b 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -3497,7 +3497,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args, const Arg *ProfileSampleUse = Args.getLastArg(OPT_profile_sample_use); Opts.UseSampleProfile = ProfileSampleUse ? ProfileSampleUse->getValue() : ""; - Opts.EnableIRProfileGen = Args.hasArg(OPT_ir_profile_generate); + Opts.EnableIRProfileGen = Args.hasArg(OPT_ir_profile_generate) || + Args.hasArg(OPT_ir_profile_generate_EQ); + if (auto V = Args.getLastArgValue(OPT_ir_profile_generate_EQ); !V.empty()) + Opts.InstrProfileOutput = V.str(); Opts.EnableCSIRProfileGen = Args.hasArg(OPT_cs_profile_generate) || Args.hasArg(OPT_cs_profile_generate_EQ); if (auto V = Args.getLastArgValue(OPT_cs_profile_generate_EQ); !V.empty()) diff --git a/test/IRGen/cs_profile_generate.sil b/test/IRGen/cs_profile_generate.sil index f42893b8676ff..40a09fa4e000e 100644 --- a/test/IRGen/cs_profile_generate.sil +++ b/test/IRGen/cs_profile_generate.sil @@ -5,6 +5,11 @@ // RUN: %empty-directory(%t.dir) // RUN: %target-swift-frontend -O -cs-profile-generate=%t.dir -emit-ir %s | %FileCheck %s +// Ensure Passes: PGOInstrumentationGenPass and InstrProfilingLoweringPass are invoked. +// RUN: %target-swift-frontend -O -cs-profile-generate -emit-ir %s -Xllvm -debug-pass=Structure -Xllvm --time-passes -o /dev/null 2>&1 | %FileCheck -check-prefix=CHECK-PGOGENPASS-INVOKED-INSTR-GEN --check-prefix=CHECK-INSTRPROF %s +// CHECK-PGOGENPASS-INVOKED-INSTR-GEN: PGOInstrumentationGen +// CHECK-INSTRPROF: InstrProfilingLoweringPass + sil_stage canonical import Builtin @@ -19,7 +24,7 @@ sil @c : $@convention(thin) () -> () // CHECK: @__llvm_profile_runtime = external hidden global // counter array (2 counters for the then/else) -// CHECK: @__profc_a = {{.*}} global [2 x i64] +// CHECK: @__profc_a = {{.*}} global {{.*}} // data record pointing at the counter array and the function // CHECK: @__profd_a = {{.*}} global {{.*}} ptr @a.local @@ -27,17 +32,15 @@ sil @c : $@convention(thin) () -> () // CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] // THEN: increment counter[0] and call b() // CHECK: [[THEN]]: -// CHECK: %[[L0:.*]] = load i64, ptr @__profc_a, align 8 -// CHECK: %[[A0:.*]] = add i64 %[[L0]], 1 -// CHECK: store i64 %[[A0]], ptr @__profc_a, align 8 +// CHECK: {{.*}} = load {{.*}}@__profc_a{{.*}} +// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a // CHECK: call swiftcc void @b() // CHECK: br label %[[MERGE:[0-9]+]] // ELSE: increment counter[1] and call c() // CHECK: [[ELSE]]: -// CHECK: %[[L1:.*]] = load i64, ptr getelementptr inbounds (i8, ptr @__profc_a, i64 8), align 8 -// CHECK: %[[A1:.*]] = add i64 %[[L1]], 1 -// CHECK: store i64 %[[A1]], ptr getelementptr inbounds (i8, ptr @__profc_a, i64 8), align 8 +// CHECK: {{.*}} = load {{.*}}, {{.*}} getelementptr{{.*}}@__profc_a{{.*}} +// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a // CHECK: call swiftcc void @c() // CHECK: br label %[[MERGE]] diff --git a/test/IRGen/ir_profile_generate.sil b/test/IRGen/ir_profile_generate.sil index 1120f380c5276..534429c86966f 100644 --- a/test/IRGen/ir_profile_generate.sil +++ b/test/IRGen/ir_profile_generate.sil @@ -1,5 +1,16 @@ +// Without directory // RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s | %FileCheck %s +// With directory +// RUN: %empty-directory(%t.dir) +// RUN: %target-swift-frontend -ir-profile-generate=%t.dir -emit-ir %s | %FileCheck %s + +// Ensure Passes: PGOInstrumentationGenPass and InstrProfilingLoweringPass are invoked. +// RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s -Xllvm -debug-pass=Structure -Xllvm --time-passes -o /dev/null 2>&1 | %FileCheck -check-prefix=CHECK-PGOGENPASS-INVOKED-INSTR-GEN --check-prefix=CHECK-INSTRPROF %s +// CHECK-PGOGENPASS-INVOKED-INSTR-GEN: PGOInstrumentationGen +// CHECK-INSTRPROF: InstrProfilingLoweringPass + + sil_stage canonical import Builtin @@ -13,25 +24,23 @@ sil @c : $@convention(thin) () -> () // CHECK: @__llvm_profile_runtime = external hidden global // counter array (2 counters for the then/else) -// CHECK: @__profc_a = {{.*}} global [2 x i64] +// CHECK: @__profc_a = {{.*}} global {{.*}} // data record pointing at the counter array and the function // CHECK: @__profd_a = {{.*}} global {{.*}} ptr @a.local -// CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] +// CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] // THEN: increment counter[0] and call b() // CHECK: [[THEN]]: -// CHECK: %[[L0:.*]] = load i64, ptr @__profc_a, align 8 -// CHECK: %[[A0:.*]] = add i64 %[[L0]], 1 -// CHECK: store i64 %[[A0]], ptr @__profc_a, align 8 +// CHECK: {{.*}} = load {{.*}}@__profc_a{{.*}} +// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a // CHECK: call swiftcc void @b() // CHECK: br label %[[MERGE:[0-9]+]] // ELSE: increment counter[1] and call c() // CHECK: [[ELSE]]: -// CHECK: %[[L1:.*]] = load i64, ptr getelementptr inbounds ([2 x i64], ptr @__profc_a, i32 0, i32 1), align 8 -// CHECK: %[[A1:.*]] = add i64 %[[L1]], 1 -// CHECK: store i64 %[[A1]], ptr getelementptr inbounds ([2 x i64], ptr @__profc_a, i32 0, i32 1), align 8 +// CHECK: {{.*}} = load {{.*}}, {{.*}} getelementptr{{.*}}@__profc_a{{.*}} +// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a // CHECK: call swiftcc void @c() // CHECK: br label %[[MERGE]] From 01583d98fbad44440ca9bf9a79499dfdf34fd772 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Tue, 30 Sep 2025 11:53:37 -0700 Subject: [PATCH 7/9] Relax IR checks --- test/IRGen/cs_profile_generate.sil | 18 ++++++++---------- test/IRGen/ir_profile_generate.sil | 20 +++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/test/IRGen/cs_profile_generate.sil b/test/IRGen/cs_profile_generate.sil index 40a09fa4e000e..23dd0a37388ea 100644 --- a/test/IRGen/cs_profile_generate.sil +++ b/test/IRGen/cs_profile_generate.sil @@ -21,8 +21,6 @@ sil @b : $@convention(thin) () -> () sil @c : $@convention(thin) () -> () -// CHECK: @__llvm_profile_runtime = external hidden global - // counter array (2 counters for the then/else) // CHECK: @__profc_a = {{.*}} global {{.*}} // data record pointing at the counter array and the function @@ -32,23 +30,23 @@ sil @c : $@convention(thin) () -> () // CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] // THEN: increment counter[0] and call b() // CHECK: [[THEN]]: -// CHECK: {{.*}} = load {{.*}}@__profc_a{{.*}} -// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a -// CHECK: call swiftcc void @b() +// CHECK: {{.*}}load{{.*}}@__profc_a +// CHECK: {{.*}}store{{.*}}@__profc_a +// CHECK: {{.*}}call{{.*}}@b( // CHECK: br label %[[MERGE:[0-9]+]] // ELSE: increment counter[1] and call c() // CHECK: [[ELSE]]: -// CHECK: {{.*}} = load {{.*}}, {{.*}} getelementptr{{.*}}@__profc_a{{.*}} -// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a -// CHECK: call swiftcc void @c() +// CHECK: {{.*}} = {{.*}}@__profc_a{{.*}} +// CHECK: store{{.*}}@__profc_a +// CHECK: {{.*}}call{{.*}}@c( // CHECK: br label %[[MERGE]] // CHECK: [[MERGE]]: // CHECK: ret void -// CHECK: declare swiftcc void @c() -// CHECK: declare swiftcc void @b() +// CHECK: declare {{.*}}@c( +// CHECK: declare {{.*}}@b( sil @a : $@convention(thin) (Bool) -> () { bb0(%0 : $Bool): diff --git a/test/IRGen/ir_profile_generate.sil b/test/IRGen/ir_profile_generate.sil index 534429c86966f..b94788a9f7db0 100644 --- a/test/IRGen/ir_profile_generate.sil +++ b/test/IRGen/ir_profile_generate.sil @@ -21,8 +21,6 @@ sil @b : $@convention(thin) () -> () sil @c : $@convention(thin) () -> () -// CHECK: @__llvm_profile_runtime = external hidden global - // counter array (2 counters for the then/else) // CHECK: @__profc_a = {{.*}} global {{.*}} // data record pointing at the counter array and the function @@ -32,24 +30,24 @@ sil @c : $@convention(thin) () -> () // CHECK: br i1 {{.*}}, label %[[THEN:[0-9]+]], label %[[ELSE:[0-9]+]] // THEN: increment counter[0] and call b() // CHECK: [[THEN]]: -// CHECK: {{.*}} = load {{.*}}@__profc_a{{.*}} -// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a -// CHECK: call swiftcc void @b() +// CHECK: {{.*}}load{{.*}}@__profc_a +// CHECK: {{.*}}store{{.*}}@__profc_a +// CHECK: {{.*}}call{{.*}}@b( // CHECK: br label %[[MERGE:[0-9]+]] // ELSE: increment counter[1] and call c() // CHECK: [[ELSE]]: -// CHECK: {{.*}} = load {{.*}}, {{.*}} getelementptr{{.*}}@__profc_a{{.*}} -// CHECK: store {{.*}} %{{.*}}, ptr @__profc_a -// CHECK: call swiftcc void @c() +/// CHECK: {{.*}} = {{.*}}@__profc_a{{.*}} +// CHECK: store{{.*}}@__profc_a +// CHECK: {{.*}}call{{.*}}@c( // CHECK: br label %[[MERGE]] // CHECK: [[MERGE]]: // CHECK: ret void -// CHECK: declare swiftcc void @c() -// CHECK: declare swiftcc void @b() -// CHECK: declare void @llvm.instrprof.increment +// CHECK: declare {{.*}}@c( +// CHECK: declare {{.*}}@b( +// CHECK: declare {{.*}}@llvm.instrprof.increment sil @a : $@convention(thin) (Bool) -> () { bb0(%0 : $Bool): From 35b7c753a2661ee4d20f482432316c290f0391da Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Wed, 1 Oct 2025 10:48:05 -0700 Subject: [PATCH 8/9] Make LLVM passes check order independent --- test/IRGen/cs_profile_generate.sil | 6 +++--- test/IRGen/ir_profile_generate.sil | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/test/IRGen/cs_profile_generate.sil b/test/IRGen/cs_profile_generate.sil index 23dd0a37388ea..226b1f9a86634 100644 --- a/test/IRGen/cs_profile_generate.sil +++ b/test/IRGen/cs_profile_generate.sil @@ -6,9 +6,9 @@ // RUN: %target-swift-frontend -O -cs-profile-generate=%t.dir -emit-ir %s | %FileCheck %s // Ensure Passes: PGOInstrumentationGenPass and InstrProfilingLoweringPass are invoked. -// RUN: %target-swift-frontend -O -cs-profile-generate -emit-ir %s -Xllvm -debug-pass=Structure -Xllvm --time-passes -o /dev/null 2>&1 | %FileCheck -check-prefix=CHECK-PGOGENPASS-INVOKED-INSTR-GEN --check-prefix=CHECK-INSTRPROF %s -// CHECK-PGOGENPASS-INVOKED-INSTR-GEN: PGOInstrumentationGen -// CHECK-INSTRPROF: InstrProfilingLoweringPass +// RUN: %target-swift-frontend -O -cs-profile-generate -emit-ir %s -Xllvm -debug-pass=Structure -Xllvm --time-passes -o /dev/null 2>&1 | %FileCheck -check-prefix=CHECK-PASSES %s +// CHECK-PASSES-DAG: PGOInstrumentationGen +// CHECK-PASSES-DAG: InstrProfilingLoweringPass sil_stage canonical diff --git a/test/IRGen/ir_profile_generate.sil b/test/IRGen/ir_profile_generate.sil index b94788a9f7db0..61e4bb9e8d4e0 100644 --- a/test/IRGen/ir_profile_generate.sil +++ b/test/IRGen/ir_profile_generate.sil @@ -6,10 +6,9 @@ // RUN: %target-swift-frontend -ir-profile-generate=%t.dir -emit-ir %s | %FileCheck %s // Ensure Passes: PGOInstrumentationGenPass and InstrProfilingLoweringPass are invoked. -// RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s -Xllvm -debug-pass=Structure -Xllvm --time-passes -o /dev/null 2>&1 | %FileCheck -check-prefix=CHECK-PGOGENPASS-INVOKED-INSTR-GEN --check-prefix=CHECK-INSTRPROF %s -// CHECK-PGOGENPASS-INVOKED-INSTR-GEN: PGOInstrumentationGen -// CHECK-INSTRPROF: InstrProfilingLoweringPass - +// RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s -Xllvm -debug-pass=Structure -Xllvm --time-passes -o /dev/null 2>&1 | %FileCheck -check-prefix=CHECK-PASSES %s +// CHECK-PASSES-DAG: PGOInstrumentationGen +// CHECK-PASSES-DAG: InstrProfilingLoweringPass sil_stage canonical From 9dc69323fcb68f6912e481b65d5ccc4c8b307c49 Mon Sep 17 00:00:00 2001 From: Chirag Ramani Date: Thu, 2 Oct 2025 09:44:03 -0700 Subject: [PATCH 9/9] Ignore case for error message as different platforms have different conventions --- test/IRGen/ir_profile_use.sil | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/IRGen/ir_profile_use.sil b/test/IRGen/ir_profile_use.sil index 1a2739f66871d..aaa2068c245ec 100644 --- a/test/IRGen/ir_profile_use.sil +++ b/test/IRGen/ir_profile_use.sil @@ -1,5 +1,5 @@ // Missing file -// RUN: %target-swift-frontend -ir-profile-use=missing.profdata -emit-ir %s 2>&1 | %FileCheck --check-prefix=CHECK-NOFILE %s +// RUN: %target-swift-frontend -ir-profile-use=missing.profdata -emit-ir %s 2>&1 | %FileCheck --ignore-case --check-prefix=CHECK-NOFILE %s // CHECK-NOFILE: error reading profile // CHECK-NOFILE: .profdata // CHECK-NOFILE: No such file or directory @@ -8,10 +8,10 @@ // Invalid IR file // RUN: %empty-directory(%t) // RUN: echo "" > %t.invalid.profdata -// RUN: %target-swift-frontend -ir-profile-use=%t.invalid.profdata -emit-ir %s 2>&1 | %FileCheck --check-prefix=CHECK-INVALIDFILE %s +// RUN: %target-swift-frontend -ir-profile-use=%t.invalid.profdata -emit-ir %s 2>&1 | %FileCheck --ignore-case --check-prefix=CHECK-INVALIDFILE %s // CHECK-INVALIDFILE: error reading profile // CHECK-INVALIDFILE: invalid.profdata -// CHECK-INVALIDFILE: invalid instrumentation profile data (bad magic) +// CHECK-INVALIDFILE: invalid instrumentation profile data sil_stage canonical