Skip to content

[CFI] Create an external linkage alias instead of promoting internals#203171

Open
mtrofin wants to merge 1 commit into
mainfrom
users/mtrofin/06-10-cfi_creates_alias
Open

[CFI] Create an external linkage alias instead of promoting internals#203171
mtrofin wants to merge 1 commit into
mainfrom
users/mtrofin/06-10-cfi_creates_alias

Conversation

@mtrofin

@mtrofin mtrofin commented Jun 11, 2026

Copy link
Copy Markdown
Member

The problem is described in PR #201849. This is a simple fix - thanks, @teresajohnson for the idea. When CFI promotes an internal symbol, it previously would have to create an alias to avoid breakages in the case of references from inline asm. The alternative here is to leave the symbol as-is and create an alias for CFI's purposes. This makes the calculation of a GUID for this new symbol avoid the class of colisions discussed in the earlier PR. Then, during LowerTypeTests, we give the functon the alias' name and the rest of the CFI functionality remains unaffected.

An alternative we discussed would be to use the same kind of hash as CFI uses - based on module content. That would add some cost to compile time for all modules (it could be paid only in PGO/ThinLTO cases, though). We preferred this alternative here because it's simpler and has no compile-time penalty.

Co-authored-by: Teresa Johnson <tejohnson@google.com>

mtrofin commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

🪟 Windows x64 Test Results

  • 138460 tests passed
  • 3523 tests skipped

✅ The build succeeded and all tests passed.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

🐧 Linux x64 Test Results

  • 200095 tests passed
  • 5501 tests skipped

✅ The build succeeded and all tests passed.

@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch 2 times, most recently from d6411b2 to e737beb Compare June 11, 2026 16:40
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from f8a1ec9 to 854a6f9 Compare June 11, 2026 16:40
});

if (MustPromote) {
PromoteExtra->remove(&ExportGV);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename Promote Extra to what it actually is, so that it is more obvious why the set needs an update (probably add a comment too, or at least)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would do as NFC, before this. Just reusing the current name here.

auto Name = ExportGV.getName();
GlobalValue *ImportGV = nullptr;
if (!PromoteExtra.count(&ExportGV)) {
const bool MustPromote = PromoteExtra && PromoteExtra->count(&ExportGV);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the SetVector need to be a pointer now? Can it just be a non-const ref?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was "meh" about it, too... yes, it could be, but then we'd have to pass down an empty CfiFunctions at the call site. When reading this function, the nuance that "it's empty in one case, intentionally" may be missed. The pointer sends a clearer message, imho.


TargetsForSlot.push_back(VTP.FuncVI);
ValueInfo TheFn = VTP.FuncVI;
if (TheFn.getSummaryList().size() == 1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if there is more than one? Also, add a comment about why we need to get aliasee

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote, ptal

report_fatal_error(
"unexpected call to llvm.icall.branch.funnel during import phase");

for (auto &A : llvm::make_early_inc_range(M.aliases())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of A.eraseFromParent(), which then invalidates the iterator.

@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch 3 times, most recently from 8aa17d1 to 7a0e8d6 Compare June 16, 2026 03:17
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch 2 times, most recently from 6dc97cf to 6f02138 Compare June 17, 2026 00:08
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from 7a0e8d6 to d74770c Compare June 17, 2026 00:08
@mtrofin mtrofin changed the title cfi creates alias [CFI] Create an external linkage alias instead of promoting internals Jun 17, 2026
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from 6f02138 to c7ff207 Compare June 17, 2026 14:54
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from d74770c to 59609f0 Compare June 17, 2026 14:54
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from c7ff207 to 525046e Compare June 17, 2026 18:15
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from 59609f0 to d17b651 Compare June 17, 2026 18:15
@mtrofin mtrofin marked this pull request as ready for review June 17, 2026 18:18
@llvmorg-github-actions llvmorg-github-actions Bot added LTO Link time optimization (regular/full LTO or ThinLTO) llvm:transforms labels Jun 17, 2026
@mtrofin mtrofin requested review from pcc and vitalybuka June 17, 2026 18:18
@llvmorg-github-actions

llvmorg-github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-lto

@llvm/pr-subscribers-llvm-transforms

Author: Mircea Trofin (mtrofin)

Changes

The problem is described in PR #201849. This is a simple fix - thanks, @teresajohnson for the idea. When CFI promotes an internal symbol, it previously would have to create an alias to avoid breakages in the case of references from inline asm. The alternative here is to leave the symbol as-is and create an alias for CFI's purposes. This makes the calculation of a GUID for this new symbol avoid the class of colisions discussed in the earlier PR.

An alternative we discussed would be to use the same kind of hash as CFI uses - based on module content. That would add some cost to compile time for all modules (it could be paid only in PGO/ThinLTO cases, though). We preferred this alternative here because it's simpler and has no compile-time penalty.


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

9 Files Affected:

  • (modified) llvm/lib/Transforms/IPO/LowerTypeTests.cpp (+16)
  • (modified) llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp (+20-33)
  • (modified) llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp (+20-5)
  • (modified) llvm/test/ThinLTO/X86/devirt_function_alias2.ll (+6-4)
  • (modified) llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll (+2-2)
  • (modified) llvm/test/Transforms/ThinLTOBitcodeWriter/comdat.ll (+10-7)
  • (modified) llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll (+2-1)
  • (modified) llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll (+3-2)
  • (modified) llvm/test/Transforms/ThinLTOBitcodeWriter/split-vfunc-internal.ll (+4-2)
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 7c045c90ead5b..d399b6f9d1090 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -2138,6 +2138,22 @@ bool LowerTypeTestsModule::lower() {
       report_fatal_error(
           "unexpected call to llvm.icall.branch.funnel during import phase");
 
+    for (auto &A : llvm::make_early_inc_range(M.aliases())) {
+      if (A.hasLocalLinkage())
+        continue;
+      if (ImportSummary->cfiFunctionDefs().contains(A.getName()) ||
+          ImportSummary->cfiFunctionDecls().contains(A.getName())) {
+        if (auto *F = dyn_cast<Function>(A.getAliasee()->stripPointerCasts())) {
+          A.replaceAllUsesWith(F);
+          std::string Name = std::string(A.getName());
+          A.eraseFromParent();
+          F->setName(Name);
+          F->setLinkage(GlobalValue::ExternalLinkage);
+          F->setVisibility(GlobalValue::HiddenVisibility);
+        }
+      }
+    }
+
     SmallVector<Function *, 8> Defs;
     SmallVector<Function *, 8> Decls;
     for (auto &F : M) {
diff --git a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
index 40200a6554249..1f4dff0aae472 100644
--- a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
+++ b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
@@ -30,23 +30,10 @@ using namespace llvm;
 
 namespace {
 
-// Determine if a promotion alias should be created for a symbol name.
-static bool allowPromotionAlias(const std::string &Name) {
-  // Promotion aliases are used only in inline assembly. It's safe to
-  // simply skip unusual names. Subset of MCAsmInfo::isAcceptableChar()
-  // and MCAsmInfoXCOFF::isAcceptableChar().
-  for (const char &C : Name) {
-    if (isAlnum(C) || C == '_' || C == '.')
-      continue;
-    return false;
-  }
-  return true;
-}
-
 // Promote each local-linkage entity defined by ExportM and used by ImportM by
 // changing visibility and appending the given ModuleId.
 void promoteInternals(Module &ExportM, Module &ImportM, StringRef ModuleId,
-                      const SetVector<GlobalValue *> &PromoteExtra) {
+                      SetVector<GlobalValue *> *PromoteExtra = nullptr) {
   DenseMap<const Comdat *, Comdat *> RenamedComdats;
   for (auto &ExportGV : ExportM.global_values()) {
     if (!ExportGV.hasLocalLinkage())
@@ -54,7 +41,8 @@ void promoteInternals(Module &ExportM, Module &ImportM, StringRef ModuleId,
 
     auto Name = ExportGV.getName();
     GlobalValue *ImportGV = nullptr;
-    if (!PromoteExtra.count(&ExportGV)) {
+    const bool MustPromote = PromoteExtra && PromoteExtra->count(&ExportGV);
+    if (!MustPromote) {
       ImportGV = ImportM.getNamedValue(Name);
       if (!ImportGV)
         continue;
@@ -72,24 +60,24 @@ void promoteInternals(Module &ExportM, Module &ImportM, StringRef ModuleId,
       if (C->getName() == Name)
         RenamedComdats.try_emplace(C, ExportM.getOrInsertComdat(NewName));
 
-    ExportGV.setName(NewName);
-    ExportGV.setLinkage(GlobalValue::ExternalLinkage);
-    ExportGV.setVisibility(GlobalValue::HiddenVisibility);
-    // TODO: remove this reassign and instead create an alias.
-    ExportGV.reassignGUID();
+    auto *ExternalAlias = GlobalAlias::create(
+        ExportGV.getType(), ExportGV.getAddressSpace(),
+        GlobalValue::ExternalLinkage, NewName, &ExportGV, &ExportM);
+    ExternalAlias->setVisibility(GlobalValue::HiddenVisibility);
+    ExportGV.replaceUsesWithIf(ExternalAlias, [ExternalAlias](Use &U) {
+      return U.getUser() != ExternalAlias;
+    });
+
+    if (MustPromote) {
+      PromoteExtra->remove(&ExportGV);
+      PromoteExtra->insert(ExternalAlias);
+    }
+
     if (ImportGV) {
       ImportGV->setName(NewName);
       ImportGV->setVisibility(GlobalValue::HiddenVisibility);
       ImportGV->reassignGUID();
     }
-
-    if (isa<Function>(&ExportGV) && allowPromotionAlias(OldName)) {
-      // Create a local alias with the original name to avoid breaking
-      // references from inline assembly.
-      std::string Alias =
-          ".lto_set_conditional " + OldName + "," + NewName + "\n";
-      ExportM.appendModuleInlineAsm(Alias);
-    }
   }
 
   if (!RenamedComdats.empty())
@@ -191,9 +179,8 @@ void simplifyExternals(Module &M) {
         F.getName().starts_with("llvm."))
       continue;
 
-    Function *NewF =
-        Function::Create(EmptyFT, GlobalValue::ExternalLinkage,
-                         F.getAddressSpace(), "", &M);
+    Function *NewF = Function::Create(EmptyFT, GlobalValue::ExternalLinkage,
+                                      F.getAddressSpace(), "", &M);
     NewF->copyAttributesFrom(&F);
     // Only copy function attribtues.
     NewF->setAttributes(AttributeList::get(M.getContext(),
@@ -419,8 +406,8 @@ void splitAndWriteThinLTOBitcode(
   // match values from its first argument (the "exporting module") in
   // CfiFunctions. So we only need CfiFunctions for the second promotion (M ->
   // MergedM)
-  promoteInternals(*MergedM, M, ModuleId, {});
-  promoteInternals(M, *MergedM, ModuleId, CfiFunctions);
+  promoteInternals(*MergedM, M, ModuleId, nullptr);
+  promoteInternals(M, *MergedM, ModuleId, &CfiFunctions);
 
   auto &Ctx = MergedM->getContext();
   SmallVector<MDNode *, 8> CfiFunctionMDs;
diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
index f876d4f02bb15..2c199896e2fe3 100644
--- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
+++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
@@ -1147,6 +1147,8 @@ bool DevirtModule::tryFindVirtualCallTargets(
     // target.
     auto *GV = dyn_cast<GlobalValue>(C);
     assert(GV);
+    if (auto *GA = dyn_cast<GlobalAlias>(GV))
+      GV = GA->getAliaseeObject();
     TargetsForSlot.push_back({GV, &TM});
   }
 
@@ -1203,7 +1205,17 @@ bool DevirtIndex::tryFindVirtualCallTargets(
       if (mustBeUnreachableFunction(VTP.FuncVI))
         continue;
 
-      TargetsForSlot.push_back(VTP.FuncVI);
+      ValueInfo TheFn = VTP.FuncVI;
+      // Internal linkage functions get an external linkage alias to them.
+      if (!TheFn.getSummaryList().empty())
+        if (auto *AS =
+                dyn_cast<AliasSummary>(TheFn.getSummaryList()[0].get())) {
+          assert(TheFn.getSummaryList().size() == 1 &&
+                 "Aliases to internal linkage targets of indirect calls are "
+                 "expected to point to exactly one definition.");
+          TheFn = AS->getAliaseeVI();
+        }
+      TargetsForSlot.push_back(TheFn);
     }
   }
 
@@ -2261,10 +2273,13 @@ void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) {
     assert(!Res.SingleImplName.empty());
     // The type of the function in the declaration is irrelevant because every
     // call site will cast it to the correct type.
-    Constant *SingleImpl =
-        cast<Constant>(M.getOrInsertFunction(Res.SingleImplName,
-                                             Type::getVoidTy(M.getContext()))
-                           .getCallee());
+    Value *SingleImplVal =
+        M.getOrInsertFunction(Res.SingleImplName,
+                              Type::getVoidTy(M.getContext()))
+            .getCallee();
+    if (auto *A = dyn_cast<GlobalAlias>(SingleImplVal->stripPointerCasts()))
+      SingleImplVal = A->getAliaseeObject();
+    Constant *SingleImpl = cast<Constant>(SingleImplVal);
 
     // This is the import phase so we should not be exporting anything.
     bool IsExported = false;
diff --git a/llvm/test/ThinLTO/X86/devirt_function_alias2.ll b/llvm/test/ThinLTO/X86/devirt_function_alias2.ll
index 3b3208e300aa8..e4f93208fba54 100644
--- a/llvm/test/ThinLTO/X86/devirt_function_alias2.ll
+++ b/llvm/test/ThinLTO/X86/devirt_function_alias2.ll
@@ -24,8 +24,8 @@
 ; RUN:   2>&1 | FileCheck %s --check-prefix=REMARK --check-prefix=PRINT
 ; RUN: llvm-dis %t2.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR1
 
-; PRINT-DAG: Devirtualized call to {{.*}} (_ZN1D1mEiAlias)
-; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEiAlias
+; PRINT-DAG: Devirtualized call to {{.*}} (_ZN1D1mEi)
+; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi
 
 ;; Test hybrid Thin/Regular LTO
 
@@ -63,13 +63,15 @@ target triple = "x86_64-grtev4-linux-gnu"
 
 @_ZTV1D = constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr undef, ptr @_ZN1D1mEiAlias] }, !type !3
 
-define i32 @_ZN1D1mEi(ptr %this, i32 %a) {
+define i32 @_ZN1D1mEi(ptr %this, i32 %a) #0 {
    ret i32 0;
 }
 
 @_ZN1D1mEiAlias = unnamed_addr alias i32 (ptr, i32), ptr @_ZN1D1mEi
 
-; CHECK-IR1-LABEL: define i32 @test
+attributes #0 = { noinline optnone }
+
+; CHECK-IR1-LABEL: define {{.*}}i32 @test
 define i32 @test(ptr %obj2, i32 %a) {
 entry:
   %vtable2 = load ptr, ptr %obj2
diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll
index d8ebae17d4693..9d2ee425031ab 100644
--- a/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll
+++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/cfi-icall-static-inline-asm.ll
@@ -3,7 +3,7 @@
 
 target triple = "x86_64-unknown-linux-gnu"
 
-; CHECK: module asm ".lto_set_conditional a,a.[[HASH:[0-9a-f]+]]"
+; CHECK: @a.[[HASH:[0-9a-f]+]] = hidden alias
 
 define void @b() {
   %f = alloca ptr, align 8
@@ -14,7 +14,7 @@ define void @b() {
   ret void
 }
 
-; CHECK: define{{.*}} @a.[[HASH]](){{.*}} !type
+; CHECK: define internal void @a() {{.*}}!type
 define internal void @a() !type !0 {
   ret void
 }
diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/comdat.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/comdat.ll
index f9545e2010c10..48d371531ed7b 100644
--- a/llvm/test/Transforms/ThinLTOBitcodeWriter/comdat.ll
+++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/comdat.ll
@@ -25,9 +25,8 @@ $nt = comdat any
 ; MERGED-SAME: comdat(${{"?lwt[^ ]+}})
 @lwt_aliasee = private unnamed_addr global [1 x ptr] [ptr null], comdat($lwt), !type !0
 
-; MERGED: {{@"?lwt_nl[^ ]+}} = hidden unnamed_addr global
-; MERGED-SAME: comdat(${{"?lwt[^ ]+}})
-; THIN: {{@"?lwt_nl[^ ]+}} = external hidden
+; MERGED: @lwt_nl = internal unnamed_addr global i32 0, comdat(${{"?lwt[^ ]+}})
+; THIN: {{@"?lwt_nl\.[^ ]+}} = external hidden unnamed_addr global i32
 @lwt_nl = internal unnamed_addr global i32 0, comdat($lwt)
 
 ; MERGED: @nlwt_aliasee = private unnamed_addr global
@@ -47,12 +46,16 @@ $nt = comdat any
 ; THIN-SAME: comdat($nt)
 @nt_nl = internal unnamed_addr global i32 0, comdat($nt)
 
-; MERGED: {{@"?lwt[^ ]+}} = hidden unnamed_addr alias
-; THIN: {{@"?lwt[^ ]+}} = external hidden
+; MERGED: @lwt = internal unnamed_addr alias [1 x ptr], ptr @lwt_aliasee
+; MERGED: @nlwt_nl = internal unnamed_addr alias [1 x ptr], ptr @nlwt_aliasee
+; MERGED: {{@"?lwt_nl\.[^ ]+}} = hidden alias ptr, ptr @lwt_nl
+; MERGED: {{@"?lwt\.[^ ]+}} = hidden alias ptr, ptr @lwt
+; MERGED: {{@"?nlwt_nl\.[^ ]+}} = hidden alias ptr, ptr @nlwt_nl
+
+; THIN: {{@"?lwt\.[^ ]+}} = external hidden global [1 x ptr]
 @lwt = internal unnamed_addr alias [1 x ptr], ptr @lwt_aliasee
 
-; MERGED: {{@"?nlwt_nl[^ ]+}} = hidden unnamed_addr alias
-; THIN: {{@"?nlwt_nl[^ ]+}} = external hidden
+; THIN: {{@"?nlwt_nl\.[^ ]+}} = external hidden global [1 x ptr]
 @nlwt_nl = internal unnamed_addr alias [1 x ptr], ptr @nlwt_aliasee
 
 ; The functions below exist just to make sure the globals are used.
diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll
index 1a6cbb7e7e84b..2c84ab86cac63 100644
--- a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll
+++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal1.ll
@@ -13,7 +13,8 @@
 ; BCA1-NOT: <GLOBALVAL_SUMMARY_BLOCK
 
 ; M0: @g.581d7631532fa146ba4061179da39272 = external hidden global i8
-; M1: @g.581d7631532fa146ba4061179da39272 = hidden global i8 42, !type !0
+; M1: @g = internal global i8 42, !type !0
+; M1: @g.581d7631532fa146ba4061179da39272 = hidden alias ptr, ptr @g
 @g = internal global i8 42, !type !0
 
 ; M0: define ptr @f()
diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll
index 1a7b1aba66576..d9d9f516015d9 100644
--- a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll
+++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-internal2.ll
@@ -20,8 +20,9 @@ target triple = "x86_64-unknown-linux-gnu"
 ; M1: @g = global ptr @f.13757e0fb71915e385efa4dc9d1e08fd, !type !0
 @g = global ptr @f, !type !0
 
-; M0: define hidden void @f.13757e0fb71915e385efa4dc9d1e08fd()
-; M1: declare !guid !4 hidden void @f.13757e0fb71915e385efa4dc9d1e08fd()
+; M0: @f.13757e0fb71915e385efa4dc9d1e08fd = hidden alias ptr, ptr @f
+; M0: define internal void @f()
+; M1: declare !guid !{{[0-9]+}} hidden void @f.13757e0fb71915e385efa4dc9d1e08fd()
 define internal void @f() {
   call void @f2()
   ret void
diff --git a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-vfunc-internal.ll b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-vfunc-internal.ll
index 75932fd84d83e..1d17699be1c3c 100644
--- a/llvm/test/Transforms/ThinLTOBitcodeWriter/split-vfunc-internal.ll
+++ b/llvm/test/Transforms/ThinLTOBitcodeWriter/split-vfunc-internal.ll
@@ -10,12 +10,14 @@ define ptr @source() {
 }
 
 ; M0: @g.84f59439b469192440047efc8de357fb = external hidden constant [1 x ptr]
-; M1: @g.84f59439b469192440047efc8de357fb = hidden constant [1 x ptr] [ptr @ok.84f59439b469192440047efc8de357fb]
+; M1: @g = internal constant [1 x ptr] [ptr @ok.84f59439b469192440047efc8de357fb]
+; M1: @g.84f59439b469192440047efc8de357fb = hidden alias ptr, ptr @g
 @g = internal constant [1 x ptr] [
   ptr @ok
 ], !type !0
 
-; M0: define hidden i64 @ok.84f59439b469192440047efc8de357fb
+; M0: @ok.84f59439b469192440047efc8de357fb = hidden alias ptr, ptr @ok
+; M0: define internal i64 @ok(ptr %this)
 ; M1: define available_externally hidden i64 @ok.84f59439b469192440047efc8de357fb
 define internal i64 @ok(ptr %this) {
   ret i64 42

@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from 525046e to fa7961a Compare June 17, 2026 21:36
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch 2 times, most recently from 1bca656 to 6bc8b3c Compare June 18, 2026 04:14
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from fa7961a to 1817d11 Compare June 18, 2026 04:14
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from 6bc8b3c to ff0a5e1 Compare June 18, 2026 05:19
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch 2 times, most recently from 2829ab3 to a6fdde1 Compare June 18, 2026 19:43
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from ff0a5e1 to e0392b6 Compare June 18, 2026 19:43
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from a6fdde1 to 4da2c32 Compare June 23, 2026 01:23
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch 2 times, most recently from c1b8c2a to 677d378 Compare June 23, 2026 01:28
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch 2 times, most recently from 4bf5379 to 1cbfe8b Compare June 23, 2026 02:06
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from 677d378 to 9b228b5 Compare June 23, 2026 02:06

@teresajohnson teresajohnson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from 9b228b5 to d9d66bd Compare June 30, 2026 01:29
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from 1cbfe8b to 55df97c Compare June 30, 2026 01:29
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch 3 times, most recently from bdc1c87 to ff1fe38 Compare July 6, 2026 14:51
@mtrofin mtrofin force-pushed the users/mtrofin/06-05-reland_184065 branch from 55df97c to e432443 Compare July 6, 2026 14:51
mtrofin added a commit that referenced this pull request Jul 7, 2026
This reverts #201194, thus relanding @orodley's PR #184065 (and
#200323):

> This allows us to keep GUIDs consistent across compilation phases
which may change the name or linkage type.
> See
https://discourse.llvm.org/t/rfc-keep-globalvalue-guids-stable/84801

The CFI issues that triggered the original revert are fixed by #201370,
together with the addressing of the TODOs in `LowerTypeTests.cpp` left
in the latter. The [graphite
diff](https://app.graphite.com/github/pr/llvm/llvm-project/201849/Reland-%23184065)
between this change's V1 and V2 shows what's been added:

- the `TODO`s from #201370 are done
- in LowerTypeTests.cpp, passing `!guid` when creating a new declaration
and when converting a definition to a declaration.
- `llvm/test/Transforms/LowerTypeTests/export-icall.ll` tests also the
above def->decl conversion
- removed
`test/Analysis/CtxProfAnalysis/flatten-prethinlink-requires-guid-metadata.ll`
introduced in #194383 (this was between the revert and this PR), as now
the general expectation is that GUID assignment happens appropriately
and all passes use `getGUID`, so there's no reason for `CtxProfAnalysis`
to do something different.

Currently, we reassign GUIDs when CFI promotes internal linkage symbols,
which is counter to the goal of the RFC. This is addressed in PR
#203171. The reason for this split fix can be explained on
`compiler-rt/test/cfi/icall/wrong-signature-mixed-lto.c`. Here, a module
with the exact same source path is compiled twice, under different
conditional compilation, to produce 2 objects. Each object defines an
internal linkage symbol with the same name (this is
`install_trap_loop_detection` from
`compiler-rt/test/cfi/trap_loop_signal_handler.inc` which is
`-include`\-d by both - see how `%clang_cfi` is defined). The ThinLTO
GUID of this symbol will be the same. Its name won't be - because CFI
promotes it and renames it using a hash that is based on the IR Module
content (rather than the source path). During thinlink,
`LTO::addThinLTO`will mark each of the 2 exported symbols as prevailing
in their corresponding modules. But that is done by associating their
GUID to the module. So whichever comes last wins. The other symbol will
be marked available externally and its body DCEd later in backend. But
each module will refer to its copy of `install_trap_loop_detection`, and
so we end up with a linker error.

As mentioned, the fix is in PR #203171, and this relanding PR just
maintains the existing ThinLTO behavior by rewriting the GUIDs. Since we
haven't yet leveraged the GUID mechanics for e.g. simplifying PGO, this
aspect of this change is essentially NFC.

Co-authored-by: Owen Rodley <orodley@google.com>
Base automatically changed from users/mtrofin/06-05-reland_184065 to main July 7, 2026 23:04
gandhi56 pushed a commit that referenced this pull request Jul 9, 2026
This reverts #201194, thus relanding @orodley's PR #184065 (and
#200323):

> This allows us to keep GUIDs consistent across compilation phases
which may change the name or linkage type.
> See
https://discourse.llvm.org/t/rfc-keep-globalvalue-guids-stable/84801

The CFI issues that triggered the original revert are fixed by #201370,
together with the addressing of the TODOs in `LowerTypeTests.cpp` left
in the latter. The [graphite
diff](https://app.graphite.com/github/pr/llvm/llvm-project/201849/Reland-%23184065)
between this change's V1 and V2 shows what's been added:

- the `TODO`s from #201370 are done
- in LowerTypeTests.cpp, passing `!guid` when creating a new declaration
and when converting a definition to a declaration.
- `llvm/test/Transforms/LowerTypeTests/export-icall.ll` tests also the
above def->decl conversion
- removed
`test/Analysis/CtxProfAnalysis/flatten-prethinlink-requires-guid-metadata.ll`
introduced in #194383 (this was between the revert and this PR), as now
the general expectation is that GUID assignment happens appropriately
and all passes use `getGUID`, so there's no reason for `CtxProfAnalysis`
to do something different.

Currently, we reassign GUIDs when CFI promotes internal linkage symbols,
which is counter to the goal of the RFC. This is addressed in PR
#203171. The reason for this split fix can be explained on
`compiler-rt/test/cfi/icall/wrong-signature-mixed-lto.c`. Here, a module
with the exact same source path is compiled twice, under different
conditional compilation, to produce 2 objects. Each object defines an
internal linkage symbol with the same name (this is
`install_trap_loop_detection` from
`compiler-rt/test/cfi/trap_loop_signal_handler.inc` which is
`-include`\-d by both - see how `%clang_cfi` is defined). The ThinLTO
GUID of this symbol will be the same. Its name won't be - because CFI
promotes it and renames it using a hash that is based on the IR Module
content (rather than the source path). During thinlink,
`LTO::addThinLTO`will mark each of the 2 exported symbols as prevailing
in their corresponding modules. But that is done by associating their
GUID to the module. So whichever comes last wins. The other symbol will
be marked available externally and its body DCEd later in backend. But
each module will refer to its copy of `install_trap_loop_detection`, and
so we end up with a linker error.

As mentioned, the fix is in PR #203171, and this relanding PR just
maintains the existing ThinLTO behavior by rewriting the GUIDs. Since we
haven't yet leveraged the GUID mechanics for e.g. simplifying PGO, this
aspect of this change is essentially NFC.

Co-authored-by: Owen Rodley <orodley@google.com>
@mtrofin mtrofin force-pushed the users/mtrofin/06-10-cfi_creates_alias branch from ff1fe38 to 774d8a8 Compare July 14, 2026 01:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:transforms LTO Link time optimization (regular/full LTO or ThinLTO)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants