mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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">;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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()})));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
71
test/IRGen/cs_profile_generate.sil
Normal file
71
test/IRGen/cs_profile_generate.sil
Normal 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
|
||||
}
|
||||
71
test/IRGen/ir_profile_generate.sil
Normal file
71
test/IRGen/ir_profile_generate.sil
Normal 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
|
||||
}
|
||||
23
test/IRGen/ir_profile_use.sil
Normal file
23
test/IRGen/ir_profile_use.sil
Normal 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() {}
|
||||
Reference in New Issue
Block a user