[llvm] [PGO] Refresh raw profile version during CS instrumentation (PR #214139)
Karim Alweheshy via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 4 23:46:08 PDT 2026
https://github.com/karim-alweheshy created https://github.com/llvm/llvm-project/pull/214139
## Summary
- Recompute the IR profile version when context-sensitive instrumentation runs
in the LTO backend.
- Refresh only the prevailing `__llvm_profile_raw_version` definition.
- Preserve non-prevailing copies as external declarations.
## Problem
CS instrumentation creates `__llvm_profile_raw_version` before LTO, then adds
the counters in an LTO backend pass. The backend deliberately does not create
another version variable. However, it also leaves the prevailing initializer
unchanged.
This can make the initializer stale when the frontend bitcode carries an older
raw format version or when the backend adds an instrumentation variant such as
temporal profiling. The runtime then sees a version value that does not
describe the records emitted by the backend.
## Fix
Factor the existing IR profile-version calculation into a helper. When the
instrumentation type is CSFDO, look up the existing version variable and
replace its initializer if this module owns the definition. If ThinLTO made
the symbol non-prevailing, leave its declaration untouched.
## Testing
- Added a two-module ThinLTO regression with a raw-version-10 frontend value
and raw-version-11 temporal CS backend instrumentation. The unpatched parent
retains the stale value; the patch emits the current raw, IR, CSIR, and
temporal bits.
- The same regression verifies that the non-prevailing module retains an
external declaration.
- `check-llvm-transforms-pgoprofile`: 159 passed, 16 unsupported.
- `ProfileDataTests`: 255 passed.
>From 6f2decc9f4c8247d801a1f37bc03de3cf39c0cf6 Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at gmail.com>
Date: Wed, 5 Aug 2026 08:44:56 +0200
Subject: [PATCH] [PGO] Refresh raw profile version during CS instrumentation
---
.../Instrumentation/PGOInstrumentation.cpp | 41 +++++++++++++++----
...thinlto_cspgo_raw_version_nonprevailing.ll | 7 ++++
.../PGOProfile/thinlto_cspgo_raw_version.ll | 33 +++++++++++++++
3 files changed, 73 insertions(+), 8 deletions(-)
create mode 100644 llvm/test/Transforms/PGOProfile/Inputs/thinlto_cspgo_raw_version_nonprevailing.ll
create mode 100644 llvm/test/Transforms/PGOProfile/thinlto_cspgo_raw_version.ll
diff --git a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
index 92cd9b33f2ef1..e17125e98cf49 100644
--- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
+++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
@@ -447,13 +447,8 @@ static const char *ValueProfKindDescr[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
-// Create a COMDAT variable INSTR_PROF_RAW_VERSION_VAR to make the runtime
-// aware this is an ir_level profile so it can set the version flag.
-static GlobalVariable *
-createIRLevelProfileFlagVar(Module &M,
- PGOInstrumentationType InstrumentationType) {
- const StringRef VarName(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
- Type *IntTy64 = Type::getInt64Ty(M.getContext());
+static uint64_t
+getIRLevelProfileVersion(PGOInstrumentationType InstrumentationType) {
uint64_t ProfileVersion = (INSTR_PROF_RAW_VERSION | VARIANT_MASK_IR_PROF);
if (InstrumentationType == PGOInstrumentationType::CSFDO)
ProfileVersion |= VARIANT_MASK_CSIR_PROF;
@@ -471,9 +466,21 @@ createIRLevelProfileFlagVar(Module &M,
ProfileVersion |= VARIANT_MASK_BYTE_COVERAGE;
if (PGOTemporalInstrumentation)
ProfileVersion |= VARIANT_MASK_TEMPORAL_PROF;
+ return ProfileVersion;
+}
+
+// Create a COMDAT variable INSTR_PROF_RAW_VERSION_VAR to make the runtime
+// aware this is an ir_level profile so it can set the version flag.
+static GlobalVariable *
+createIRLevelProfileFlagVar(Module &M,
+ PGOInstrumentationType InstrumentationType) {
+ const StringRef VarName(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
+ Type *IntTy64 = Type::getInt64Ty(M.getContext());
auto IRLevelVersionVariable = new GlobalVariable(
M, IntTy64, true, GlobalValue::WeakAnyLinkage,
- Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName);
+ Constant::getIntegerValue(
+ IntTy64, APInt(64, getIRLevelProfileVersion(InstrumentationType))),
+ VarName);
IRLevelVersionVariable->setVisibility(GlobalValue::HiddenVisibility);
Triple TT(M.getTargetTriple());
@@ -484,6 +491,22 @@ createIRLevelProfileFlagVar(Module &M,
return IRLevelVersionVariable;
}
+// CS instrumentation creates the raw-version variable before LTO. Refresh the
+// prevailing definition because the LTO backend may use a newer raw format or
+// add variant bits for instrumentation inserted only in the backend.
+static void
+updateIRLevelProfileFlagVar(Module &M,
+ PGOInstrumentationType InstrumentationType) {
+ const StringRef VarName(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
+ GlobalVariable *IRLevelVersionVariable = M.getNamedGlobal(VarName);
+ if (!IRLevelVersionVariable || IRLevelVersionVariable->isDeclaration())
+ return;
+
+ Type *IntTy64 = Type::getInt64Ty(M.getContext());
+ IRLevelVersionVariable->setInitializer(Constant::getIntegerValue(
+ IntTy64, APInt(64, getIRLevelProfileVersion(InstrumentationType))));
+}
+
namespace {
/// The select instruction visitor plays three roles specified
@@ -2019,6 +2042,8 @@ static bool InstrumentAllFunctions(
// (before LTO/ThinLTO linking) to create these variables.
if (InstrumentationType == PGOInstrumentationType::FDO)
createIRLevelProfileFlagVar(M, InstrumentationType);
+ else if (InstrumentationType == PGOInstrumentationType::CSFDO)
+ updateIRLevelProfileFlagVar(M, InstrumentationType);
Triple TT(M.getTargetTriple());
LLVMContext &Ctx = M.getContext();
diff --git a/llvm/test/Transforms/PGOProfile/Inputs/thinlto_cspgo_raw_version_nonprevailing.ll b/llvm/test/Transforms/PGOProfile/Inputs/thinlto_cspgo_raw_version_nonprevailing.ll
new file mode 100644
index 0000000000000..2e68b6a41a291
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/Inputs/thinlto_cspgo_raw_version_nonprevailing.ll
@@ -0,0 +1,7 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+$__llvm_profile_raw_version = comdat any
+
+ at __llvm_profile_raw_version = hidden constant i64 216172782113783818, comdat
+ at llvm.compiler.used = appending global [1 x ptr] [ptr @__llvm_profile_raw_version], section "llvm.metadata"
diff --git a/llvm/test/Transforms/PGOProfile/thinlto_cspgo_raw_version.ll b/llvm/test/Transforms/PGOProfile/thinlto_cspgo_raw_version.ll
new file mode 100644
index 0000000000000..a3a4b1d5f0dcb
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/thinlto_cspgo_raw_version.ll
@@ -0,0 +1,33 @@
+; REQUIRES: x86-registered-target
+
+;; The prevailing raw-version definition may have been emitted before LTO by a
+;; frontend using an older raw format. Backend-only CS temporal instrumentation
+;; must refresh that initializer to describe the records it actually emits.
+;; The non-prevailing copy must remain a declaration.
+; RUN: opt -module-summary %s -o %t1.bc
+; RUN: opt -module-summary %S/Inputs/thinlto_cspgo_raw_version_nonprevailing.ll -o %t2.bc
+; RUN: llvm-lto2 run -lto-cspgo-profile-file=alloc -lto-cspgo-gen \
+; RUN: -pgo-temporal-instrumentation -save-temps -o %t %t1.bc %t2.bc \
+; RUN: -r=%t1.bc,main,plx \
+; RUN: -r=%t1.bc,__llvm_profile_raw_version,plx \
+; RUN: -r=%t2.bc,__llvm_profile_raw_version,x
+; RUN: llvm-dis %t.1.4.opt.bc -o - | FileCheck %s --check-prefix=PREVAILING
+; RUN: llvm-dis %t.2.4.opt.bc -o - | FileCheck %s --check-prefix=NONPREVAILING
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+$__llvm_profile_raw_version = comdat any
+
+;; Raw version 10 with the IR and CSIR variant masks. The backend uses raw
+;; version 11 and adds the temporal-profile variant mask.
+ at __llvm_profile_raw_version = hidden constant i64 216172782113783818, comdat
+ at llvm.compiler.used = appending global [1 x ptr] [ptr @__llvm_profile_raw_version], section "llvm.metadata"
+
+; PREVAILING: @__llvm_profile_raw_version = hidden constant i64 -9007199254740991989
+; NONPREVAILING: @__llvm_profile_raw_version = external hidden constant i64
+
+define i32 @main() {
+entry:
+ ret i32 0
+}
More information about the llvm-commits
mailing list