[llvm] [LTO] Hash CSIR configuration in the ThinLTO cache key (PR #214162)

Karim Alweheshy via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 02:03:34 PDT 2026


https://github.com/karim-alweheshy created https://github.com/llvm/llvm-project/pull/214162

## Summary

- Include CSIR instrumentation mode and its output path in the ThinLTO cache
  key.
- Include consumed CSIR profile and remapping contents in the cache key.
- Add shared-cache versus fresh-cache regressions for both generation and use.

## Problem

`Config::RunCSIRInstr` and `Config::CSIRProfile` affect ThinLTO backend output,
but neither currently contributes to `computeLTOCacheKey`.

Two reduced current-`main` controls reproduce stale reuse:

- A cache populated without CS instrumentation returns the same uninstrumented
  object when CS instrumentation is then enabled. A fresh cache emits the
  expected profile-runtime references and data.
- Replacing a consumed CSIR profile at a stable path returns the object built
  with the previous profile. A fresh cache reflects the new branch counts.

## Fix

Hash the CS instrumentation mode and, while generating, the configured profile
output path because it is embedded in the instrumented object. While consuming
a CSIR profile, hash the profile contents and optional remapping contents, as is
already done for sample profiles.

## Testing

- Verified both stale-reuse cases on unpatched current `main`.
- Compiled the changed LTO translation unit from current `main`.
- Ran the focused patched controls:
  - shared-cache instrumentation output is byte-identical to the instrumented
    fresh-cache output and contains the profile runtime reference;
  - after an in-place CSIR profile change, shared-cache output is byte-identical
    to the fresh-cache output and distinct from the first profile's output.
- `git diff --check`.


>From 065c867d9cef08108bc61c7ae686abdf51557fa0 Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at gmail.com>
Date: Wed, 5 Aug 2026 11:02:28 +0200
Subject: [PATCH] [LTO] Hash CSIR configuration in the ThinLTO cache key

---
 llvm/lib/LTO/LTO.cpp                     | 16 +++++
 llvm/test/ThinLTO/AArch64/cspgo-cache.ll | 88 ++++++++++++++++++++++++
 2 files changed, 104 insertions(+)
 create mode 100644 llvm/test/ThinLTO/AArch64/cspgo-cache.ll

diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index c8c5b0880819a..51b6976115a1b 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -204,6 +204,9 @@ std::string llvm::computeLTOCacheKey(
   AddString(Conf.DefaultTriple);
   AddString(Conf.DwoDir);
   AddUint8(Conf.Dtlto);
+  AddUint8(Conf.RunCSIRInstr);
+  if (Conf.RunCSIRInstr)
+    AddString(Conf.CSIRProfile);
 
   // Include the hash for the current module
   auto ModHash = Index.getModuleHash(ModuleID);
@@ -388,6 +391,19 @@ std::string llvm::computeLTOCacheKey(
     }
   }
 
+  if (!Conf.RunCSIRInstr && !Conf.CSIRProfile.empty()) {
+    auto FileOrErr = MemoryBuffer::getFile(Conf.CSIRProfile);
+    if (FileOrErr) {
+      Hasher.update(FileOrErr.get()->getBuffer());
+
+      if (!Conf.ProfileRemapping.empty()) {
+        FileOrErr = MemoryBuffer::getFile(Conf.ProfileRemapping);
+        if (FileOrErr)
+          Hasher.update(FileOrErr.get()->getBuffer());
+      }
+    }
+  }
+
   return toHex(Hasher.result());
 }
 
diff --git a/llvm/test/ThinLTO/AArch64/cspgo-cache.ll b/llvm/test/ThinLTO/AArch64/cspgo-cache.ll
new file mode 100644
index 0000000000000..299b0eebb5fc0
--- /dev/null
+++ b/llvm/test/ThinLTO/AArch64/cspgo-cache.ll
@@ -0,0 +1,88 @@
+; Verify that the ThinLTO cache key includes CSIR instrumentation mode and
+; profile inputs.
+;
+; RUN: rm -rf %t; split-file %s %t
+; RUN: opt -module-summary -module-hash %t/input.ll -o %t/input.bc
+;
+; Changing only CS instrumentation mode must not reuse an uninstrumented
+; object. The shared-cache result must match a fresh-cache control.
+; RUN: llvm-lto2 run -cache-dir=%t/gen-cache %t/input.bc -o %t/gen-off \
+; RUN:   -r %t/input.bc,_foo,px -r %t/input.bc,_bar,
+; RUN: llvm-lto2 run -cache-dir=%t/gen-cache -lto-cspgo-gen \
+; RUN:   -lto-cspgo-profile-file=%t/default_%m.profraw \
+; RUN:   %t/input.bc -o %t/gen-on-shared \
+; RUN:   -r %t/input.bc,_foo,px -r %t/input.bc,_bar,
+; RUN: llvm-lto2 run -cache-dir=%t/gen-fresh-cache -lto-cspgo-gen \
+; RUN:   -lto-cspgo-profile-file=%t/default_%m.profraw \
+; RUN:   %t/input.bc -o %t/gen-on-fresh \
+; RUN:   -r %t/input.bc,_foo,px -r %t/input.bc,_bar,
+; RUN: llvm-nm %t/gen-on-shared.1 | FileCheck %s --check-prefix=INSTRUMENTED
+; RUN: cmp %t/gen-on-shared.1 %t/gen-on-fresh.1
+;
+; INSTRUMENTED: __llvm_profile_runtime
+;
+; Changing only the contents of a CSIR profile at a stable path must likewise
+; invalidate the cache. The two profiles reverse the hot loop edge.
+; RUN: llvm-profdata merge %t/hot-loop.proftext -o %t/active.profdata
+; RUN: llvm-lto2 run -cache-dir=%t/use-cache -pgo-instrument-entry=false \
+; RUN:   -lto-cspgo-profile-file=%t/active.profdata \
+; RUN:   %t/input.bc -o %t/use-hot \
+; RUN:   -r %t/input.bc,_foo,px -r %t/input.bc,_bar,
+; RUN: llvm-profdata merge %t/cold-loop.proftext -o %t/active.profdata
+; RUN: llvm-lto2 run -cache-dir=%t/use-cache -pgo-instrument-entry=false \
+; RUN:   -lto-cspgo-profile-file=%t/active.profdata \
+; RUN:   %t/input.bc -o %t/use-cold-shared \
+; RUN:   -r %t/input.bc,_foo,px -r %t/input.bc,_bar,
+; RUN: llvm-lto2 run -cache-dir=%t/use-fresh-cache \
+; RUN:   -pgo-instrument-entry=false \
+; RUN:   -lto-cspgo-profile-file=%t/active.profdata \
+; RUN:   %t/input.bc -o %t/use-cold-fresh \
+; RUN:   -r %t/input.bc,_foo,px -r %t/input.bc,_bar,
+; RUN: cmp %t/use-cold-shared.1 %t/use-cold-fresh.1
+; RUN: not cmp %t/use-hot.1 %t/use-cold-shared.1
+;
+;--- input.ll
+source_filename = "cspgo.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+target triple = "arm64-apple-ios12.0.0"
+
+define void @foo() {
+entry:
+  br label %for.body
+
+for.body:
+  %i = phi i32 [ 0, %entry ], [ %next, %for.body ]
+  call void @bar(i32 %i)
+  %odd = or i32 %i, 1
+  call void @bar(i32 %odd)
+  %next = add nuw nsw i32 %i, 2
+  %cmp = icmp ult i32 %next, 200000
+  br i1 %cmp, label %for.body, label %for.end
+
+for.end:
+  ret void
+}
+
+declare void @bar(i32)
+
+;--- hot-loop.proftext
+:csir
+foo
+# Func Hash:
+1936928561113927580
+# Num Counters:
+2
+# Counter Values:
+100000
+1
+
+;--- cold-loop.proftext
+:csir
+foo
+# Func Hash:
+1936928561113927580
+# Num Counters:
+2
+# Counter Values:
+1
+100000



More information about the llvm-commits mailing list