-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[CodeGen][StaticDataPartitioning]Place local-linkage global variables in hot or unlikely prefixed sections based on profile information #125756
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
Changes from 11 commits
8eea1ea
93d9881
5cbe8f8
8f21570
f07d34d
4e096e9
4a2a881
1f50494
967dc03
9302b2b
97103c6
38c8a03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #ifndef LLVM_ANALYSIS_STATICDATAPROFILEINFO_H | ||
| #define LLVM_ANALYSIS_STATICDATAPROFILEINFO_H | ||
|
|
||
| #include "llvm/ADT/DenseMap.h" | ||
| #include "llvm/ADT/DenseSet.h" | ||
| #include "llvm/Analysis/ProfileSummaryInfo.h" | ||
| #include "llvm/IR/Constant.h" | ||
| #include "llvm/Pass.h" | ||
|
|
||
| namespace llvm { | ||
|
|
||
| /// A class that holds the constants that represent static data and their | ||
| /// profile information and provides methods to operate on them. | ||
| class StaticDataProfileInfo { | ||
| public: | ||
| /// Accummulate the profile count of a constant that will be lowered to static | ||
| /// data sections. | ||
| DenseMap<const Constant *, uint64_t> ConstantProfileCounts; | ||
|
|
||
| /// Keeps track of the constants that are seen at least once without profile | ||
| /// counts. | ||
| DenseSet<const Constant *> ConstantWithoutCounts; | ||
|
|
||
| /// If \p C has a count, return it. Otherwise, return std::nullopt. | ||
| std::optional<uint64_t> getConstantProfileCount(const Constant *C) const; | ||
|
|
||
| public: | ||
| StaticDataProfileInfo() = default; | ||
|
|
||
| /// If \p Count is not nullopt, add it to the profile count of the constant \p | ||
| /// C in a saturating way, and clamp the count to \p getInstrMaxCountValue if | ||
| /// the result exceeds it. Otherwise, mark the constant as having no profile | ||
| /// count. | ||
| void addConstantProfileCount(const Constant *C, | ||
| std::optional<uint64_t> Count); | ||
|
|
||
| /// Return a section prefix for the constant \p C based on its profile count. | ||
| /// - If a constant doesn't have a counter, return an empty string. | ||
| /// - Otherwise, | ||
| /// - If it has a hot count, return "hot". | ||
| /// - If it is seen by unprofiled function, return an empty string. | ||
| /// - If it has a cold count, return "unlikely". | ||
| /// - Otherwise (e.g. it's used by lukewarm functions), return an empty | ||
| /// string. | ||
| StringRef getConstantSectionPrefix(const Constant *C, | ||
| const ProfileSummaryInfo *PSI) const; | ||
| }; | ||
|
|
||
| /// This wraps the StaticDataProfileInfo object as an immutable pass, for a | ||
| /// backend pass to operate on. | ||
| class StaticDataProfileInfoWrapperPass : public ImmutablePass { | ||
| public: | ||
| static char ID; | ||
| StaticDataProfileInfoWrapperPass(); | ||
| bool doInitialization(Module &M) override; | ||
| bool doFinalization(Module &M) override; | ||
|
|
||
| StaticDataProfileInfo &getStaticDataProfileInfo() { return *Info; } | ||
| const StaticDataProfileInfo &getStaticDataProfileInfo() const { | ||
| return *Info; | ||
| } | ||
|
|
||
| /// This pass provides StaticDataProfileInfo for reads/writes but does not | ||
| /// modify \p M or other analysis. All analysis are preserved. | ||
| void getAnalysisUsage(AnalysisUsage &AU) const override { | ||
| AU.setPreservesAll(); | ||
| } | ||
|
|
||
| private: | ||
| std::unique_ptr<StaticDataProfileInfo> Info; | ||
| }; | ||
|
|
||
| } // namespace llvm | ||
|
|
||
| #endif // LLVM_ANALYSIS_STATICDATAPROFILEINFO_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #include "llvm/Analysis/StaticDataProfileInfo.h" | ||
| #include "llvm/Analysis/ProfileSummaryInfo.h" | ||
| #include "llvm/IR/Constant.h" | ||
| #include "llvm/IR/GlobalVariable.h" | ||
| #include "llvm/InitializePasses.h" | ||
| #include "llvm/ProfileData/InstrProf.h" | ||
| #include <sys/types.h> | ||
|
|
||
| using namespace llvm; | ||
| void StaticDataProfileInfo::addConstantProfileCount( | ||
| const Constant *C, std::optional<uint64_t> Count) { | ||
| if (!Count) { | ||
| ConstantWithoutCounts.insert(C); | ||
| return; | ||
| } | ||
| uint64_t &OriginalCount = ConstantProfileCounts[C]; | ||
| OriginalCount = llvm::SaturatingAdd(*Count, OriginalCount); | ||
| // Clamp the count to getInstrMaxCountValue. InstrFDO reserves a few | ||
| // large values for special use. | ||
| if (OriginalCount > getInstrMaxCountValue()) | ||
| OriginalCount = getInstrMaxCountValue(); | ||
| } | ||
|
|
||
| std::optional<uint64_t> | ||
| StaticDataProfileInfo::getConstantProfileCount(const Constant *C) const { | ||
| auto I = ConstantProfileCounts.find(C); | ||
| if (I == ConstantProfileCounts.end()) | ||
| return std::nullopt; | ||
| return I->second; | ||
| } | ||
|
|
||
| StringRef StaticDataProfileInfo::getConstantSectionPrefix( | ||
| const Constant *C, const ProfileSummaryInfo *PSI) const { | ||
| auto Count = getConstantProfileCount(C); | ||
| if (!Count) | ||
| return ""; | ||
| if (PSI->isHotCount(*Count)) | ||
| return "hot"; | ||
| if (ConstantWithoutCounts.count(C)) | ||
snehasish marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return ""; | ||
| if (PSI->isColdCount(*Count)) | ||
| return "unlikely"; | ||
| return ""; | ||
| } | ||
|
|
||
| bool StaticDataProfileInfoWrapperPass::doInitialization(Module &M) { | ||
| Info.reset(new StaticDataProfileInfo()); | ||
| return false; | ||
| } | ||
|
|
||
| bool StaticDataProfileInfoWrapperPass::doFinalization(Module &M) { | ||
| Info.reset(); | ||
| return false; | ||
| } | ||
|
|
||
| INITIALIZE_PASS(StaticDataProfileInfoWrapperPass, "static-data-profile-info", | ||
| "Static Data Profile Info", false, true) | ||
|
|
||
| StaticDataProfileInfoWrapperPass::StaticDataProfileInfoWrapperPass() | ||
| : ImmutablePass(ID) { | ||
| initializeStaticDataProfileInfoWrapperPassPass( | ||
| *PassRegistry::getPassRegistry()); | ||
| } | ||
|
|
||
| char StaticDataProfileInfoWrapperPass::ID = 0; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,103 @@ | ||||||||||||||||||||||||||||||||||
| //===- StaticDataAnnotator - Annotate static data's section prefix --------===// | ||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||||||||||||||||||||||||||||||||
| // See https://llvm.org/LICENSE.txt for license information. | ||||||||||||||||||||||||||||||||||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||
| // To reason about module-wide data hotness in a module granularity, this file | ||||||||||||||||||||||||||||||||||
| // implements a module pass StaticDataAnnotator to work coordinately with the | ||||||||||||||||||||||||||||||||||
| // StaticDataSplitter pass. | ||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||
| // The StaticDataSplitter pass is a machine function pass. It analyzes data | ||||||||||||||||||||||||||||||||||
| // hotness based on code and adds counters in the StaticDataProfileInfo. | ||||||||||||||||||||||||||||||||||
| // The StaticDataAnnotator pass is a module pass. It iterates global variables | ||||||||||||||||||||||||||||||||||
| // in the module, looks up counters from StaticDataProfileInfo and sets the | ||||||||||||||||||||||||||||||||||
| // section prefix based on profiles. | ||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||
| // The three-pass structure is implemented for practical reasons, to work around | ||||||||||||||||||||||||||||||||||
snehasish marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
| // the limitation that a module pass based on legacy pass manager cannot make | ||||||||||||||||||||||||||||||||||
| // use of MachineBlockFrequencyInfo analysis. In the future, we can consider | ||||||||||||||||||||||||||||||||||
| // porting the StaticDataSplitter pass to a module-pass using the new pass | ||||||||||||||||||||||||||||||||||
| // manager framework. That way, analysis are lazily computed as opposed to | ||||||||||||||||||||||||||||||||||
| // eagerly scheduled, and a module pass can use MachineBlockFrequencyInfo. | ||||||||||||||||||||||||||||||||||
| //===----------------------------------------------------------------------===// | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| #include "llvm/Analysis/ProfileSummaryInfo.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/Analysis/StaticDataProfileInfo.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/CodeGen/Passes.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/IR/Analysis.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/IR/Module.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/IR/PassManager.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/InitializePasses.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/Pass.h" | ||||||||||||||||||||||||||||||||||
| #include "llvm/Support/raw_ostream.h" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| #define DEBUG_TYPE "static-data-annotator" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| using namespace llvm; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| class StaticDataAnnotator : public ModulePass { | ||||||||||||||||||||||||||||||||||
snehasish marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
| public: | ||||||||||||||||||||||||||||||||||
| static char ID; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StaticDataProfileInfo *SDPI = nullptr; | ||||||||||||||||||||||||||||||||||
| const ProfileSummaryInfo *PSI = nullptr; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StaticDataAnnotator() : ModulePass(ID) { | ||||||||||||||||||||||||||||||||||
| initializeStaticDataAnnotatorPass(*PassRegistry::getPassRegistry()); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| void getAnalysisUsage(AnalysisUsage &AU) const override { | ||||||||||||||||||||||||||||||||||
| AU.addRequired<StaticDataProfileInfoWrapperPass>(); | ||||||||||||||||||||||||||||||||||
| AU.addRequired<ProfileSummaryInfoWrapperPass>(); | ||||||||||||||||||||||||||||||||||
| AU.setPreservesAll(); | ||||||||||||||||||||||||||||||||||
| ModulePass::getAnalysisUsage(AU); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StringRef getPassName() const override { return "Static Data Annotator"; } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| bool runOnModule(Module &M) override; | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Returns true if the global variable already has a section prefix that is the | ||||||||||||||||||||||||||||||||||
| // same as `Prefix`. | ||||||||||||||||||||||||||||||||||
| static bool alreadyHasSectionPrefix(const GlobalVariable &GV, | ||||||||||||||||||||||||||||||||||
| StringRef Prefix) { | ||||||||||||||||||||||||||||||||||
| std::optional<StringRef> SectionPrefix = GV.getSectionPrefix(); | ||||||||||||||||||||||||||||||||||
| return SectionPrefix && (*SectionPrefix == Prefix); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| bool StaticDataAnnotator::runOnModule(Module &M) { | ||||||||||||||||||||||||||||||||||
| SDPI = &getAnalysis<StaticDataProfileInfoWrapperPass>() | ||||||||||||||||||||||||||||||||||
| .getStaticDataProfileInfo(); | ||||||||||||||||||||||||||||||||||
| PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI(); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if (!PSI->hasProfileSummary()) | ||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| bool Changed = false; | ||||||||||||||||||||||||||||||||||
| for (auto &GV : M.globals()) { | ||||||||||||||||||||||||||||||||||
| if (GV.isDeclarationForLinker()) | ||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| StringRef SectionPrefix = SDPI->getConstantSectionPrefix(&GV, PSI); | ||||||||||||||||||||||||||||||||||
| if (SectionPrefix.empty() || alreadyHasSectionPrefix(GV, SectionPrefix)) | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| /// This method computes the appropriate section to emit the specified global | |
| /// variable or function definition. This should not be passed external (or | |
| /// available externally) globals. | |
| MCSection *TargetLoweringObjectFile::SectionForGlobal( | |
| const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { | |
| // Select section name. | |
| if (GO->hasSection()) | |
| return getExplicitSectionGlobal(GO, Kind, TM); | |
| if (auto *GVar = dyn_cast<GlobalVariable>(GO)) { | |
| auto Attrs = GVar->getAttributes(); | |
| if ((Attrs.hasAttribute("bss-section") && Kind.isBSS()) || | |
| (Attrs.hasAttribute("data-section") && Kind.isData()) || | |
| (Attrs.hasAttribute("relro-section") && Kind.isReadOnlyWithRel()) || | |
| (Attrs.hasAttribute("rodata-section") && Kind.isReadOnly())) { | |
| return getExplicitSectionGlobal(GO, Kind, TM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is reasonable to put this definition into ProfileSummaryInfo.h, as it is about summary/aggregation of const profiles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I don't feel strong about having new classes in
ProfileSummaryInfo.hor in new files. Actually I once thought about the former and hesitated between the two.I ended up with the latter mainly because PSI is included in multiple cpp files, and the new classes don't need to be included as much.
As the static data partitioning work is under development and symbolized data access profiles may need helper functions and libraries, I think we can keep it this way and do refactors later where they fit. What do you think about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.