[llvm] [StaticDataLayout][MemProf] Annotate string literal hotness by making use of data access profiles. (PR #178333)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 27 17:49:12 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-pgo

Author: Mingming Liu (mingmingl-llvm)

<details>
<summary>Changes</summary>

The change is gated under a new option `memprof-annotate-string-literal-section-prefix` so we can flag-gate it for rollout purposes.

A follow-up PR https://github.com/llvm/llvm-project/pull/178336/changes (test case working-in-progress) updates the codegen pass to reconcile the hotness similar to the reconciliation for other global variables.

---
Full diff: https://github.com/llvm/llvm-project/pull/178333.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Instrumentation/MemProfUse.cpp (+54-1) 
- (modified) llvm/test/Transforms/PGOProfile/data-access-profile.ll (+73-26) 


``````````diff
diff --git a/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp b/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
index 748c4cb711027..29ec4c297ed7f 100644
--- a/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemProfUse.cpp
@@ -92,6 +92,14 @@ 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"));
 
+// FIXME: This option is added for incremental rollout purposes.
+// After the option, string literal partitioning should be implied by
+// AnnotateStaticDataSectionPrefix above and this option should be cleaned up.
+static cl::opt<bool> AnnotateStringLiteralSectionPrefix(
+    "memprof-annotate-string-literal-section-prefix", cl::init(false),
+    cl::Hidden,
+    cl::desc("If true, annotate the string literal data section prefix"));
+
 // Matching statistics
 STATISTIC(NumOfMemProfMissing, "Number of functions without memory profile.");
 STATISTIC(NumOfMemProfMismatch,
@@ -233,6 +241,20 @@ static void HandleUnsupportedAnnotationKinds(GlobalVariable &GVar,
                     << Reason << ".\n");
 }
 
+// Computes the LLVM version of MD5 hash for the string content of a string
+// literal.
+static std::optional<uint64_t>
+getStringContentHash(const GlobalVariable &GVar) {
+  auto *Initializer = GVar.getInitializer();
+  if (!Initializer)
+    return std::nullopt;
+  if (auto *C = dyn_cast<ConstantDataSequential>(Initializer))
+    if (C->isString()) {
+      return llvm::MD5Hash(C->getAsString());
+    }
+  return std::nullopt;
+}
+
 // Structure for tracking info about matched allocation contexts for use with
 // -memprof-print-match-info and -memprof-print-matched-alloc-stack.
 struct AllocMatchInfo {
@@ -937,7 +959,38 @@ bool MemProfUsePass::annotateGlobalVariables(
     // 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");
+      if (!AnnotateStringLiteralSectionPrefix) {
+        LLVM_DEBUG(
+            dbgs() << "String literal annotation is off. Skip annotating "
+                   << Name << "\n");
+        continue;
+      }
+      std::optional<uint64_t> Hash = getStringContentHash(GVar);
+      if (!Hash) {
+        LLVM_DEBUG(dbgs() << "Cannot compute content hash for string literal "
+                          << Name << "\n");
+        continue;
+      }
+      const uint64_t HashValue = Hash.value();
+      std::optional<memprof::DataAccessProfRecord> Record =
+          DataAccessProf->getProfileRecord(HashValue);
+      if (Record && Record->AccessCount > 0) {
+        ++NumOfMemProfHotGlobalVars;
+        Changed |= GVar.setSectionPrefix("hot");
+        LLVM_DEBUG(dbgs() << "Global variable " << Name
+                          << " is annotated as hot\n");
+      } else if (DataAccessProf->isKnownColdSymbol(HashValue)) {
+        ++NumOfMemProfColdGlobalVars;
+        Changed |= 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");
+      }
+
       continue;
     }
 
diff --git a/llvm/test/Transforms/PGOProfile/data-access-profile.ll b/llvm/test/Transforms/PGOProfile/data-access-profile.ll
index 205184bdd7156..3342f460f27be 100644
--- a/llvm/test/Transforms/PGOProfile/data-access-profile.ll
+++ b/llvm/test/Transforms/PGOProfile/data-access-profile.ll
@@ -1,5 +1,5 @@
 ; REQUIRES: asserts
-; asserts are required for -debug-only=<pass-name>
+; asserts are required for -debug-only=<pass-name> and -stats
 
 ; RUN: rm -rf %t && split-file %s %t && cd %t
 
@@ -10,11 +10,21 @@
 ;; Run optimizer pass on an IR module without IR functions, and test that global
 ;; variables in the module could be annotated (i.e., no early return),
 ; RUN: opt -passes='memprof-use<profile-filename=memprof.profdata>' -memprof-annotate-static-data-prefix \
-; RUN: -debug-only=memprof -stats -S funcless-module.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOG,IR,STAT
+; RUN: -debug-only=memprof -stats -S funcless-module.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOGCOMMON,LOG,IRCOMMON,IR,STAT
+
+;; Add '-memprof-annotate-string-literal-section-prefix' to RUN command above.
+; RUN: opt -passes='memprof-use<profile-filename=memprof.profdata>' -memprof-annotate-static-data-prefix \
+; RUN: -memprof-annotate-string-literal-section-prefix \
+; RUN: -debug-only=memprof -stats -S funcless-module.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOGCOMMON,LOGSTR,IRCOMMON,IRSTR,STRSTAT
 
 ;; Run optimizer pass on the IR, and check the section prefix.
 ; RUN: opt -passes='memprof-use<profile-filename=memprof.profdata>' -memprof-annotate-static-data-prefix \
-; RUN: -debug-only=memprof -stats -S input.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOG,IR,STAT
+; RUN: -debug-only=memprof -stats -S input.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOGCOMMON,LOG,IR,STAT
+
+;; Add '-memprof-annotate-string-literal-section-prefix' to RUN command above.
+; RUN: opt -passes='memprof-use<profile-filename=memprof.profdata>' -memprof-annotate-static-data-prefix \
+; RUN: -memprof-annotate-string-literal-section-prefix \
+; RUN: -debug-only=memprof -stats -S input.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOGCOMMON,LOGSTR,IRSTR,STRSTAT
 
 ;; Run memprof without providing memprof data. Test that IR has module flag
 ;; `EnableDataAccessProf` as 0.
@@ -26,40 +36,65 @@
 ; RUN: opt -passes='memprof-use<profile-filename=memprof.profdata>' \
 ; RUN: -debug-only=memprof -stats -S input.ll -o - | FileCheck %s --check-prefix=FLAGLESS --implicit-check-not="section_prefix"
 
-; LOG: Skip annotating string literal .str
-; LOG: Global variable var1 is annotated as hot
-; LOG: Global variable var2.llvm.125 is annotated as hot
-; LOG: Global variable bar is not annotated
-; LOG: Global variable foo is annotated as unlikely
-; LOG: Skip annotation for var3 due to explicit section name.
-; LOG: Skip annotation for var4 due to explicit section name.
-; LOG: Skip annotation for llvm.fake_var due to name starts with `llvm.`.
-; LOG: Skip annotation for qux due to linker declaration.
+; LOG: String literal annotation is off. Skip annotating .str
+; LOGSTR: Global variable .str is annotated as hot
+
+;; Common log lines
+; LOGCOMMON: Global variable var1 is annotated as hot
+; LOGCOMMON: Global variable var2.llvm.125 is annotated as hot
+; LOGCOMMON: Global variable bar is not annotated
+; LOGCOMMON: Global variable foo is annotated as unlikely
+; LOGCOMMON: Skip annotation for var3 due to explicit section name.
+; LOGCOMMON: Skip annotation for var4 due to explicit section name.
+; LOGCOMMON: Skip annotation for llvm.fake_var due to name starts with `llvm.`.
+; LOGCOMMON: Skip annotation for qux due to linker declaration.
+
+; LOG: String literal annotation is off. Skip annotating .str.llvm.98765
+; LOG: String literal annotation is off. Skip annotating .str.2
+
+; LOGSTR: Global variable .str.llvm.98765 is annotated as unlikely
+; LOGSTR: Global variable .str.2 is not annotated
+
+
 
 ;; String literals are not annotated.
 ; IR: @.str = unnamed_addr constant [5 x i8] c"abcde"
 ; IR-NOT: section_prefix
-; IR: @var1 = global i32 123, !section_prefix !0
+; IR-SAME: {{$}}
+
+;; .str is hot
+; IRSTR: @.str = unnamed_addr constant [5 x i8] c"abcde", !section_prefix !0
+
+; IRCOMMON: @var1 = global i32 123, !section_prefix !0
 
 ;; @var.llvm.125 will be canonicalized to @var2 for profile look-up.
-; IR-NEXT: @var2.llvm.125 = global i64 0, !section_prefix !0
+; IRCOMMON-NEXT: @var2.llvm.125 = global i64 0, !section_prefix !0
 
 ;; @bar is not seen in hot symbol or known symbol set, so it won't get a section
 ;; prefix. Test this by testing that there is no section_prefix between @bar and
 ;; @foo.
-; IR-NEXT: @bar = global i16 3
-; IR-NOT: !section_prefix
+; IRCOMMON-NEXT: @bar = global i16 3
+; IRCOMMON-NOT: !section_prefix
+; IRCOMMON-SAME: {{$}}
 
 ;; @foo is unlikely.
-; IR-NEXT: @foo = global i8 2, !section_prefix !1
+; IRCOMMON-NEXT: @foo = global i8 2, !section_prefix !1
 
-; IR-NEXT: @var3 = constant [2 x i32] [i32 12345, i32 6789], section "sec1"
-; IR-NEXT: @var4 = constant [1 x i64] [i64 98765] #0
+; IRCOMMON-NEXT: @var3 = constant [2 x i32] [i32 12345, i32 6789], section "sec1"
+; IRCOMMON-NEXT: @var4 = constant [1 x i64] [i64 98765] #0
 
-; IR: @llvm.fake_var = global i32 123
-; IR-NOT: !section_prefix
-; IR: @qux = external global i64
-; IR-NOT: !section_prefix
+; IRCOMMON: @llvm.fake_var = global i32 123
+; IRCOMMON-NOT: !section_prefix
+; IRCOMMON-SAME: {{$}}
+; IRCOMMON: @qux = external global i64
+; IRCOMMON-NOT: !section_prefix
+; IRCOMMON-SAME: {{$}}
+
+;; @.str.llvm.98765 is unlikely and @.str.2 has no section prefix.
+; IRSTR: @.str.llvm.98765 = constant [5 x i8] c"Joins", align 1, !section_prefix !1
+; IRSTR: @.str.2 = constant [15 x i8] c"*ptr == nullptr", align 1
+; IRSTR-NOT: section_prefix
+; IRSTR-SAME: {{$}}
 
 ; IR: attributes #0 = { "rodata-section"="sec2" }
 
@@ -75,6 +110,11 @@
 ; STAT: 2 memprof - Number of global vars annotated with 'hot' section prefix.
 ; STAT: 1 memprof - Number of global vars with unknown hotness (no section prefix).
 
+; STRSTAT: 2 memprof - Number of global vars annotated with 'unlikely' section prefix.
+; STRSTAT: 2 memprof - Number of global vars with user-specified section (not annotated).
+; STRSTAT: 3 memprof - Number of global vars annotated with 'hot' section prefix.
+; STRSTAT: 2 memprof - Number of global vars with unknown hotness (no section prefix).
+
 ;--- memprof.yaml
 ---
 DataAccessProfiles:
@@ -83,11 +123,14 @@ DataAccessProfiles:
       AccessCount:     1000
     - Symbol:          var2
       AccessCount:     5
-    - Hash:            101010
+      # 4211217683648370347 is the hash for 'abcde'
+    - Hash:            4211217683648370347
       AccessCount:     145
   KnownColdSymbols:
     - foo
-  KnownColdStrHashes: [ 999, 1001 ]
+  # 1698928196410683810 is the hash for 'Joins'
+  # 9184168289490324740 is the hash for 'ptr = nullptr'. This string doesn't exist in the IR.
+  KnownColdStrHashes: [ 1698928196410683810, 9184168289490324740 ]
 ...
 ;--- memprof-no-dap.yaml
 ---
@@ -114,13 +157,15 @@ target triple = "x86_64-unknown-linux-gnu"
 
 @.str = unnamed_addr constant [5 x i8] c"abcde"
 @var1 = global i32 123
- at var2.llvm.125 = global i64 0 
+ at var2.llvm.125 = global i64 0
 @bar = global i16 3
 @foo = global i8 2
 @var3 = constant [2 x i32][i32 12345, i32 6789], section "sec1"
 @var4 = constant [1 x i64][i64 98765] #0
 @llvm.fake_var = global i32 123
 @qux = external global i64
+ at .str.llvm.98765 = constant [5 x i8] c"Joins", align 1
+ at .str.2 = constant [15 x i8] c"*ptr == nullptr", align 1
 
 define i32 @func() {
   %a = load i32, ptr @var1
@@ -148,6 +193,8 @@ target triple = "x86_64-unknown-linux-gnu"
 @var4 = constant [1 x i64][i64 98765] #0
 @llvm.fake_var = global i32 123
 @qux = external global i64
+ at .str.llvm.98765 = constant [5 x i8] c"Joins", align 1
+ at .str.2 = constant [15 x i8] c"*ptr == nullptr", align 1
 
 
 attributes #0 = { "rodata-section"="sec2" }

``````````

</details>


https://github.com/llvm/llvm-project/pull/178333


More information about the llvm-commits mailing list