22
22
#include " llvm/IR/Function.h"
23
23
#include " llvm/IR/IntrinsicInst.h"
24
24
#include " llvm/IR/Module.h"
25
+ #include " llvm/ProfileData/DataAccessProf.h"
25
26
#include " llvm/ProfileData/InstrProf.h"
26
27
#include " llvm/ProfileData/InstrProfReader.h"
27
28
#include " llvm/ProfileData/MemProfCommon.h"
@@ -75,6 +76,10 @@ static cl::opt<unsigned> MinMatchedColdBytePercent(
75
76
" memprof-matching-cold-threshold" , cl::init(100 ), cl::Hidden,
76
77
cl::desc(" Min percent of cold bytes matched to hint allocation cold" ));
77
78
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
+
78
83
// Matching statistics
79
84
STATISTIC (NumOfMemProfMissing, " Number of functions without memory profile." );
80
85
STATISTIC (NumOfMemProfMismatch,
@@ -90,6 +95,14 @@ STATISTIC(NumOfMemProfMatchedAllocs,
90
95
" Number of matched memory profile allocs." );
91
96
STATISTIC (NumOfMemProfMatchedCallSites,
92
97
" 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)." );
93
106
94
107
static void addCallsiteMetadata (Instruction &I,
95
108
ArrayRef<uint64_t > InlinedCallStack,
@@ -674,11 +687,12 @@ MemProfUsePass::MemProfUsePass(std::string MemoryProfileFile,
674
687
}
675
688
676
689
PreservedAnalyses 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 ())
679
693
return PreservedAnalyses::all ();
680
694
681
- LLVM_DEBUG (dbgs () << " Read in memory profile:" );
695
+ LLVM_DEBUG (dbgs () << " Read in memory profile:\n " );
682
696
auto &Ctx = M.getContext ();
683
697
auto ReaderOrErr = IndexedInstrProfReader::create (MemoryProfileFileName, *FS);
684
698
if (Error E = ReaderOrErr.takeError ()) {
@@ -703,6 +717,14 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
703
717
return PreservedAnalyses::all ();
704
718
}
705
719
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
+
706
728
auto &FAM = AM.getResult <FunctionAnalysisManagerModuleProxy>(M).getManager ();
707
729
708
730
TargetLibraryInfo &TLI = FAM.getResult <TargetLibraryAnalysis>(*M.begin ());
@@ -752,3 +774,95 @@ PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
752
774
753
775
return PreservedAnalyses::none ();
754
776
}
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