Skip to content

Fix and reapply IR PGO support for Flang #142892

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions clang/include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,11 @@ AFFECTING_VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is
CODEGENOPT(AtomicProfileUpdate , 1, 0) ///< Set -fprofile-update=atomic
CODEGENOPT(ContinuousProfileSync, 1, 0) ///< Enable continuous instrumentation profiling
/// Choose profile instrumenation kind or no instrumentation.
ENUM_CODEGENOPT(ProfileInstr, ProfileInstrKind, 4, ProfileNone)

ENUM_CODEGENOPT(ProfileInstr, llvm::driver::ProfileInstrKind, 4, llvm::driver::ProfileInstrKind::ProfileNone)

/// Choose profile kind for PGO use compilation.
ENUM_CODEGENOPT(ProfileUse, ProfileInstrKind, 2, ProfileNone)
ENUM_CODEGENOPT(ProfileUse, llvm::driver::ProfileInstrKind, 2, llvm::driver::ProfileInstrKind::ProfileNone)
/// Partition functions into N groups and select only functions in group i to be
/// instrumented. Selected group numbers can be 0 to N-1 inclusive.
VALUE_CODEGENOPT(ProfileTotalFunctionGroups, 32, 1)
Expand Down
32 changes: 14 additions & 18 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,6 @@ class CodeGenOptions : public CodeGenOptionsBase {
SRCK_InRegs // Small structs in registers (-freg-struct-return).
};

enum ProfileInstrKind {
ProfileNone, // Profile instrumentation is turned off.
ProfileClangInstr, // Clang instrumentation to generate execution counts
// to use with PGO.
ProfileIRInstr, // IR level PGO instrumentation in LLVM.
ProfileCSIRInstr, // IR level PGO context sensitive instrumentation in LLVM.
ProfileIRSampleColdCov, // IR level sample pgo based cold function coverage
// instrumentation in LLVM.
};

enum EmbedBitcodeKind {
Embed_Off, // No embedded bitcode.
Embed_All, // Embed both bitcode and commandline in the output.
Expand Down Expand Up @@ -522,35 +512,41 @@ class CodeGenOptions : public CodeGenOptionsBase {

/// Check if Clang profile instrumenation is on.
bool hasProfileClangInstr() const {
return getProfileInstr() == ProfileClangInstr;
return getProfileInstr() ==
llvm::driver::ProfileInstrKind::ProfileClangInstr;
}

/// Check if IR level profile instrumentation is on.
bool hasProfileIRInstr() const {
return getProfileInstr() == ProfileIRInstr;
return getProfileInstr() == llvm::driver::ProfileInstrKind::ProfileIRInstr;
}

/// Check if CS IR level profile instrumentation is on.
bool hasProfileCSIRInstr() const {
return getProfileInstr() == ProfileCSIRInstr;
return getProfileInstr() ==
llvm::driver::ProfileInstrKind::ProfileCSIRInstr;
}

/// Check if any form of instrumentation is on.
bool hasProfileInstr() const { return getProfileInstr() != ProfileNone; }
bool hasProfileInstr() const {
return getProfileInstr() != llvm::driver::ProfileInstrKind::ProfileNone;
}

/// Check if Clang profile use is on.
bool hasProfileClangUse() const {
return getProfileUse() == ProfileClangInstr;
return getProfileUse() == llvm::driver::ProfileInstrKind::ProfileClangInstr;
}

/// Check if IR level profile use is on.
bool hasProfileIRUse() const {
return getProfileUse() == ProfileIRInstr ||
getProfileUse() == ProfileCSIRInstr;
return getProfileUse() == llvm::driver::ProfileInstrKind::ProfileIRInstr ||
getProfileUse() == llvm::driver::ProfileInstrKind::ProfileCSIRInstr;
}

/// Check if CSIR profile use is on.
bool hasProfileCSIRUse() const { return getProfileUse() == ProfileCSIRInstr; }
bool hasProfileCSIRUse() const {
return getProfileUse() == llvm::driver::ProfileInstrKind::ProfileCSIRInstr;
}

/// Check if type and variable info should be emitted.
bool hasReducedDebugInfo() const {
Expand Down
9 changes: 4 additions & 5 deletions clang/include/clang/Basic/ProfileList.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,16 @@ class ProfileList {
~ProfileList();

bool isEmpty() const { return Empty; }
ExclusionType getDefault(CodeGenOptions::ProfileInstrKind Kind) const;
ExclusionType getDefault(llvm::driver::ProfileInstrKind Kind) const;

std::optional<ExclusionType>
isFunctionExcluded(StringRef FunctionName,
CodeGenOptions::ProfileInstrKind Kind) const;
llvm::driver::ProfileInstrKind Kind) const;
std::optional<ExclusionType>
isLocationExcluded(SourceLocation Loc,
CodeGenOptions::ProfileInstrKind Kind) const;
llvm::driver::ProfileInstrKind Kind) const;
std::optional<ExclusionType>
isFileExcluded(StringRef FileName,
CodeGenOptions::ProfileInstrKind Kind) const;
isFileExcluded(StringRef FileName, llvm::driver::ProfileInstrKind Kind) const;
};

} // namespace clang
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1772,7 +1772,7 @@ def fmcdc_max_test_vectors_EQ : Joined<["-"], "fmcdc-max-test-vectors=">,
HelpText<"Maximum number of test vectors in MC/DC coverage">,
MarshallingInfoInt<CodeGenOpts<"MCDCMaxTVs">, "0x7FFFFFFE">;
def fprofile_generate : Flag<["-"], "fprofile-generate">,
Group<f_Group>, Visibility<[ClangOption, CLOption]>,
Group<f_Group>, Visibility<[ClangOption, CLOption, FlangOption, FC1Option]>,
HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">;
def fprofile_generate_EQ : Joined<["-"], "fprofile-generate=">,
Group<f_Group>, Visibility<[ClangOption, CLOption]>,
Expand All @@ -1789,7 +1789,7 @@ def fprofile_use : Flag<["-"], "fprofile-use">, Group<f_Group>,
Visibility<[ClangOption, CLOption]>, Alias<fprofile_instr_use>;
def fprofile_use_EQ : Joined<["-"], "fprofile-use=">,
Group<f_Group>,
Visibility<[ClangOption, CLOption]>,
Visibility<[ClangOption, CLOption, FlangOption, FC1Option]>,
MetaVarName<"<pathname>">,
HelpText<"Use instrumentation data for profile-guided optimization. If pathname is a directory, it reads from <pathname>/default.profdata. Otherwise, it reads from file <pathname>.">;
def fno_profile_instr_generate : Flag<["-"], "fno-profile-instr-generate">,
Expand Down Expand Up @@ -7761,7 +7761,7 @@ def fpatchable_function_entry_section_EQ
MarshallingInfoString<CodeGenOpts<"PatchableFunctionEntrySection">>;
def fprofile_instrument_EQ : Joined<["-"], "fprofile-instrument=">,
HelpText<"Enable PGO instrumentation">, Values<"none,clang,llvm,csllvm,sample-coldcov">,
NormalizedValuesScope<"CodeGenOptions">,
NormalizedValuesScope<"llvm::driver::ProfileInstrKind">,
NormalizedValues<["ProfileNone", "ProfileClangInstr", "ProfileIRInstr", "ProfileCSIRInstr", "ProfileIRSampleColdCov"]>,
MarshallingInfoEnum<CodeGenOpts<"ProfileInstr">, "ProfileNone">;
def fprofile_instrument_path_EQ : Joined<["-"], "fprofile-instrument-path=">,
Expand Down
22 changes: 11 additions & 11 deletions clang/lib/Basic/ProfileList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,24 @@ ProfileList::ProfileList(ArrayRef<std::string> Paths, SourceManager &SM)

ProfileList::~ProfileList() = default;

static StringRef getSectionName(CodeGenOptions::ProfileInstrKind Kind) {
static StringRef getSectionName(llvm::driver::ProfileInstrKind Kind) {
switch (Kind) {
case CodeGenOptions::ProfileNone:
case llvm::driver::ProfileInstrKind::ProfileNone:
return "";
case CodeGenOptions::ProfileClangInstr:
case llvm::driver::ProfileInstrKind::ProfileClangInstr:
return "clang";
case CodeGenOptions::ProfileIRInstr:
case llvm::driver::ProfileInstrKind::ProfileIRInstr:
return "llvm";
case CodeGenOptions::ProfileCSIRInstr:
case llvm::driver::ProfileInstrKind::ProfileCSIRInstr:
return "csllvm";
case CodeGenOptions::ProfileIRSampleColdCov:
case llvm::driver::ProfileInstrKind::ProfileIRSampleColdCov:
return "sample-coldcov";
}
llvm_unreachable("Unhandled CodeGenOptions::ProfileInstrKind enum");
llvm_unreachable("Unhandled llvm::driver::ProfileInstrKind enum");
}

ProfileList::ExclusionType
ProfileList::getDefault(CodeGenOptions::ProfileInstrKind Kind) const {
ProfileList::getDefault(llvm::driver::ProfileInstrKind Kind) const {
StringRef Section = getSectionName(Kind);
// Check for "default:<type>"
if (SCL->inSection(Section, "default", "allow"))
Expand Down Expand Up @@ -117,7 +117,7 @@ ProfileList::inSection(StringRef Section, StringRef Prefix,

std::optional<ProfileList::ExclusionType>
ProfileList::isFunctionExcluded(StringRef FunctionName,
CodeGenOptions::ProfileInstrKind Kind) const {
llvm::driver::ProfileInstrKind Kind) const {
StringRef Section = getSectionName(Kind);
// Check for "function:<regex>=<case>"
if (auto V = inSection(Section, "function", FunctionName))
Expand All @@ -131,13 +131,13 @@ ProfileList::isFunctionExcluded(StringRef FunctionName,

std::optional<ProfileList::ExclusionType>
ProfileList::isLocationExcluded(SourceLocation Loc,
CodeGenOptions::ProfileInstrKind Kind) const {
llvm::driver::ProfileInstrKind Kind) const {
return isFileExcluded(SM.getFilename(SM.getFileLoc(Loc)), Kind);
}

std::optional<ProfileList::ExclusionType>
ProfileList::isFileExcluded(StringRef FileName,
CodeGenOptions::ProfileInstrKind Kind) const {
llvm::driver::ProfileInstrKind Kind) const {
StringRef Section = getSectionName(Kind);
// Check for "source:<regex>=<case>"
if (auto V = inSection(Section, "source", FileName))
Expand Down
9 changes: 1 addition & 8 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,10 @@ namespace clang {
extern llvm::cl::opt<bool> ClSanitizeGuardChecks;
}

// Default filename used for profile generation.
static std::string getDefaultProfileGenName() {
return DebugInfoCorrelate || ProfileCorrelate != InstrProfCorrelator::NONE
? "default_%m.proflite"
: "default_%m.profraw";
}

// Path and name of file used for profile generation
static std::string getProfileGenName(const CodeGenOptions &CodeGenOpts) {
std::string FileName = CodeGenOpts.InstrProfileOutput.empty()
? getDefaultProfileGenName()
? llvm::driver::getDefaultProfileGenName()
: CodeGenOpts.InstrProfileOutput;
if (CodeGenOpts.ContinuousProfileSync)
FileName = "%c" + FileName;
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CodeGenAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ void BackendConsumer::HandleTranslationUnit(ASTContext &C) {
std::unique_ptr<llvm::ToolOutputFile> OptRecordFile =
std::move(*OptRecordFileOrErr);

if (OptRecordFile &&
CodeGenOpts.getProfileUse() != CodeGenOptions::ProfileNone)
if (OptRecordFile && CodeGenOpts.getProfileUse() !=
llvm::driver::ProfileInstrKind::ProfileNone)
Ctx.setDiagnosticsHotnessRequested(true);

if (CodeGenOpts.MisExpect) {
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
}
}

if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone) {
if (CGM.getCodeGenOpts().getProfileInstr() !=
llvm::driver::ProfileInstrKind::ProfileNone) {
switch (CGM.isFunctionBlockedFromProfileInstr(Fn, Loc)) {
case ProfileList::Skip:
Fn->addFnAttr(llvm::Attribute::SkipProfile);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3608,7 +3608,7 @@ CodeGenModule::isFunctionBlockedByProfileList(llvm::Function *Fn,
// If the profile list is empty, then instrument everything.
if (ProfileList.isEmpty())
return ProfileList::Allow;
CodeGenOptions::ProfileInstrKind Kind = getCodeGenOpts().getProfileInstr();
llvm::driver::ProfileInstrKind Kind = getCodeGenOpts().getProfileInstr();
// First, check the function name.
if (auto V = ProfileList.isFunctionExcluded(Fn->getName(), Kind))
return *V;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,10 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
// TODO: Handle interactions between -w, -pedantic, -Wall, -WOption
Args.AddLastArg(CmdArgs, options::OPT_w);

// recognise options: fprofile-generate -fprofile-use=
Args.addAllArgs(
CmdArgs, {options::OPT_fprofile_generate, options::OPT_fprofile_use_EQ});

// Forward flags for OpenMP. We don't do this if the current action is an
// device offloading action other than OpenMP.
if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1492,11 +1492,11 @@ static void setPGOUseInstrumentor(CodeGenOptions &Opts,
// which is available (might be one or both).
if (PGOReader->isIRLevelProfile() || PGOReader->hasMemoryProfile()) {
if (PGOReader->hasCSIRLevelProfile())
Opts.setProfileUse(CodeGenOptions::ProfileCSIRInstr);
Opts.setProfileUse(llvm::driver::ProfileInstrKind::ProfileCSIRInstr);
else
Opts.setProfileUse(CodeGenOptions::ProfileIRInstr);
Opts.setProfileUse(llvm::driver::ProfileInstrKind::ProfileIRInstr);
} else
Opts.setProfileUse(CodeGenOptions::ProfileClangInstr);
Opts.setProfileUse(llvm::driver::ProfileInstrKind::ProfileClangInstr);
}

void CompilerInvocation::setDefaultPointerAuthOptions(
Expand Down
7 changes: 7 additions & 0 deletions flang/include/flang/Frontend/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,15 @@ CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified.
CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new
///< pass manager.


/// Choose profile instrumenation kind or no instrumentation.
ENUM_CODEGENOPT(ProfileInstr, llvm::driver::ProfileInstrKind, 2, llvm::driver::ProfileInstrKind::ProfileNone)
/// Choose profile kind for PGO use compilation.
ENUM_CODEGENOPT(ProfileUse, llvm::driver::ProfileInstrKind, 2, llvm::driver::ProfileInstrKind::ProfileNone)

CODEGENOPT(InstrumentFunctions, 1, 0) ///< Set when -finstrument_functions is
///< enabled on the compile step.

CODEGENOPT(IsPIE, 1, 0) ///< PIE level is the same as PIC Level.
CODEGENOPT(PICLevel, 2, 0) ///< PIC level of the LLVM module.
CODEGENOPT(PrepareForFullLTO , 1, 0) ///< Set when -flto is enabled on the
Expand Down
38 changes: 38 additions & 0 deletions flang/include/flang/Frontend/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,44 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// OpenMP is enabled.
using DoConcurrentMappingKind = flangomp::DoConcurrentMappingKind;

/// Name of the profile file to use as output for -fprofile-instr-generate,
/// -fprofile-generate, and -fcs-profile-generate.
std::string InstrProfileOutput;

/// Name of the profile file to use as input for -fmemory-profile-use.
std::string MemoryProfileUsePath;

/// Name of the profile file to use as input for -fprofile-instr-use
std::string ProfileInstrumentUsePath;

/// Name of the profile remapping file to apply to the profile data supplied
/// by -fprofile-sample-use or -fprofile-instr-use.
std::string ProfileRemappingFile;

/// Check if Clang profile instrumenation is on.
bool hasProfileClangInstr() const {
return getProfileInstr() == llvm::driver::ProfileClangInstr;
}

/// Check if IR level profile instrumentation is on.
bool hasProfileIRInstr() const {
return getProfileInstr() == llvm::driver::ProfileIRInstr;
}

/// Check if CS IR level profile instrumentation is on.
bool hasProfileCSIRInstr() const {
return getProfileInstr() == llvm::driver::ProfileCSIRInstr;
}
/// Check if IR level profile use is on.
bool hasProfileIRUse() const {
return getProfileUse() == llvm::driver::ProfileIRInstr ||
getProfileUse() == llvm::driver::ProfileCSIRInstr;
}
/// Check if CSIR profile use is on.
bool hasProfileCSIRUse() const {
return getProfileUse() == llvm::driver::ProfileCSIRInstr;
}

// Define accessors/mutators for code generation options of enumeration type.
#define CODEGENOPT(Name, Bits, Default)
#define ENUM_CODEGENOPT(Name, Type, Bits, Default) \
Expand Down
10 changes: 10 additions & 0 deletions flang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Frontend/Debug/Options.h"
#include "llvm/Frontend/Driver/CodeGenOptions.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
Expand Down Expand Up @@ -441,6 +442,15 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
opts.IsPIE = 1;
}

if (args.hasArg(clang::driver::options::OPT_fprofile_generate)) {
opts.setProfileInstr(llvm::driver::ProfileInstrKind::ProfileIRInstr);
}

if (auto A = args.getLastArg(clang::driver::options::OPT_fprofile_use_EQ)) {
opts.setProfileUse(llvm::driver::ProfileInstrKind::ProfileIRInstr);
opts.ProfileInstrumentUsePath = A->getValue();
}

// -mcmodel option.
if (const llvm::opt::Arg *a =
args.getLastArg(clang::driver::options::OPT_mcmodel_EQ)) {
Expand Down
26 changes: 26 additions & 0 deletions flang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,20 @@
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/ProfileData/InstrProfCorrelator.h"
#include "llvm/Support/AMDGPUAddrSpace.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/PGOOptions.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/TargetParser/RISCVISAInfo.h"
#include "llvm/TargetParser/RISCVTargetParser.h"
#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/Instrumentation/InstrProfiling.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <memory>
#include <system_error>
Expand Down Expand Up @@ -919,6 +922,29 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
llvm::PassInstrumentationCallbacks pic;
llvm::PipelineTuningOptions pto;
std::optional<llvm::PGOOptions> pgoOpt;

if (opts.hasProfileIRInstr()) {
// -fprofile-generate.
pgoOpt = llvm::PGOOptions(opts.InstrProfileOutput.empty()
? llvm::driver::getDefaultProfileGenName()
: opts.InstrProfileOutput,
"", "", opts.MemoryProfileUsePath, nullptr,
llvm::PGOOptions::IRInstr,
llvm::PGOOptions::NoCSAction,
llvm::PGOOptions::ColdFuncOpt::Default, false,
/*PseudoProbeForProfiling=*/false, false);
} else if (opts.hasProfileIRUse()) {
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
llvm::vfs::getRealFileSystem();
// -fprofile-use.
auto CSAction = opts.hasProfileCSIRUse() ? llvm::PGOOptions::CSIRUse
: llvm::PGOOptions::NoCSAction;
pgoOpt = llvm::PGOOptions(
opts.ProfileInstrumentUsePath, "", opts.ProfileRemappingFile,
opts.MemoryProfileUsePath, VFS, llvm::PGOOptions::IRUse, CSAction,
llvm::PGOOptions::ColdFuncOpt::Default, false);
}

llvm::StandardInstrumentations si(llvmModule->getContext(),
opts.DebugPassManager);
si.registerCallbacks(pic, &mam);
Expand Down
Loading