[llvm] [LTO] Hash consumed CGData in the ThinLTO cache key (PR #214161)
Karim Alweheshy via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 5 01:53:45 PDT 2026
https://github.com/karim-alweheshy created https://github.com/llvm/llvm-project/pull/214161
## Summary
- Include the contents of consumed CGData in the ThinLTO cache key.
- Add a regression that changes CGData contents at a stable path and compares
shared-cache output with a fresh-cache control.
## Problem
`-codegen-data-use-path` is carried in `MllvmArgs`, so its path is part of the
ThinLTO cache key, but the file contents are not. Replacing a CGData file in
place can therefore reuse objects generated from its previous function map.
In the reduced negative control, the first map enabled Global Function Merging
for a pair of functions. Replacing that map at the same path with a different
valid map still returned cached objects containing the old `.Tgm` functions.
The same second map with a fresh cache correctly produced unmerged objects.
## Fix
Expose the selected CGData input path and hash its contents alongside the other
code-generation inputs in `computeLTOCacheKey`. The path remains represented by
`MllvmArgs`; the added content identity distinguishes in-place updates.
## Testing
- Verified the stale shared-cache reuse on unpatched current `main`.
- Compiled the changed CGData and LTO translation units from current `main`.
- Ran the focused regression with patched tools; the shared-cache result after
the content change is byte-identical to the fresh-cache control and no longer
contains the stale merge result.
- `git diff --check`.
>From 06be6c50af365980916e41541e72a20c672b9f70 Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at gmail.com>
Date: Wed, 5 Aug 2026 10:52:08 +0200
Subject: [PATCH] [LTO] Hash consumed CGData in the ThinLTO cache key
---
llvm/include/llvm/CGData/CodeGenData.h | 3 +
llvm/lib/CGData/CodeGenData.cpp | 2 +
llvm/lib/LTO/LTO.cpp | 6 +
llvm/test/ThinLTO/AArch64/cgdata-cache.ll | 129 ++++++++++++++++++++++
4 files changed, 140 insertions(+)
create mode 100644 llvm/test/ThinLTO/AArch64/cgdata-cache.ll
diff --git a/llvm/include/llvm/CGData/CodeGenData.h b/llvm/include/llvm/CGData/CodeGenData.h
index e44497a408245..55ba664233109 100644
--- a/llvm/include/llvm/CGData/CodeGenData.h
+++ b/llvm/include/llvm/CGData/CodeGenData.h
@@ -169,6 +169,9 @@ class CodeGenData {
namespace cgdata {
+/// Return the path of the CGData file selected for consumption.
+LLVM_ABI StringRef getCodeGenDataUsePath();
+
inline bool hasOutlinedHashTree() {
return CodeGenData::getInstance().hasOutlinedHashTree();
}
diff --git a/llvm/lib/CGData/CodeGenData.cpp b/llvm/lib/CGData/CodeGenData.cpp
index 7900dc7653c03..d981f30c20ad1 100644
--- a/llvm/lib/CGData/CodeGenData.cpp
+++ b/llvm/lib/CGData/CodeGenData.cpp
@@ -32,6 +32,8 @@ static cl::opt<std::string>
CodeGenDataUsePath("codegen-data-use-path", cl::init(""), cl::Hidden,
cl::desc("File path to where .cgdata file is read"));
+StringRef llvm::cgdata::getCodeGenDataUsePath() { return CodeGenDataUsePath; }
+
namespace llvm {
cl::opt<bool> CodeGenDataThinLTOTwoRounds(
"codegen-data-thinlto-two-rounds", cl::init(false), cl::Hidden,
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index c8c5b0880819a..64f6d0cba86bf 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -194,6 +194,12 @@ std::string llvm::computeLTOCacheKey(
AddUnsigned(-1);
for (const auto &S : Conf.MllvmArgs)
AddString(S);
+ if (StringRef CodeGenDataPath = cgdata::getCodeGenDataUsePath();
+ !CodeGenDataPath.empty()) {
+ auto FileOrErr = MemoryBuffer::getFile(CodeGenDataPath);
+ if (FileOrErr)
+ Hasher.update(FileOrErr.get()->getBuffer());
+ }
AddUnsigned(static_cast<int>(Conf.CGOptLevel));
AddUnsigned(static_cast<int>(Conf.CGFileType));
AddUnsigned(Conf.OptLevel);
diff --git a/llvm/test/ThinLTO/AArch64/cgdata-cache.ll b/llvm/test/ThinLTO/AArch64/cgdata-cache.ll
new file mode 100644
index 0000000000000..15aec1c5c1109
--- /dev/null
+++ b/llvm/test/ThinLTO/AArch64/cgdata-cache.ll
@@ -0,0 +1,129 @@
+; Verify that the ThinLTO cache key includes the contents of consumed CGData.
+;
+; RUN: rm -rf %t; split-file %s %t
+; RUN: opt -module-summary -module-hash %t/foo.ll -o %t/foo.bc
+; RUN: opt -module-summary -module-hash %t/goo.ll -o %t/goo.bc
+; RUN: opt -module-summary -module-hash %t/x.ll -o %t/x.bc
+; RUN: opt -module-summary -module-hash %t/y.ll -o %t/y.bc
+;
+; Generate two valid CGData files containing different function maps.
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN: -codegen-data-generate=true %t/foo.bc %t/goo.bc -o %t/a-write \
+; RUN: -r %t/foo.bc,_f1,px -r %t/goo.bc,_f2,px \
+; RUN: -r %t/foo.bc,_g,l -r %t/foo.bc,_g1,l \
+; RUN: -r %t/goo.bc,_g,l -r %t/goo.bc,_g2,l
+; RUN: llvm-cgdata --merge -o %t/a.cgdata %t/a-write.1 %t/a-write.2
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN: -codegen-data-generate=true %t/x.bc %t/y.bc -o %t/b-write \
+; RUN: -r %t/x.bc,_x1,px -r %t/y.bc,_x2,px \
+; RUN: -r %t/x.bc,_h,l -r %t/x.bc,_h1,l \
+; RUN: -r %t/y.bc,_h,l -r %t/y.bc,_h2,l
+; RUN: llvm-cgdata --merge -o %t/b.cgdata %t/b-write.1 %t/b-write.2
+;
+; Populate a cache while the stable path contains the map for f1/f2.
+; RUN: cp %t/a.cgdata %t/active.cgdata
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN: -codegen-data-use-path=%t/active.cgdata -cache-dir=%t/cache \
+; RUN: %t/foo.bc %t/goo.bc -o %t/use-a \
+; RUN: -r %t/foo.bc,_f1,px -r %t/goo.bc,_f2,px \
+; RUN: -r %t/foo.bc,_g,l -r %t/foo.bc,_g1,l \
+; RUN: -r %t/goo.bc,_g,l -r %t/goo.bc,_g2,l
+; RUN: llvm-nm %t/use-a.1 | FileCheck %s --check-prefix=MERGED
+;
+; Change only the contents at that path. The shared-cache result must match a
+; fresh-cache run and must not reuse the f1/f2 merge result.
+; RUN: cp %t/b.cgdata %t/active.cgdata
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN: -codegen-data-use-path=%t/active.cgdata -cache-dir=%t/cache \
+; RUN: %t/foo.bc %t/goo.bc -o %t/use-b-shared \
+; RUN: -r %t/foo.bc,_f1,px -r %t/goo.bc,_f2,px \
+; RUN: -r %t/foo.bc,_g,l -r %t/foo.bc,_g1,l \
+; RUN: -r %t/goo.bc,_g,l -r %t/goo.bc,_g2,l
+; RUN: llvm-lto2 run -enable-global-merge-func=true \
+; RUN: -codegen-data-use-path=%t/active.cgdata -cache-dir=%t/fresh-cache \
+; RUN: %t/foo.bc %t/goo.bc -o %t/use-b-fresh \
+; RUN: -r %t/foo.bc,_f1,px -r %t/goo.bc,_f2,px \
+; RUN: -r %t/foo.bc,_g,l -r %t/foo.bc,_g1,l \
+; RUN: -r %t/goo.bc,_g,l -r %t/goo.bc,_g2,l
+; RUN: llvm-nm %t/use-b-shared.1 | FileCheck %s --check-prefix=UNMERGED
+; RUN: cmp %t/use-b-shared.1 %t/use-b-fresh.1
+; RUN: cmp %t/use-b-shared.2 %t/use-b-fresh.2
+;
+; MERGED: _f1.Tgm
+; UNMERGED-NOT: _f1.Tgm
+;
+;--- foo.ll
+source_filename = "foo.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios12.0.0"
+
+ at g = external local_unnamed_addr global [0 x i32], align 4
+ at g1 = external global i32, align 4
+
+define i32 @f1(i32 %a) {
+entry:
+ %idx = sext i32 %a to i64
+ %p = getelementptr inbounds [0 x i32], ptr @g, i64 0, i64 %idx
+ %v = load i32, ptr %p, align 4
+ %c = load volatile i32, ptr @g1, align 4
+ %m = mul nsw i32 %c, %v
+ %r = add nsw i32 %m, 1
+ ret i32 %r
+}
+
+;--- goo.ll
+source_filename = "goo.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios12.0.0"
+
+ at g = external local_unnamed_addr global [0 x i32], align 4
+ at g2 = external global i32, align 4
+
+define i32 @f2(i32 %a) {
+entry:
+ %idx = sext i32 %a to i64
+ %p = getelementptr inbounds [0 x i32], ptr @g, i64 0, i64 %idx
+ %v = load i32, ptr %p, align 4
+ %c = load volatile i32, ptr @g2, align 4
+ %m = mul nsw i32 %c, %v
+ %r = add nsw i32 %m, 1
+ ret i32 %r
+}
+
+;--- x.ll
+source_filename = "x.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios12.0.0"
+
+ at h = external local_unnamed_addr global [0 x i32], align 4
+ at h1 = external global i32, align 4
+
+define i32 @x1(i32 %a) {
+entry:
+ %idx = sext i32 %a to i64
+ %p = getelementptr inbounds [0 x i32], ptr @h, i64 0, i64 %idx
+ %v = load i32, ptr %p, align 4
+ %c = load volatile i32, ptr @h1, align 4
+ %m = mul nsw i32 %c, %v
+ %r = sub nsw i32 %m, 1
+ ret i32 %r
+}
+
+;--- y.ll
+source_filename = "y.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios12.0.0"
+
+ at h = external local_unnamed_addr global [0 x i32], align 4
+ at h2 = external global i32, align 4
+
+define i32 @x2(i32 %a) {
+entry:
+ %idx = sext i32 %a to i64
+ %p = getelementptr inbounds [0 x i32], ptr @h, i64 0, i64 %idx
+ %v = load i32, ptr %p, align 4
+ %c = load volatile i32, ptr @h2, align 4
+ %m = mul nsw i32 %c, %v
+ %r = sub nsw i32 %m, 1
+ ret i32 %r
+}
More information about the llvm-commits
mailing list