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

@@ -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,

View File

@@ -548,10 +548,23 @@ public:
/// 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 = "";
/// 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;
/// Whether to enable IR level instrumentation.
bool EnableIRProfileGen = false;
/// Controls whether DWARF discriminators are added to the IR.
unsigned DebugInfoForProfiling : 1;

View File

@@ -1631,6 +1631,29 @@ def profile_sample_use : Joined<["-"], "profile-sample-use=">,
MetaVarName<"<profile data>">,
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<"<directory>">,
HelpText<"Generate instrumented code to collect context sensitive execution counts into <directory>/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 ir_profile_generate_EQ : Joined<["-"], "ir-profile-generate=">,
Flags<[FrontendOption, NoInteractiveOption]>,
MetaVarName<"<directory>">,
HelpText<"Generate instrumented code to collect execution counts into <directory>/default.profraw (overridden by LLVM_PROFILE_FILE env var)">;
def ir_profile_use : CommaJoined<["-"], "ir-profile-use=">,
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath]>,
MetaVarName<"<profdata>">,
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">;

View File

@@ -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 {
@@ -472,7 +473,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);

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());
}
}
}

View File

@@ -160,6 +160,16 @@ 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) ||
Args.hasArg(options::OPT_ir_profile_generate_EQ);
}
} // namespace swift::driver::toolchains
void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
const CommandOutput &output,
const ArgList &inputArgs,
@@ -323,6 +333,11 @@ 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_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);
inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
inputArgs.AddAllArgs(arguments, options::OPT_warning_treating_Group);
inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ);

View File

@@ -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:

View File

@@ -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");

View File

@@ -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");

View File

@@ -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()})));

View File

@@ -3572,6 +3572,17 @@ 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) ||
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())
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);

View File

@@ -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"
@@ -217,8 +218,57 @@ static void align(llvm::Module *Module) {
}
}
static std::unique_ptr<llvm::IndexedInstrProfReader>
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<PGOOptions> buildIRUseOptions(const IRGenOptions &Opts,
DiagnosticEngine &Diags) {
if (Opts.UseIRProfile.empty())
return std::nullopt;
auto FS = llvm::vfs::getRealFileSystem();
std::unique_ptr<llvm::IndexedInstrProfReader> 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<PGOOptions> &Out,
const IRGenOptions &Opts) {
const IRGenOptions &Opts,
DiagnosticEngine &Diags) {
if (!Opts.UseSampleProfile.empty()) {
Out = PGOOptions(
/*ProfileFile=*/ Opts.UseSampleProfile,
@@ -234,6 +284,40 @@ static void populatePGOOptions(std::optional<PGOOptions> &Out,
return;
}
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);
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);
return;
}
if (auto IRUseOptions = buildIRUseOptions(Opts, Diags)) {
Out = *IRUseOptions;
return;
}
if (Opts.DebugInfoForProfiling) {
Out = PGOOptions(
/*ProfileFile=*/ "",
@@ -269,7 +353,7 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts,
llvm::TargetMachine *TargetMachine,
llvm::raw_pwrite_stream *out) {
std::optional<PGOOptions> PGOOpt;
populatePGOOptions(PGOOpt, Opts);
populatePGOOptions(PGOOpt, Opts, Diags);
PipelineTuningOptions PTO;

View File

@@ -0,0 +1,71 @@
// 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
// 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-PASSES %s
// CHECK-PASSES-DAG: PGOInstrumentationGen
// CHECK-PASSES-DAG: InstrProfilingLoweringPass
sil_stage canonical
import Builtin
import Swift
import SwiftShims
sil @b : $@convention(thin) () -> ()
sil @c : $@convention(thin) () -> ()
// counter array (2 counters for the then/else)
// 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]+]]
// THEN: increment counter[0] and call b()
// CHECK: [[THEN]]:
// 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: {{.*}} = {{.*}}@__profc_a{{.*}}
// CHECK: store{{.*}}@__profc_a
// CHECK: {{.*}}call{{.*}}@c(
// CHECK: br label %[[MERGE]]
// CHECK: [[MERGE]]:
// CHECK: ret void
// CHECK: declare {{.*}}@c(
// CHECK: declare {{.*}}@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
}

View File

@@ -0,0 +1,71 @@
// 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-PASSES %s
// CHECK-PASSES-DAG: PGOInstrumentationGen
// CHECK-PASSES-DAG: InstrProfilingLoweringPass
sil_stage canonical
import Builtin
import Swift
import SwiftShims
sil @b : $@convention(thin) () -> ()
sil @c : $@convention(thin) () -> ()
// counter array (2 counters for the then/else)
// 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]+]]
// THEN: increment counter[0] and call b()
// CHECK: [[THEN]]:
// 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: {{.*}} = {{.*}}@__profc_a{{.*}}
// CHECK: store{{.*}}@__profc_a
// CHECK: {{.*}}call{{.*}}@c(
// CHECK: br label %[[MERGE]]
// CHECK: [[MERGE]]:
// CHECK: ret void
// CHECK: declare {{.*}}@c(
// CHECK: declare {{.*}}@b(
// CHECK: declare {{.*}}@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
}

View File

@@ -0,0 +1,23 @@
// Missing file
// 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
// 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 --ignore-case --check-prefix=CHECK-INVALIDFILE %s
// CHECK-INVALIDFILE: error reading profile
// CHECK-INVALIDFILE: invalid.profdata
// CHECK-INVALIDFILE: invalid instrumentation profile data
sil_stage canonical
import Builtin
import Swift
import SwiftShims
public func foo() {}