[llvm] [CFI] Propagate GUIDs correctly after PR #184065 (PR #200542)
Mircea Trofin via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 08:05:46 PDT 2026
https://github.com/mtrofin updated https://github.com/llvm/llvm-project/pull/200542
>From 3068ca371f546d8302ad1b42fc92687668f805bb Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Fri, 29 May 2026 20:56:12 -0700
Subject: [PATCH 1/5] [CFI] Propagate GUIDs correctly after PR #184065
LowerTypeTests and the ModuleSummaryIndex mapped CFI function declarations
and definitions based purely on the string name. If an internal function
was promoted and renamed during ThinLTO, its name-derived GUID would change,
leading to mismatches, resulting in runtime traps.
This patch saves the GUIDs in the CFI tables. LowerTypeTests doesn't need to
perform name-based lookups, relying on this GUID instead.
---
llvm/include/llvm/IR/GlobalValue.h | 4 +
llvm/include/llvm/IR/ModuleSummaryIndex.h | 24 ++--
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 18 ++-
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 21 +++-
llvm/lib/IR/Globals.cpp | 7 ++
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 40 +++++--
.../Transforms/IPO/ThinLTOBitcodeWriter.cpp | 4 +
.../test/ThinLTO/X86/cfi-icall-only-defuse.ll | 4 +-
llvm/test/ThinLTO/X86/cfi-icall-thinlto.ll | 106 ++++++++++++++++++
llvm/test/ThinLTO/X86/cfi-icall.ll | 4 +-
.../Transforms/LowerTypeTests/export-alias.ll | 8 +-
.../LowerTypeTests/export-cross-dso-cfi.ll | 6 +-
.../Transforms/LowerTypeTests/export-icall.ll | 16 +--
.../LowerTypeTests/export-rename-local.ll | 2 +-
.../LowerTypeTests/export-symver.ll | 4 +-
.../test/Transforms/LowerTypeTests/pr37625.ll | 4 +-
16 files changed, 210 insertions(+), 62 deletions(-)
create mode 100644 llvm/test/ThinLTO/X86/cfi-icall-thinlto.ll
diff --git a/llvm/include/llvm/IR/GlobalValue.h b/llvm/include/llvm/IR/GlobalValue.h
index ef037a25fac2e..81859745386e0 100644
--- a/llvm/include/llvm/IR/GlobalValue.h
+++ b/llvm/include/llvm/IR/GlobalValue.h
@@ -638,6 +638,10 @@ class GlobalValue : public Constant {
/// that might pre-date the storage of GUIDs in metadata.
GUID getGUIDOrFallback() const;
+ /// same as getGUIDOrFallback, but for fallback we dropLLVMManglingEscape the
+ /// name first.
+ GUID getGUIDOrFallbackDropEscape() const;
+
/// @name Materialization
/// Materialization is used to construct functions only as they're needed.
/// This
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h
index ed39eb99a9196..512ffde9aab04 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -1355,8 +1355,12 @@ class CfiFunctionIndex {
CfiFunctionIndex() = default;
template <typename It> CfiFunctionIndex(It B, It E) {
- for (; B != E; ++B)
- emplace(*B);
+ for (; B != E; ++B) {
+ StringRef S(*B);
+ GlobalValue::GUID GUID = GlobalValue::getGUIDAssumingExternalLinkage(
+ GlobalValue::dropLLVMManglingEscape(S));
+ Index[GUID].emplace(S);
+ }
}
std::vector<StringRef> symbols() const {
@@ -1381,21 +1385,9 @@ class CfiFunctionIndex {
return make_range(I->second.begin(), I->second.end());
}
- template <typename... Args> void emplace(Args &&...A) {
- StringRef S(std::forward<Args>(A)...);
- GlobalValue::GUID GUID = GlobalValue::getGUIDAssumingExternalLinkage(
- GlobalValue::dropLLVMManglingEscape(S));
- Index[GUID].emplace(S);
- }
+ void emplace(GlobalValue::GUID GUID, StringRef S) { Index[GUID].emplace(S); }
- size_t count(StringRef S) const {
- GlobalValue::GUID GUID = GlobalValue::getGUIDAssumingExternalLinkage(
- GlobalValue::dropLLVMManglingEscape(S));
- auto I = Index.find(GUID);
- if (I == Index.end())
- return 0;
- return I->second.count(S);
- }
+ size_t count(GlobalValue::GUID GUID) const { return Index.count(GUID); }
bool empty() const { return Index.empty(); }
};
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index f4324aa37f2e8..99922579c4e5e 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -8189,17 +8189,23 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) {
case bitc::FS_CFI_FUNCTION_DEFS: {
auto &CfiFunctionDefs = TheIndex.cfiFunctionDefs();
- for (unsigned I = 0; I != Record.size(); I += 2)
- CfiFunctionDefs.emplace(Strtab.data() + Record[I],
- static_cast<size_t>(Record[I + 1]));
+ for (unsigned I = 0; I != Record.size(); I += 3) {
+ uint64_t GUID = Record[I];
+ StringRef Name(Strtab.data() + Record[I + 1],
+ static_cast<size_t>(Record[I + 2]));
+ CfiFunctionDefs.emplace(GUID, Name);
+ }
break;
}
case bitc::FS_CFI_FUNCTION_DECLS: {
auto &CfiFunctionDecls = TheIndex.cfiFunctionDecls();
- for (unsigned I = 0; I != Record.size(); I += 2)
- CfiFunctionDecls.emplace(Strtab.data() + Record[I],
- static_cast<size_t>(Record[I + 1]));
+ for (unsigned I = 0; I != Record.size(); I += 3) {
+ uint64_t GUID = Record[I];
+ StringRef Name(Strtab.data() + Record[I + 1],
+ static_cast<size_t>(Record[I + 2]));
+ CfiFunctionDecls.emplace(GUID, Name);
+ }
break;
}
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 21cb0d2a28e36..3788365366426 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -5360,21 +5360,30 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() {
getReferencedTypeIds(FS, ReferencedTypeIds);
}
- SmallVector<StringRef, 4> Functions;
+ struct CfiFunction {
+ GlobalValue::GUID GUID = 0;
+ StringRef Name;
+ bool operator<(const CfiFunction &RHS) const {
+ return Name < RHS.Name;
+ }
+ };
+ std::vector<CfiFunction> Functions;
auto EmitCfiFunctions = [&](const CfiFunctionIndex &CfiIndex,
bitc::GlobalValueSummarySymtabCodes Code) {
if (CfiIndex.empty())
return;
for (GlobalValue::GUID GUID : DefOrUseGUIDs) {
- auto Defs = CfiIndex.forGuid(GUID);
- llvm::append_range(Functions, Defs);
+ for (StringRef Name : CfiIndex.forGuid(GUID)) {
+ Functions.push_back({GUID, Name});
+ }
}
if (Functions.empty())
return;
llvm::sort(Functions);
- for (const auto &S : Functions) {
- NameVals.push_back(StrtabBuilder.add(S));
- NameVals.push_back(S.size());
+ for (const auto &F : Functions) {
+ NameVals.push_back(F.GUID);
+ NameVals.push_back(StrtabBuilder.add(F.Name));
+ NameVals.push_back(F.Name.size());
}
Stream.EmitRecord(Code, NameVals);
NameVals.clear();
diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp
index 832eed31fa40a..0ab265c4e756f 100644
--- a/llvm/lib/IR/Globals.cpp
+++ b/llvm/lib/IR/Globals.cpp
@@ -106,6 +106,13 @@ GlobalValue::GUID GlobalValue::getGUIDOrFallback() const {
return getGUIDAssumingExternalLinkage(getGlobalIdentifier());
}
+GlobalValue::GUID GlobalValue::getGUIDOrFallbackDropEscape() const {
+ auto GuidIfPresent = getGUIDIfAssigned();
+ return GuidIfPresent ? *GuidIfPresent
+ : GlobalValue::getGUIDAssumingExternalLinkage(
+ GlobalValue::dropLLVMManglingEscape(getName()));
+}
+
std::optional<GlobalValue::GUID> GlobalValue::getGUIDIfAssigned() const {
// First check the metadata.
auto *MD = getGUIDMetadata();
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index a57e0c59726a3..1e0f6b28647c0 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -1788,10 +1788,11 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative(
}
if (IsExported) {
+ GlobalValue::GUID GUID = F->getGUIDOrFallbackDropEscape();
if (IsJumpTableCanonical)
- ExportSummary->cfiFunctionDefs().emplace(F->getName());
+ ExportSummary->cfiFunctionDefs().emplace(GUID, F->getName());
else
- ExportSummary->cfiFunctionDecls().emplace(F->getName());
+ ExportSummary->cfiFunctionDecls().emplace(GUID, F->getName());
}
if (!IsJumpTableCanonical) {
@@ -2123,9 +2124,10 @@ bool LowerTypeTestsModule::lower() {
// have the same name, but it's not the one we are looking for.
if (F.hasLocalLinkage())
continue;
- if (ImportSummary->cfiFunctionDefs().count(F.getName()))
+ auto GUID = F.getGUIDOrFallback();
+ if (ImportSummary->cfiFunctionDefs().count(GUID))
Defs.push_back(&F);
- else if (ImportSummary->cfiFunctionDecls().count(F.getName()))
+ else if (ImportSummary->cfiFunctionDecls().count(GUID))
Decls.push_back(&F);
}
@@ -2169,7 +2171,7 @@ bool LowerTypeTestsModule::lower() {
struct ExportedFunctionInfo {
CfiFunctionLinkage Linkage;
- MDNode *FuncMD; // {name, linkage, type[, type...]}
+ MDNode *FuncMD; // {name, linkage, stable_GUID, type[, type...]}
};
MapVector<StringRef, ExportedFunctionInfo> ExportedFunctions;
if (ExportSummary) {
@@ -2207,9 +2209,18 @@ bool LowerTypeTestsModule::lower() {
->getValue()
->getUniqueInteger()
.getZExtValue());
- const GlobalValue::GUID GUID =
- GlobalValue::getGUIDAssumingExternalLinkage(
- GlobalValue::dropLLVMManglingEscape(FunctionName));
+ // Use the stable GUID stored in !cfi.functions (element 2) if
+ // present. Promoted internal functions have a stable GUID in their
+ // !guid metadata that differs from
+ // getGUIDAssumingExternalLinkage(promoted_name), and the combined
+ // index stores entries under the stable GUID.
+ assert(FuncMD->getNumOperands() >= 3 &&
+ isa<ConstantAsMetadata>(FuncMD->getOperand(2)) &&
+ "GUID metadata missing");
+ GlobalValue::GUID GUID = cast<ConstantAsMetadata>(FuncMD->getOperand(2))
+ ->getValue()
+ ->getUniqueInteger()
+ .getZExtValue();
// Do not emit jumptable entries for functions that are not-live and
// have no live references (and are not exported with cross-DSO CFI.)
if (!ExportSummary->isGUIDLive(GUID))
@@ -2248,11 +2259,15 @@ bool LowerTypeTestsModule::lower() {
F = nullptr;
}
- if (!F)
+ if (!F) {
F = Function::Create(
FunctionType::get(Type::getVoidTy(M.getContext()), false),
GlobalVariable::ExternalLinkage,
M.getDataLayout().getProgramAddressSpace(), FunctionName, &M);
+ F->setMetadata(
+ LLVMContext::MD_unique_id,
+ MDTuple::get(M.getContext(), {FuncMD->getOperand(2).get()}));
+ }
// If the function is available_externally, remove its definition so
// that it is handled the same way as a declaration. Later we will try
@@ -2280,7 +2295,12 @@ bool LowerTypeTestsModule::lower() {
F->setLinkage(GlobalValue::ExternalWeakLinkage);
F->eraseMetadata(LLVMContext::MD_type);
- for (unsigned I = 2; I < FuncMD->getNumOperands(); ++I)
+ // Type metadata starts at operand 3 (operand 2 is the stable GUID).
+ assert(FuncMD->getNumOperands() >= 3 &&
+ isa<ConstantAsMetadata>(FuncMD->getOperand(2)) &&
+ "GUID metadata missing");
+ unsigned TypesStart = 3;
+ for (unsigned I = TypesStart; I < FuncMD->getNumOperands(); ++I)
F->addMetadata(LLVMContext::MD_type,
*cast<MDNode>(FuncMD->getOperand(I).get()));
}
diff --git a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
index 8ceefae72d9bf..a95e4b1d47cfb 100644
--- a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
+++ b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
@@ -438,6 +438,10 @@ void splitAndWriteThinLTOBitcode(
Linkage = CFL_Declaration;
Elts.push_back(ConstantAsMetadata::get(
llvm::ConstantInt::get(Type::getInt8Ty(Ctx), Linkage)));
+ // Store the function's stable GUID so that LowerTypeTests can find the
+ // corresponding entry in the combined index even after promotion renaming.
+ Elts.push_back(ConstantAsMetadata::get(llvm::ConstantInt::get(
+ Type::getInt64Ty(Ctx), V->getGUIDOrFallbackDropEscape())));
append_range(Elts, Types);
CfiFunctionMDs.push_back(MDTuple::get(Ctx, Elts));
}
diff --git a/llvm/test/ThinLTO/X86/cfi-icall-only-defuse.ll b/llvm/test/ThinLTO/X86/cfi-icall-only-defuse.ll
index 41fb17d574985..a1467920ebf24 100644
--- a/llvm/test/ThinLTO/X86/cfi-icall-only-defuse.ll
+++ b/llvm/test/ThinLTO/X86/cfi-icall-only-defuse.ll
@@ -40,7 +40,7 @@ define i8 @f(i1 %i, ptr %p) {
!0 = !{i64 0, !"t1"}
; FOOBAZ: <GLOBALVAL_SUMMARY_BLOCK
-; FOOBAZ: <CFI_FUNCTION_DEFS op0=0 op1=3 op2=3 op3=3 op4=6 op5=3/>
+; FOOBAZ: <CFI_FUNCTION_DEFS op0=-2012135647395072713 op1=0 op2=3 op3=7546896869197086323 op4=3 op5=3 op6=6699318081062747564 op7=6 op8=3/>
; FOOBAZ: <TYPE_ID op0=9 op1=2 op2=4 op3=7 op4=0 op5=0 op6=0 op7=0/>
; FOOBAZ: </GLOBALVAL_SUMMARY_BLOCK>
; FOOBAZ: <STRTAB_BLOCK
@@ -48,7 +48,7 @@ define i8 @f(i1 %i, ptr %p) {
; FOOBAZ-NEXT: </STRTAB_BLOCK>
; BARQUX: <GLOBALVAL_SUMMARY_BLOCK
-; BARQUX: <CFI_FUNCTION_DEFS op0=0 op1=3 op2=3 op3=3 op4=6 op5=3/>
+; BARQUX: <CFI_FUNCTION_DEFS op0=-2012135647395072713 op1=0 op2=3 op3=7546896869197086323 op4=3 op5=3 op6=-2941065755689329704 op7=6 op8=3/>
; BARQUX: <TYPE_ID op0=9 op1=2 op2=4 op3=7 op4=0 op5=0 op6=0 op7=0/>
; BARQUX: </GLOBALVAL_SUMMARY_BLOCK>
; BARQUX: <STRTAB_BLOCK
diff --git a/llvm/test/ThinLTO/X86/cfi-icall-thinlto.ll b/llvm/test/ThinLTO/X86/cfi-icall-thinlto.ll
new file mode 100644
index 0000000000000..8bf3d0b74ce4f
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/cfi-icall-thinlto.ll
@@ -0,0 +1,106 @@
+; REQUIRES: x86-registered-target
+
+; RUN: rm -rf %t.dir && split-file %s %t.dir
+; RUN: opt -thinlto-bc -thinlto-split-lto-unit %t.dir/lib.ll -o %t.dir/lib.bc
+; RUN: opt -thinlto-bc -thinlto-split-lto-unit %t.dir/main.ll -o %t.dir/main.bc
+; RUN: llvm-lto2 run -save-temps %t.dir/lib.bc %t.dir/main.bc -o %t.dir/summary \
+; RUN: -r=%t.dir/lib.bc,_Z11public_funcv,plx \
+; RUN: -r=%t.dir/lib.bc,syscall, \
+; RUN: -r=%t.dir/lib.bc,_ZN12_GLOBAL__N_113internal_funcEv.35df87b54cddf81e734207bfc5eea57a,pl \
+; RUN: -r=%t.dir/main.bc,main,plx \
+; RUN: -r=%t.dir/main.bc,_Z11public_funcv,l
+; RUN: llvm-dis %t.dir/summary.2.4.opt.bc -o - | FileCheck %s
+
+; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.dir/lib.bc %t.dir/main.bc \
+; RUN: -o %t.dir/distidx \
+; RUN: -r=%t.dir/lib.bc,_Z11public_funcv,plx \
+; RUN: -r=%t.dir/lib.bc,syscall, \
+; RUN: -r=%t.dir/lib.bc,_ZN12_GLOBAL__N_113internal_funcEv.35df87b54cddf81e734207bfc5eea57a,pl \
+; RUN: -r=%t.dir/main.bc,main,plx \
+; RUN: -r=%t.dir/main.bc,_Z11public_funcv,l
+; RUN: llvm-bcanalyzer -dump %t.dir/lib.bc.thinlto.bc | FileCheck %s --check-prefix=DIST
+
+; Verify that the type test is NOT lowered to an unconditional trap,
+; and that the initialization path (which calls syscall) is preserved.
+; CHECK: define hidden noundef i32 @main()
+; CHECK: %[[LOAD:.*]] = load i1, ptr @_ZN12_GLOBAL__N_18lazy_valE.1.llvm.{{.*}}
+; CHECK-NEXT: br i1 %[[LOAD]], label %{{.*}}, label %[[LABEL:.*]]
+; CHECK: [[LABEL]]:
+; CHECK: call i64 (i64, ...) @syscall(i64 noundef 186)
+; CHECK-NOT: call void @llvm.ubsantrap
+
+; Verify that the distributed index records CFI_FUNCTION_DEFS with the
+; 3-field format [GUID, strtab_offset, name_length]. The promoted internal
+; function is listed with its stable pre-promotion GUID.
+; DIST: <CFI_FUNCTION_DEFS op0={{[-0-9]+}} op1=0 op2=67/>
+; DIST: <STRTAB_BLOCK
+; DIST-NEXT: <BLOB abbrevid=4/> blob data = '_ZN12_GLOBAL__N_113internal_funcEv.35df87b54cddf81e734207bfc5eea57a_ZTSFjvE'
+
+;--- lib.ll
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+
+ at _ZN12_GLOBAL__N_18lazy_valE.1 = internal unnamed_addr global i1 false, align 4
+
+define hidden void @_Z11public_funcv() local_unnamed_addr #0 !type !10 !type !11 {
+ %1 = load i1, ptr @_ZN12_GLOBAL__N_18lazy_valE.1, align 1
+ %2 = zext i1 %1 to i8
+ %3 = trunc nuw i8 %2 to i1
+ br i1 %3, label %9, label %4
+
+4: ; preds = %0
+ %5 = tail call i1 @llvm.type.test(ptr nonnull @_ZN12_GLOBAL__N_113internal_funcEv, metadata !"_ZTSFjvE")
+ br i1 %5, label %7, label %6
+6: ; preds = %4
+ tail call void @llvm.ubsantrap(i8 2) #5
+ unreachable
+
+7: ; preds = %4
+ %8 = tail call i64 (i64, ...) @syscall(i64 noundef 186)
+ store i1 true, ptr @_ZN12_GLOBAL__N_18lazy_valE.1, align 1
+ br label %9
+
+9: ; preds = %0, %7
+ ret void
+}
+
+define internal noundef i32 @_ZN12_GLOBAL__N_113internal_funcEv() #1 !type !15 !type !16 {
+ %1 = tail call i64 (i64, ...) @syscall(i64 noundef 186)
+ %2 = trunc i64 %1 to i32
+ ret i32 %2
+}
+
+declare i1 @llvm.type.test(ptr, metadata) #2
+declare void @llvm.ubsantrap(i8 immarg) #3
+declare i64 @syscall(i64 noundef, ...) #4
+
+attributes #0 = { alwaysinline mustprogress nounwind uwtable "target-cpu"="x86-64" }
+attributes #1 = { mustprogress nounwind uwtable "target-cpu"="x86-64" }
+attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #3 = { cold noreturn nounwind memory(inaccessiblemem: write) }
+attributes #4 = { nounwind "target-cpu"="x86-64" }
+attributes #5 = { nomerge noreturn nounwind }
+
+!10 = !{i64 0, !"_ZTSFvvE"}
+!11 = !{i64 0, !"_ZTSFvvE.generalized"}
+!15 = !{i64 0, !"_ZTSFjvE"}
+!16 = !{i64 0, !"_ZTSFjvE.generalized"}
+
+;--- main.ll
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+
+define hidden noundef i32 @main() local_unnamed_addr #0 !type !8 !type !9 {
+ tail call void @_Z11public_funcv()
+ ret i32 0
+}
+
+declare !type !11 !type !12 dso_local void @_Z11public_funcv() local_unnamed_addr #1
+
+attributes #0 = { mustprogress norecurse uwtable "target-cpu"="x86-64" }
+attributes #1 = { "target-cpu"="x86-64" }
+
+!8 = !{i64 0, !"_ZTSFivE"}
+!9 = !{i64 0, !"_ZTSFivE.generalized"}
+!11 = !{i64 0, !"_ZTSFvvE"}
+!12 = !{i64 0, !"_ZTSFvvE.generalized"}
diff --git a/llvm/test/ThinLTO/X86/cfi-icall.ll b/llvm/test/ThinLTO/X86/cfi-icall.ll
index 76847e8300434..15f519ecf523a 100644
--- a/llvm/test/ThinLTO/X86/cfi-icall.ll
+++ b/llvm/test/ThinLTO/X86/cfi-icall.ll
@@ -27,8 +27,8 @@ declare i1 @llvm.type.test(ptr %ptr, metadata %type) nounwind readnone
!0 = !{i64 0, !"typeid1"}
; COMBINED: <GLOBALVAL_SUMMARY_BLOCK
-; COMBINED: <CFI_FUNCTION_DEFS op0=0 op1=3/>
-; COMBINED: <CFI_FUNCTION_DECLS op0=3 op1=3/>
+; COMBINED: <CFI_FUNCTION_DEFS op0={{-?[0-9]+}} op1=0 op2=3/>
+; COMBINED: <CFI_FUNCTION_DECLS op0={{-?[0-9]+}} op1=3 op2=3/>
; COMBINED: <TYPE_ID op0=6 op1=7 op2=4 op3=7 op4=0 op5=0 op6=0 op7=0/>
; COMBINED: </GLOBALVAL_SUMMARY_BLOCK>
diff --git a/llvm/test/Transforms/LowerTypeTests/export-alias.ll b/llvm/test/Transforms/LowerTypeTests/export-alias.ll
index 25d34833c82c3..ac9dd09a4cbd7 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-alias.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-alias.ll
@@ -10,10 +10,10 @@ target triple = "x86_64-unknown-linux"
!cfi.functions = !{!0, !2, !3, !4}
!aliases = !{!5, !6}
-!0 = !{!"external_addrtaken", i8 0, !1}
+!0 = !{!"external_addrtaken", i8 0, i64 16594175687743574550, !1}
!1 = !{i64 0, !"typeid1"}
-!2 = !{!"alias1", i8 0, !1}
-!3 = !{!"alias2", i8 0, !1}
-!4 = !{!"alias3", i8 0, !1}
+!2 = !{!"alias1", i8 0, i64 1062103744896965210, !1}
+!3 = !{!"alias2", i8 0, i64 2510616090736846890, !1}
+!4 = !{!"alias3", i8 0, i64 9766217518394409673, !1}
!5 = !{!"external_addrtaken", !"alias1", !"alias2"}
!6 = !{!"not_present", !"alias3"}
diff --git a/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll b/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll
index 68fff336e4138..dbc1e22fae029 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll
@@ -31,9 +31,9 @@ define internal void @regularlto_internal() !type !1 !type !2 {
!cfi.functions = !{!0, !3, !4}
!llvm.module.flags = !{!5}
-!0 = !{!"external", i8 0, !1, !2}
+!0 = !{!"external", i8 0, i64 5224464028922159466, !1, !2}
!1 = !{i64 0, !"typeid1"}
!2 = !{i64 0, i64 1234}
-!3 = !{!"external2", i8 1, !1, !2}
-!4 = !{!"internal", i8 0, !1, !2}
+!3 = !{!"external2", i8 1, i64 16430208882958242304, !1, !2}
+!4 = !{!"internal", i8 0, i64 15859245615183425489, !1, !2}
!5 = !{i32 4, !"Cross-DSO CFI", i32 1}
diff --git a/llvm/test/Transforms/LowerTypeTests/export-icall.ll b/llvm/test/Transforms/LowerTypeTests/export-icall.ll
index f8adb2d69910f..5830bcb97989c 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-icall.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-icall.ll
@@ -19,19 +19,19 @@ define void @f3(i32 %x) !type !8 {
!cfi.functions = !{!0, !1, !3, !9, !10, !4, !5, !6}
; declaration of @h with a different type is ignored
-!0 = !{!"h", i8 1, !7}
+!0 = !{!"h", i8 1, i64 8124147457056772133, !7}
; extern_weak declaration of @h with a different type is ignored as well
-!1 = !{!"h", i8 2, !8}
+!1 = !{!"h", i8 2, i64 8124147457056772133, !8}
!2 = !{i64 0, !"typeid1"}
; definitions of @f and @f2 replace types on the IR declarations above
-!3 = !{!"f", i8 0, !2}
-!9 = !{!"f2", i8 0, !2}
-!10 = !{!"f3", i8 0, !2}
-!4 = !{!"external", i8 1, !2}
-!5 = !{!"external_weak", i8 2, !2}
-!6 = !{!"g", i8 0, !7}
+!3 = !{!"f", i8 0, i64 14740650423002898831, !2}
+!9 = !{!"f2", i8 0, i64 8471399308421654326, !2}
+!10 = !{!"f3", i8 0, i64 4197650231481825559, !2}
+!4 = !{!"external", i8 1, i64 5224464028922159466, !2}
+!5 = !{!"external_weak", i8 2, i64 5227079976482001346, !2}
+!6 = !{!"g", i8 0, i64 13146401226427987378, !7}
!7 = !{i64 0, !"typeid2"}
!8 = !{i64 0, !"typeid3"}
diff --git a/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll b/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll
index 0f88edff5e53e..4be15b8a45c06 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll
@@ -11,5 +11,5 @@ define internal void @external_addrtaken() !type !1 {
!cfi.functions = !{!0}
-!0 = !{!"external_addrtaken", i8 0, !1}
+!0 = !{!"external_addrtaken", i8 0, i64 16594175687743574550, !1}
!1 = !{i64 0, !"typeid1"}
diff --git a/llvm/test/Transforms/LowerTypeTests/export-symver.ll b/llvm/test/Transforms/LowerTypeTests/export-symver.ll
index 39b85c6d9be2d..ea4594a359cc0 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-symver.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-symver.ll
@@ -9,8 +9,8 @@ target triple = "x86_64-unknown-linux"
!cfi.functions = !{!0, !1}
!symvers = !{!3, !4}
-!0 = !{!"external_addrtaken", i8 0, !2}
-!1 = !{!"external_addrtaken2", i8 0, !2}
+!0 = !{!"external_addrtaken", i8 0, i64 16594175687743574550, !2}
+!1 = !{!"external_addrtaken2", i8 0, i64 2415377257478301385, !2}
!2 = !{i64 0, !"typeid1"}
!3 = !{!"external_addrtaken", !"alias1"}
!4 = !{!"not_exported", !"alias2"}
diff --git a/llvm/test/Transforms/LowerTypeTests/pr37625.ll b/llvm/test/Transforms/LowerTypeTests/pr37625.ll
index cf52cdf0759a3..3f7fc8beaa964 100644
--- a/llvm/test/Transforms/LowerTypeTests/pr37625.ll
+++ b/llvm/test/Transforms/LowerTypeTests/pr37625.ll
@@ -7,8 +7,8 @@ declare !type !2 extern_weak void @external_addrtaken(i8)
!cfi.functions = !{!0, !1}
-!0 = !{!"external_addrtaken", i8 2, !2}
-!1 = !{!"external_addrtaken", i8 0, !2}
+!0 = !{!"external_addrtaken", i8 2, i64 16594175687743574550, !2}
+!1 = !{!"external_addrtaken", i8 0, i64 16594175687743574550, !2}
!2 = !{i64 0, !"typeid1"}
; CHECK-DAG: @external_addrtaken = alias [8 x i8], ptr @.cfi.jumptable
>From 3245c15b785a8fc80fe739ba701bde0a21655318 Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Fri, 29 May 2026 21:08:15 -0700
Subject: [PATCH 2/5] format
---
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 3788365366426..ce52e8cfc6a7e 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -5363,9 +5363,7 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() {
struct CfiFunction {
GlobalValue::GUID GUID = 0;
StringRef Name;
- bool operator<(const CfiFunction &RHS) const {
- return Name < RHS.Name;
- }
+ bool operator<(const CfiFunction &RHS) const { return Name < RHS.Name; }
};
std::vector<CfiFunction> Functions;
auto EmitCfiFunctions = [&](const CfiFunctionIndex &CfiIndex,
>From 79e4a27cb0e8df1f20380a6ba68b7b728e10c0ff Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Fri, 29 May 2026 21:39:26 -0700
Subject: [PATCH 3/5] one more test
---
.../Transforms/ThinLTOBitcodeWriter/function-alias.ll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/function-alias.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/function-alias.ll
index 74693c1312b01..9fb876ba905eb 100644
--- a/llvm/test/Transforms/ThinLTOBitcodeWriter/function-alias.ll
+++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/function-alias.ll
@@ -10,11 +10,11 @@ define hidden void @Func() !type !0 {
; CHECK1: !cfi.functions = !{![[F1:[0-9]+]], ![[F2:[0-9]+]], ![[F3:[0-9]+]], ![[F4:[0-9]+]]}
; CHECK1: !aliases = !{![[A:[0-9]+]]}
-; CHECK1: ![[F1]] = !{!"Func", i8 0, ![[T:[0-9]+]]}
+; CHECK1: ![[F1]] = !{!"Func", i8 0, i64 {{.*}}, ![[T:[0-9]+]]}
; CHECK1: ![[T]] = !{i64 0, !"_ZTSFvvE"}
-; CHECK1: ![[F2]] = !{!"Alias", i8 0, ![[T]]}
-; CHECK1: ![[F3]] = !{!"Hidden_Alias", i8 0, ![[T]]}
-; CHECK1: ![[F4]] = !{!"Weak_Alias", i8 0, ![[T]]}
+; CHECK1: ![[F2]] = !{!"Alias", i8 0, i64 {{.*}}, ![[T]]}
+; CHECK1: ![[F3]] = !{!"Hidden_Alias", i8 0, i64 {{.*}}, ![[T]]}
+; CHECK1: ![[F4]] = !{!"Weak_Alias", i8 0, i64 {{.*}}, ![[T]]}
;
; CHECK1: ![[A]] = !{!"Func", !"Alias", !"Hidden_Alias", !"Weak_Alias"}
@Alias = hidden alias void (), ptr @Func
>From 10398ee2954c6a11415cf6a11ffb1f6ea78b44db Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Mon, 1 Jun 2026 07:40:05 -0700
Subject: [PATCH 4/5] auto-upgrade
---
llvm/include/llvm/IR/AutoUpgrade.h | 3 +++
llvm/lib/AsmParser/LLParser.cpp | 1 +
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 1 +
llvm/lib/IR/AutoUpgrade.cpp | 24 +++++++++++++++++++
.../Assembler/auto_upgrade_cfi_functions.ll | 15 ++++++++++++
5 files changed, 44 insertions(+)
create mode 100644 llvm/test/Assembler/auto_upgrade_cfi_functions.ll
diff --git a/llvm/include/llvm/IR/AutoUpgrade.h b/llvm/include/llvm/IR/AutoUpgrade.h
index 540d60afc5a01..c211951c5ff22 100644
--- a/llvm/include/llvm/IR/AutoUpgrade.h
+++ b/llvm/include/llvm/IR/AutoUpgrade.h
@@ -71,6 +71,9 @@ namespace llvm {
/// old retain release marker to new module flag format.
LLVM_ABI void UpgradeARCRuntime(Module &M);
+ /// Add GUID field of the CFI metadata.
+ LLVM_ABI void UpgradeCFIFunctions(Module &M);
+
LLVM_ABI void UpgradeSectionAttributes(Module &M);
/// Correct any IR that is relying on old function attribute behavior.
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 44d81ad852688..f112a791d4ee9 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -477,6 +477,7 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) {
UpgradeModuleFlags(*M);
UpgradeNVVMAnnotations(*M);
+ UpgradeCFIFunctions(*M);
UpgradeSectionAttributes(*M);
copyModuleAttrToFunctions(*M);
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 99922579c4e5e..cd0ba87a1deb3 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -7237,6 +7237,7 @@ Error BitcodeReader::materializeModule() {
UpgradeNVVMAnnotations(*TheModule);
UpgradeARCRuntime(*TheModule);
+ UpgradeCFIFunctions(*TheModule);
copyModuleAttrToFunctions(*TheModule);
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index 86a8a3ec06ff3..61312c5be2bdc 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -6045,6 +6045,30 @@ static bool upgradeRetainReleaseMarker(Module &M) {
return Changed;
}
+void llvm::UpgradeCFIFunctions(Module &M) {
+ if (NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions")) {
+ LLVMContext &C = M.getContext();
+ for (unsigned I = 0, E = CfiFunctionsMD->getNumOperands(); I != E; ++I) {
+ MDNode *FuncMD = CfiFunctionsMD->getOperand(I);
+ if (FuncMD->getNumOperands() >= 3 &&
+ isa<ConstantAsMetadata>(FuncMD->getOperand(2)))
+ continue;
+
+ auto *Name = cast<MDString>(FuncMD->getOperand(0));
+ StringRef FunctionName = Name->getString();
+ const GlobalValue::GUID GUID =
+ GlobalValue::getGUIDAssumingExternalLinkage(
+ GlobalValue::dropLLVMManglingEscape(FunctionName));
+
+ SmallVector<Metadata *, 4> Ops(FuncMD->op_begin(), FuncMD->op_end());
+ Ops.insert(Ops.begin() + 2, ConstantAsMetadata::get(ConstantInt::get(
+ Type::getInt64Ty(C), GUID)));
+
+ CfiFunctionsMD->setOperand(I, MDTuple::get(C, Ops));
+ }
+ }
+}
+
void llvm::UpgradeARCRuntime(Module &M) {
// This lambda converts normal function calls to ARC runtime functions to
// intrinsic calls.
diff --git a/llvm/test/Assembler/auto_upgrade_cfi_functions.ll b/llvm/test/Assembler/auto_upgrade_cfi_functions.ll
new file mode 100644
index 0000000000000..383aacc047b39
--- /dev/null
+++ b/llvm/test/Assembler/auto_upgrade_cfi_functions.ll
@@ -0,0 +1,15 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+
+!cfi.functions = !{!0, !2, !4}
+!0 = !{!"function", i8 0, !1}
+!1 = !{i64 0, !"typeid1"}
+!2 = !{!"other_function", i8 1, !1, !3}
+!3 = !{i64 0, !"typeid2"}
+!4 = !{!"up_to_date", i8 2, i64 123456789, !1}
+
+; CHECK: !cfi.functions = !{!0, !2, !4}
+; CHECK: !0 = !{!"function", i8 0, i64 6717233803957748929, !1}
+; CHECK: !1 = !{i64 0, !"typeid1"}
+; CHECK: !2 = !{!"other_function", i8 1, i64 -2568568921219972102, !1, !3}
+; CHECK: !3 = !{i64 0, !"typeid2"}
+; CHECK: !4 = !{!"up_to_date", i8 2, i64 123456789, !1}
>From 59ed4fc91c78f8be012a377c6fcec8013ed75c55 Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Mon, 1 Jun 2026 08:05:22 -0700
Subject: [PATCH 5/5] comment
---
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 1e0f6b28647c0..1eba96e9ee09a 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -2209,11 +2209,7 @@ bool LowerTypeTestsModule::lower() {
->getValue()
->getUniqueInteger()
.getZExtValue());
- // Use the stable GUID stored in !cfi.functions (element 2) if
- // present. Promoted internal functions have a stable GUID in their
- // !guid metadata that differs from
- // getGUIDAssumingExternalLinkage(promoted_name), and the combined
- // index stores entries under the stable GUID.
+ // Use the stable GUID stored in !cfi.functions (element 2).
assert(FuncMD->getNumOperands() >= 3 &&
isa<ConstantAsMetadata>(FuncMD->getOperand(2)) &&
"GUID metadata missing");
More information about the llvm-commits
mailing list