summaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Transforms/Instrumentation/MemProfUse.cpp')
-rw-r--r--llvm/lib/Transforms/Instrumentation/MemProfUse.cpp120
1 files changed, 117 insertions, 3 deletions
diff --git a/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp b/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
index a9a0731f16d9..ecb2f2dbc552 100644
--- a/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
@@ -22,6 +22,7 @@
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
+#include "llvm/ProfileData/DataAccessProf.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/MemProfCommon.h"
@@ -75,6 +76,10 @@ static cl::opt<unsigned> MinMatchedColdBytePercent(
"memprof-matching-cold-threshold", cl::init(100), cl::Hidden,
cl::desc("Min percent of cold bytes matched to hint allocation cold"));
+static cl::opt<bool> AnnotateStaticDataSectionPrefix(
+ "memprof-annotate-static-data-prefix", cl::init(false), cl::Hidden,
+ cl::desc("If true, annotate the static data section prefix"));
+
// Matching statistics
STATISTIC(NumOfMemProfMissing, "Number of functions without memory profile.");
STATISTIC(NumOfMemProfMismatch,
@@ -90,6 +95,14 @@ STATISTIC(NumOfMemProfMatchedAllocs,
"Number of matched memory profile allocs.");
STATISTIC(NumOfMemProfMatchedCallSites,
"Number of matched memory profile callsites.");
+STATISTIC(NumOfMemProfHotGlobalVars,
+ "Number of global vars annotated with 'hot' section prefix.");
+STATISTIC(NumOfMemProfColdGlobalVars,
+ "Number of global vars annotated with 'unlikely' section prefix.");
+STATISTIC(NumOfMemProfUnknownGlobalVars,
+ "Number of global vars with unknown hotness (no section prefix).");
+STATISTIC(NumOfMemProfExplicitSectionGlobalVars,
+ "Number of global vars with user-specified section (not annotated).");
static void addCallsiteMetadata(Instruction &I,
ArrayRef<uint64_t> InlinedCallStack,
@@ -674,11 +687,12 @@ MemProfUsePass::MemProfUsePass(std::string MemoryProfileFile,
}
PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
- // Return immediately if the module doesn't contain any function.
- if (M.empty())
+ // Return immediately if the module doesn't contain any function or global
+ // variables.
+ if (M.empty() && M.globals().empty())
return PreservedAnalyses::all();
- LLVM_DEBUG(dbgs() << "Read in memory profile:");
+ LLVM_DEBUG(dbgs() << "Read in memory profile:\n");
auto &Ctx = M.getContext();
auto ReaderOrErr = IndexedInstrProfReader::create(MemoryProfileFileName, *FS);
if (Error E = ReaderOrErr.takeError()) {
@@ -703,6 +717,14 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
return PreservedAnalyses::all();
}
+ const bool Changed =
+ annotateGlobalVariables(M, MemProfReader->getDataAccessProfileData());
+
+ // If the module doesn't contain any function, return after we process all
+ // global variables.
+ if (M.empty())
+ return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
+
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
TargetLibraryInfo &TLI = FAM.getResult<TargetLibraryAnalysis>(*M.begin());
@@ -752,3 +774,95 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
return PreservedAnalyses::none();
}
+
+// Returns true iff the global variable has custom section either by
+// __attribute__((section("name")))
+// (https://clang.llvm.org/docs/AttributeReference.html#section-declspec-allocate)
+// or #pragma clang section directives
+// (https://clang.llvm.org/docs/LanguageExtensions.html#specifying-section-names-for-global-objects-pragma-clang-section).
+static bool hasExplicitSectionName(const GlobalVariable &GVar) {
+ if (GVar.hasSection())
+ return true;
+
+ auto Attrs = GVar.getAttributes();
+ if (Attrs.hasAttribute("bss-section") || Attrs.hasAttribute("data-section") ||
+ Attrs.hasAttribute("relro-section") ||
+ Attrs.hasAttribute("rodata-section"))
+ return true;
+ return false;
+}
+
+bool MemProfUsePass::annotateGlobalVariables(
+ Module &M, const memprof::DataAccessProfData *DataAccessProf) {
+ if (!AnnotateStaticDataSectionPrefix || M.globals().empty())
+ return false;
+
+ if (!DataAccessProf) {
+ M.getContext().diagnose(DiagnosticInfoPGOProfile(
+ MemoryProfileFileName.data(),
+ StringRef("Data access profiles not found in memprof. Ignore "
+ "-memprof-annotate-static-data-prefix."),
+ DS_Warning));
+ return false;
+ }
+
+ bool Changed = false;
+ // Iterate all global variables in the module and annotate them based on
+ // data access profiles. Note it's up to the linker to decide how to map input
+ // sections to output sections, and one conservative practice is to map
+ // unlikely-prefixed ones to unlikely output section, and map the rest
+ // (hot-prefixed or prefix-less) to the canonical output section.
+ for (GlobalVariable &GVar : M.globals()) {
+ assert(!GVar.getSectionPrefix().has_value() &&
+ "GVar shouldn't have section prefix yet");
+ if (GVar.isDeclarationForLinker())
+ continue;
+
+ if (hasExplicitSectionName(GVar)) {
+ ++NumOfMemProfExplicitSectionGlobalVars;
+ LLVM_DEBUG(dbgs() << "Global variable " << GVar.getName()
+ << " has explicit section name. Skip annotating.\n");
+ continue;
+ }
+
+ StringRef Name = GVar.getName();
+ // Skip string literals as their mangled names don't stay stable across
+ // binary releases.
+ // TODO: Track string content hash in the profiles and compute it inside the
+ // compiler to categeorize the hotness string literals.
+ if (Name.starts_with(".str")) {
+
+ LLVM_DEBUG(dbgs() << "Skip annotating string literal " << Name << "\n");
+ continue;
+ }
+
+ // DataAccessProfRecord's get* methods will canonicalize the name under the
+ // hood before looking it up, so optimizer doesn't need to do it.
+ std::optional<DataAccessProfRecord> Record =
+ DataAccessProf->getProfileRecord(Name);
+ // Annotate a global variable as hot if it has non-zero sampled count, and
+ // annotate it as cold if it's seen in the profiled binary
+ // file but doesn't have any access sample.
+ // For logging, optimization remark emitter requires a llvm::Function, but
+ // it's not well defined how to associate a global variable with a function.
+ // So we just print out the static data section prefix in LLVM_DEBUG.
+ if (Record && Record->AccessCount > 0) {
+ ++NumOfMemProfHotGlobalVars;
+ GVar.setSectionPrefix("hot");
+ Changed = true;
+ LLVM_DEBUG(dbgs() << "Global variable " << Name
+ << " is annotated as hot\n");
+ } else if (DataAccessProf->isKnownColdSymbol(Name)) {
+ ++NumOfMemProfColdGlobalVars;
+ GVar.setSectionPrefix("unlikely");
+ Changed = true;
+ LLVM_DEBUG(dbgs() << "Global variable " << Name
+ << " is annotated as unlikely\n");
+ } else {
+ ++NumOfMemProfUnknownGlobalVars;
+ LLVM_DEBUG(dbgs() << "Global variable " << Name << " is not annotated\n");
+ }
+ }
+
+ return Changed;
+}