2222#include " llvm/IR/Function.h"
2323#include " llvm/IR/IntrinsicInst.h"
2424#include " llvm/IR/Module.h"
25+ #include " llvm/ProfileData/DataAccessProf.h"
2526#include " llvm/ProfileData/InstrProf.h"
2627#include " llvm/ProfileData/InstrProfReader.h"
2728#include " llvm/ProfileData/MemProfCommon.h"
@@ -75,6 +76,10 @@ static cl::opt<unsigned> MinMatchedColdBytePercent(
7576 " memprof-matching-cold-threshold" , cl::init(100 ), cl::Hidden,
7677 cl::desc(" Min percent of cold bytes matched to hint allocation cold" ));
7778
79+ static cl::opt<bool > AnnotateStaticDataSectionPrefix (
80+ " memprof-annotate-static-data-prefix" , cl::init(false ), cl::Hidden,
81+ cl::desc(" If true, annotate the static data section prefix" ));
82+
7883// Matching statistics
7984STATISTIC (NumOfMemProfMissing, " Number of functions without memory profile." );
8085STATISTIC (NumOfMemProfMismatch,
@@ -90,6 +95,14 @@ STATISTIC(NumOfMemProfMatchedAllocs,
9095 " Number of matched memory profile allocs." );
9196STATISTIC (NumOfMemProfMatchedCallSites,
9297 " Number of matched memory profile callsites." );
98+ STATISTIC (NumOfMemProfHotGlobalVars,
99+ " Number of global vars annotated with 'hot' section prefix." );
100+ STATISTIC (NumOfMemProfColdGlobalVars,
101+ " Number of global vars annotated with 'unlikely' section prefix." );
102+ STATISTIC (NumOfMemProfUnknownGlobalVars,
103+ " Number of global vars with unknown hotness (no section prefix)." );
104+ STATISTIC (NumOfMemProfExplicitSectionGlobalVars,
105+ " Number of global vars with user-specified section (not annotated)." );
93106
94107static void addCallsiteMetadata (Instruction &I,
95108 ArrayRef<uint64_t > InlinedCallStack,
@@ -674,11 +687,12 @@ MemProfUsePass::MemProfUsePass(std::string MemoryProfileFile,
674687}
675688
676689PreservedAnalyses MemProfUsePass::run (Module &M, ModuleAnalysisManager &AM) {
677- // Return immediately if the module doesn't contain any function.
678- if (M.empty ())
690+ // Return immediately if the module doesn't contain any function or global
691+ // variables.
692+ if (M.empty () && M.globals ().empty ())
679693 return PreservedAnalyses::all ();
680694
681- LLVM_DEBUG (dbgs () << " Read in memory profile:" );
695+ LLVM_DEBUG (dbgs () << " Read in memory profile:\n " );
682696 auto &Ctx = M.getContext ();
683697 auto ReaderOrErr = IndexedInstrProfReader::create (MemoryProfileFileName, *FS);
684698 if (Error E = ReaderOrErr.takeError ()) {
@@ -703,6 +717,14 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
703717 return PreservedAnalyses::all ();
704718 }
705719
720+ const bool Changed =
721+ annotateGlobalVariables (M, MemProfReader->getDataAccessProfileData ());
722+
723+ // If the module doesn't contain any function, return after we process all
724+ // global variables.
725+ if (M.empty ())
726+ return Changed ? PreservedAnalyses::none () : PreservedAnalyses::all ();
727+
706728 auto &FAM = AM.getResult <FunctionAnalysisManagerModuleProxy>(M).getManager ();
707729
708730 TargetLibraryInfo &TLI = FAM.getResult <TargetLibraryAnalysis>(*M.begin ());
@@ -752,3 +774,95 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
752774
753775 return PreservedAnalyses::none ();
754776}
777+
778+ // Returns true iff the global variable has custom section either by
779+ // __attribute__((section("name")))
780+ // (https://clang.llvm.org/docs/AttributeReference.html#section-declspec-allocate)
781+ // or #pragma clang section directives
782+ // (https://clang.llvm.org/docs/LanguageExtensions.html#specifying-section-names-for-global-objects-pragma-clang-section).
783+ static bool hasExplicitSectionName (const GlobalVariable &GVar) {
784+ if (GVar.hasSection ())
785+ return true ;
786+
787+ auto Attrs = GVar.getAttributes ();
788+ if (Attrs.hasAttribute (" bss-section" ) || Attrs.hasAttribute (" data-section" ) ||
789+ Attrs.hasAttribute (" relro-section" ) ||
790+ Attrs.hasAttribute (" rodata-section" ))
791+ return true ;
792+ return false ;
793+ }
794+
795+ bool MemProfUsePass::annotateGlobalVariables (
796+ Module &M, const memprof::DataAccessProfData *DataAccessProf) {
797+ if (!AnnotateStaticDataSectionPrefix || M.globals ().empty ())
798+ return false ;
799+
800+ if (!DataAccessProf) {
801+ M.getContext ().diagnose (DiagnosticInfoPGOProfile (
802+ MemoryProfileFileName.data (),
803+ StringRef (" Data access profiles not found in memprof. Ignore "
804+ " -memprof-annotate-static-data-prefix." ),
805+ DS_Warning));
806+ return false ;
807+ }
808+
809+ bool Changed = false ;
810+ // Iterate all global variables in the module and annotate them based on
811+ // data access profiles. Note it's up to the linker to decide how to map input
812+ // sections to output sections, and one conservative practice is to map
813+ // unlikely-prefixed ones to unlikely output section, and map the rest
814+ // (hot-prefixed or prefix-less) to the canonical output section.
815+ for (GlobalVariable &GVar : M.globals ()) {
816+ assert (!GVar.getSectionPrefix ().has_value () &&
817+ " GVar shouldn't have section prefix yet" );
818+ if (GVar.isDeclarationForLinker ())
819+ continue ;
820+
821+ if (hasExplicitSectionName (GVar)) {
822+ ++NumOfMemProfExplicitSectionGlobalVars;
823+ LLVM_DEBUG (dbgs () << " Global variable " << GVar.getName ()
824+ << " has explicit section name. Skip annotating.\n " );
825+ continue ;
826+ }
827+
828+ StringRef Name = GVar.getName ();
829+ // Skip string literals as their mangled names don't stay stable across
830+ // binary releases.
831+ // TODO: Track string content hash in the profiles and compute it inside the
832+ // compiler to categeorize the hotness string literals.
833+ if (Name.starts_with (" .str" )) {
834+
835+ LLVM_DEBUG (dbgs () << " Skip annotating string literal " << Name << " \n " );
836+ continue ;
837+ }
838+
839+ // DataAccessProfRecord's get* methods will canonicalize the name under the
840+ // hood before looking it up, so optimizer doesn't need to do it.
841+ std::optional<DataAccessProfRecord> Record =
842+ DataAccessProf->getProfileRecord (Name);
843+ // Annotate a global variable as hot if it has non-zero sampled count, and
844+ // annotate it as cold if it's seen in the profiled binary
845+ // file but doesn't have any access sample.
846+ // For logging, optimization remark emitter requires a llvm::Function, but
847+ // it's not well defined how to associate a global variable with a function.
848+ // So we just print out the static data section prefix in LLVM_DEBUG.
849+ if (Record && Record->AccessCount > 0 ) {
850+ ++NumOfMemProfHotGlobalVars;
851+ GVar.setSectionPrefix (" hot" );
852+ Changed = true ;
853+ LLVM_DEBUG (dbgs () << " Global variable " << Name
854+ << " is annotated as hot\n " );
855+ } else if (DataAccessProf->isKnownColdSymbol (Name)) {
856+ ++NumOfMemProfColdGlobalVars;
857+ GVar.setSectionPrefix (" unlikely" );
858+ Changed = true ;
859+ LLVM_DEBUG (dbgs () << " Global variable " << Name
860+ << " is annotated as unlikely\n " );
861+ } else {
862+ ++NumOfMemProfUnknownGlobalVars;
863+ LLVM_DEBUG (dbgs () << " Global variable " << Name << " is not annotated\n " );
864+ }
865+ }
866+
867+ return Changed;
868+ }
0 commit comments