[llvm] XCOFF associated metadata (PR #159096)

Sean Fertile via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 4 08:39:23 PST 2025


https://github.com/mandlebug updated https://github.com/llvm/llvm-project/pull/159096

>From c2e71b2d63f988db2d98f9fba533e4147cb8b746 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Mon, 15 Sep 2025 15:46:17 -0400
Subject: [PATCH 01/12] Extend the associated metadata node impl for XCOFF.

Add support for the associated metadata node for AIX.. Map it to
the .ref assembly directive. Placing the node on a symbol will cause
the back end to emit a .ref directive in the section of the global the
metadata is placed on. the .ref will refrence the associated symbol
which will keep it alive if the section is not garbage collected.
---
 llvm/docs/LangRef.rst                         | 30 +++++++++++++++----
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp     | 19 ++++++++++++
 .../PowerPC/aix-associated-metadata.ll        | 23 ++++++++++++++
 3 files changed, 66 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 02865f8a29c67..f86f09d130931 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8337,6 +8337,13 @@ See :doc:`CalleeTypeMetadata`.
 
 The ``associated`` metadata may be attached to a global variable definition with
 a single argument that references a global object (optionally through an alias).
+The metadata is often used with an explicit section consisting of valid C
+identifiers so that the runtime can find the metadata section with
+linker-defined encapsulation symbols ``__start_<section_name>`` and
+``__stop_<section_name>``.
+
+ELF Targets
+"""""""""""
 
 This metadata lowers to the ELF section flag ``SHF_LINK_ORDER`` which prevents
 discarding of the global variable in linker GC unless the referenced object is
@@ -8354,12 +8361,6 @@ alive, but this many-to-one relationship is not representable. Moreover, if the
 metadata is retained while the function is discarded, the linker will report an
 error of a relocation referencing a discarded section.
 
-The metadata is often used with an explicit section consisting of valid C
-identifiers so that the runtime can find the metadata section with
-linker-defined encapsulation symbols ``__start_<section_name>`` and
-``__stop_<section_name>``.
-
-It does not have any effect on non-ELF targets.
 
 Example:
 
@@ -8370,6 +8371,23 @@ Example:
     @b = internal global i32 2, comdat $a, section "abc", !associated !0
     !0 = !{ptr @a}
 
+XCOFF Targets
+"""""""""""""
+
+This metadata lowers to the .ref assembly directive which will add a relocation
+representing an implicit reference from the section the global belongs to, to
+the associated symbol. This link will keep the associated symbol alive if the
+section is not garbage collected. More than one associated node can be attached
+to the same global variable.
+
+Example:
+.. code-block:: text
+
+    @a = global i32 1
+    @b = global i32 2
+    @c = global i32 3, section "abc", !associated !0, !associated !1
+    !0 = !{ptr @a}
+    !1 = !{ptr @b}
 
 '``prof``' Metadata
 ^^^^^^^^^^^^^^^^^^^
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 122738caa6827..6b045947c4d4f 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -305,6 +305,8 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter {
   void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override;
 
   void emitModuleCommandLines(Module &M) override;
+
+  void emitAssociatedMetadata(const GlobalObject *);
 };
 
 } // end anonymous namespace
@@ -2801,6 +2803,10 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) {
   // Switch to the containing csect.
   OutStreamer->switchSection(Csect);
 
+  if (GV->hasMetadata(LLVMContext::MD_associated)) {
+    emitAssociatedMetadata(GV);
+  }
+
   const DataLayout &DL = GV->getDataLayout();
 
   // Handle common and zero-initialized local symbols.
@@ -3336,6 +3342,19 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV,
     OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding));
 }
 
+void PPCAIXAsmPrinter::emitAssociatedMetadata(const GlobalObject *GO) {
+  SmallVector< MDNode * > MDs;
+  GO->getMetadata(LLVMContext::MD_associated, MDs);
+  assert(MDs.size() && "Expected asscoiated metadata nodes");
+
+  for (const MDNode *MD : MDs) {
+    const ValueAsMetadata *VAM = cast<ValueAsMetadata>(MD->getOperand(0).get());
+    const GlobalValue *Associated = cast<GlobalValue>(VAM->getValue());
+    MCSymbol *Referenced = TM.getSymbol(Associated);
+    OutStreamer->emitXCOFFRefDirective(Referenced);
+  }
+}
+
 // Return a pass that prints the PPC assembly code for a MachineFunction to the
 // given output stream.
 static AsmPrinter *
diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
new file mode 100644
index 0000000000000..edf157934b205
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
@@ -0,0 +1,23 @@
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global i32 3, section "custom_section_c"
+ at d = global i32 4, !associated !0
+ at e = constant i32 5, !associated !1, !associated !2
+ at f = global i32 6, section "custom_section_f", !associated !1
+
+
+!0 = !{ptr @a}
+!1 = !{ptr @b}
+!2 = !{ptr @c}
+
+; CHECK: .csect d[RW]
+; CHECK: .ref a[RW]
+
+; CHECK: .csect e[RO]
+; CHECK: .ref b[RW]
+; CHECK: .ref c
+
+; CHECK: .csect custom_section_f[RW]
+; CHECK: .ref b[RW]

>From a48a304508ff24ce97124e4cc056b411d9085e46 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Tue, 16 Sep 2025 09:37:13 -0400
Subject: [PATCH 02/12] Run clang-format over changes.

---
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 6b045947c4d4f..14ed96bc07b0e 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -3343,7 +3343,7 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV,
 }
 
 void PPCAIXAsmPrinter::emitAssociatedMetadata(const GlobalObject *GO) {
-  SmallVector< MDNode * > MDs;
+  SmallVector<MDNode *> MDs;
   GO->getMetadata(LLVMContext::MD_associated, MDs);
   assert(MDs.size() && "Expected asscoiated metadata nodes");
 

>From 28feedef41365a18bf67d68c52319cbb7f097365 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Thu, 18 Sep 2025 10:38:07 -0400
Subject: [PATCH 03/12] Test .ref impl with data sections disabled.

---
 .../PowerPC/aix-associated-metadata.ll        | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
index edf157934b205..8a5c36fd2b4b8 100644
--- a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
+++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
@@ -1,4 +1,5 @@
 ; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s
+; RUN: llc -data-sections=false -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck -check-prefix=NODATA %s
 
 @a = global i32 1
 @b = global i32 2
@@ -21,3 +22,21 @@
 
 ; CHECK: .csect custom_section_f[RW]
 ; CHECK: .ref b[RW]
+
+; NODATA:      .csect .data[RW]
+; NODATA-NOT:  .csect
+; NODATA:      .globl a
+; NODATA-NOT:  .csect
+; NODATA:      .globl b
+; NODATA:      .csect custom_section_c[RW]
+; NODATA:      .globl c
+; NODATA:      .csect .data[RW]
+; NODATA:      .ref a
+; NODATA:      .globl d
+; NODATA:      .csect .rodata[RO]
+; NODATA:      .ref b
+; NODATA:      .ref c
+; NODATA:      .globl e
+; NODATA:      .csect custom_section_f[RW]
+; NODATA:      .ref b
+; NODATA:      .globl f

>From bae536431eef172553b7993245926da64acb13d9 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Thu, 18 Sep 2025 11:03:20 -0400
Subject: [PATCH 04/12] Check multiple associated metadata nodes in the
 verifier.

---
 llvm/lib/IR/Verifier.cpp                  | 47 ++++++++++++++---------
 llvm/test/Verifier/associated-multiple.ll | 13 +++++++
 2 files changed, 42 insertions(+), 18 deletions(-)
 create mode 100644 llvm/test/Verifier/associated-multiple.ll

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index a1e14d8f25bf7..983db82a1c22f 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -740,25 +740,36 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
         "Global is external, but doesn't have external or weak linkage!", &GV);
 
   if (const GlobalObject *GO = dyn_cast<GlobalObject>(&GV)) {
-    if (const MDNode *Associated =
-            GO->getMetadata(LLVMContext::MD_associated)) {
-      Check(Associated->getNumOperands() == 1,
-            "associated metadata must have one operand", &GV, Associated);
-      const Metadata *Op = Associated->getOperand(0).get();
-      Check(Op, "associated metadata must have a global value", GO, Associated);
-
-      const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
-      Check(VM, "associated metadata must be ValueAsMetadata", GO, Associated);
-      if (VM) {
-        Check(isa<PointerType>(VM->getValue()->getType()),
-              "associated value must be pointer typed", GV, Associated);
-
-        const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases();
-        Check(isa<GlobalObject>(Stripped) || isa<Constant>(Stripped),
-              "associated metadata must point to a GlobalObject", GO, Stripped);
-        Check(Stripped != GO,
-              "global values should not associate to themselves", GO,
+    if (GO->hasMetadata(LLVMContext::MD_associated)) {
+      SmallVector<MDNode *, 1> MDs;
+      GO->getMetadata(LLVMContext::MD_associated, MDs);
+
+      if (TT.isOSBinFormatELF())
+        Check(MDs.size() == 1, "only a single associated metadata is supported",
+              &GV);
+
+      for (const MDNode *Associated : MDs) {
+        Check(Associated->getNumOperands() == 1,
+              "associated metadata must have one operand", &GV, Associated);
+        const Metadata *Op = Associated->getOperand(0).get();
+        Check(Op, "associated metadata must have a global value", GO,
               Associated);
+
+        const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
+        Check(VM, "associated metadata must be ValueAsMetadata", GO,
+              Associated);
+        if (VM) {
+          Check(isa<PointerType>(VM->getValue()->getType()),
+                "associated value must be pointer typed", GV, Associated);
+
+          const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases();
+          Check(isa<GlobalObject>(Stripped) || isa<Constant>(Stripped),
+                "associated metadata must point to a GlobalObject", GO,
+                Stripped);
+          Check(Stripped != GO,
+                "global values should not associate to themselves", GO,
+                Associated);
+        }
       }
     }
 
diff --git a/llvm/test/Verifier/associated-multiple.ll b/llvm/test/Verifier/associated-multiple.ll
new file mode 100644
index 0000000000000..5a640fbe5efed
--- /dev/null
+++ b/llvm/test/Verifier/associated-multiple.ll
@@ -0,0 +1,13 @@
+; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s
+
+target triple = "unknown-unknown-linux-gnu"
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global i32 3, !associated !0, !associated !1
+
+!0 = !{ptr @a}
+!1 = !{ptr @b}
+
+; CHECK: only a single associated metadata is supported
+; CHECK: ptr @c

>From 0b4808cc168827bca665fdfff8cd70b2d670b4df Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Thu, 18 Sep 2025 16:31:35 -0400
Subject: [PATCH 05/12] Add testing of the integrated assembler path.

---
 .../PowerPC/aix-associated-metadata.ll        | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
index 8a5c36fd2b4b8..8613b32f06e05 100644
--- a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
+++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
@@ -1,6 +1,12 @@
 ; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s
 ; RUN: llc -data-sections=false -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck -check-prefix=NODATA %s
 
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s
+; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=OBJ %s
+
+; RUN: llc -data-sections=false -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s
+; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=OBJ-NODATA %s
+
 @a = global i32 1
 @b = global i32 2
 @c = global i32 3, section "custom_section_c"
@@ -40,3 +46,32 @@
 ; NODATA:      .csect custom_section_f[RW]
 ; NODATA:      .ref b
 ; NODATA:      .globl f
+
+; OBJ: Disassembly of section .text:
+
+; OBJ:   e[RO]:
+; OBJ:     R_REF {{.*}} b[RW]
+; OBJ:     R_REF {{.*}} c
+
+; OBJ: Disassembly of section .data:
+; OBJ:   a[RW]:
+; OBJ:   b[RW]:
+; OBJ:   c:
+; OBJ:   d[RW]:
+; OBJ:     R_REF {{.*}} a[RW]
+; OBJ:   f:
+; OBJ:     R_REF {{.*}} b[RW]
+
+; OBJ-NODATA: Disassembly of section .text:
+; OBJ-NODATA:   e:
+; OBJ-NODATA:     R_REF {{.*}} b
+; OBJ-NODATA:     R_REF {{.*}} c
+
+; OBJ-NODATA: Disassembly of section .data:
+; OBJ-NODATA:   a:
+; OBJ-NODATA:     R_REF {{.*}} a
+; OBJ-NODATA:   b:
+; OBJ-NODATA:   d:
+; OBJ-NODATA:   c:
+; OBJ-NODATA:   f:
+; OBJ-NODATA:     R_REF {{.*}} b

>From ecf915e61670c9631baaf2a52b97e402dd2e08bf Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 24 Sep 2025 14:27:56 -0400
Subject: [PATCH 06/12] Add a new metadata node rather then piggybacking on
 associated.

---
 llvm/docs/LangRef.rst                         | 60 +++++++++-------
 llvm/include/llvm/IR/FixedMetadataKinds.def   |  1 +
 llvm/lib/IR/Verifier.cpp                      | 71 +++++++++++--------
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp     | 14 ++--
 .../PowerPC/aix-associated-metadata.ll        |  6 +-
 llvm/test/Verifier/associated-multiple.ll     | 13 ----
 llvm/test/Verifier/ref.ll                     | 28 ++++++++
 7 files changed, 117 insertions(+), 76 deletions(-)
 delete mode 100644 llvm/test/Verifier/associated-multiple.ll
 create mode 100644 llvm/test/Verifier/ref.ll

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index f86f09d130931..2ad9a9b362f19 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8337,13 +8337,6 @@ See :doc:`CalleeTypeMetadata`.
 
 The ``associated`` metadata may be attached to a global variable definition with
 a single argument that references a global object (optionally through an alias).
-The metadata is often used with an explicit section consisting of valid C
-identifiers so that the runtime can find the metadata section with
-linker-defined encapsulation symbols ``__start_<section_name>`` and
-``__stop_<section_name>``.
-
-ELF Targets
-"""""""""""
 
 This metadata lowers to the ELF section flag ``SHF_LINK_ORDER`` which prevents
 discarding of the global variable in linker GC unless the referenced object is
@@ -8361,6 +8354,12 @@ alive, but this many-to-one relationship is not representable. Moreover, if the
 metadata is retained while the function is discarded, the linker will report an
 error of a relocation referencing a discarded section.
 
+The metadata is often used with an explicit section consisting of valid C
+identifiers so that the runtime can find the metadata section with
+linker-defined encapsulation symbols ``__start_<section_name>`` and
+``__stop_<section_name>``.
+
+It does not have any effect on non-ELF targets.
 
 Example:
 
@@ -8371,23 +8370,6 @@ Example:
     @b = internal global i32 2, comdat $a, section "abc", !associated !0
     !0 = !{ptr @a}
 
-XCOFF Targets
-"""""""""""""
-
-This metadata lowers to the .ref assembly directive which will add a relocation
-representing an implicit reference from the section the global belongs to, to
-the associated symbol. This link will keep the associated symbol alive if the
-section is not garbage collected. More than one associated node can be attached
-to the same global variable.
-
-Example:
-.. code-block:: text
-
-    @a = global i32 1
-    @b = global i32 2
-    @c = global i32 3, section "abc", !associated !0, !associated !1
-    !0 = !{ptr @a}
-    !1 = !{ptr @b}
 
 '``prof``' Metadata
 ^^^^^^^^^^^^^^^^^^^
@@ -8688,6 +8670,36 @@ denoting if the type contains a pointer.
 
   !0 = !{!"<type-name>", i1 <contains-pointer>}
 
+'``ref``' Metadata
+^^^^^^^^^^^^^^^^^^
+
+The ``ref`` metadata may be attached to a global variable definition with a
+single argument that references a global object. The metadata is lowered to a
+.ref directive which will emit a relocation introducing an explicit dependence
+to the referenced symbol. This is typically used when there is some implicit
+dependence between the symbols that is otherwise opaque to the linker. One such
+example is metadata which is accessed by a runtime with associated
+``__start_<section_name>`` and ``__stop_<section_name>`` symbols.
+
+This metadata lowers to the .ref assembly directive which will add a relocation
+representing an implicit reference from the section the global belongs to, to
+the associated symbol. This link will keep the associated symbol alive if the
+section is not garbage collected. More than one associated node can be attached
+to the same global variable.
+
+It does not have any effect on non-XCOFF targets.
+
+Example:
+
+.. code-block:: text
+
+    @a = global i32 1
+    @b = global i32 2
+    @c = global i32 3, section "abc", !ref !0, !ref !1
+    !0 = !{ptr @a}
+    !1 = !{ptr @b}
+
+
 Module Flags Metadata
 =====================
 
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index 74746cced6f23..b65a5bb8ab272 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -57,3 +57,4 @@ LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
 LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
 LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
+LLVM_FIXED_MD_KIND(MD_ref, "ref", 46)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 983db82a1c22f..ea37bbcc13e11 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -740,36 +740,25 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
         "Global is external, but doesn't have external or weak linkage!", &GV);
 
   if (const GlobalObject *GO = dyn_cast<GlobalObject>(&GV)) {
-    if (GO->hasMetadata(LLVMContext::MD_associated)) {
-      SmallVector<MDNode *, 1> MDs;
-      GO->getMetadata(LLVMContext::MD_associated, MDs);
-
-      if (TT.isOSBinFormatELF())
-        Check(MDs.size() == 1, "only a single associated metadata is supported",
-              &GV);
-
-      for (const MDNode *Associated : MDs) {
-        Check(Associated->getNumOperands() == 1,
-              "associated metadata must have one operand", &GV, Associated);
-        const Metadata *Op = Associated->getOperand(0).get();
-        Check(Op, "associated metadata must have a global value", GO,
+    if (const MDNode *Associated =
+            GO->getMetadata(LLVMContext::MD_associated)) {
+      Check(Associated->getNumOperands() == 1,
+            "associated metadata must have one operand", &GV, Associated);
+      const Metadata *Op = Associated->getOperand(0).get();
+      Check(Op, "associated metadata must have a global value", GO, Associated);
+
+      const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
+      Check(VM, "associated metadata must be ValueAsMetadata", GO, Associated);
+      if (VM) {
+        Check(isa<PointerType>(VM->getValue()->getType()),
+              "associated value must be pointer typed", GV, Associated);
+
+        const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases();
+        Check(isa<GlobalObject>(Stripped) || isa<Constant>(Stripped),
+              "associated metadata must point to a GlobalObject", GO, Stripped);
+        Check(Stripped != GO,
+              "global values should not associate to themselves", GO,
               Associated);
-
-        const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
-        Check(VM, "associated metadata must be ValueAsMetadata", GO,
-              Associated);
-        if (VM) {
-          Check(isa<PointerType>(VM->getValue()->getType()),
-                "associated value must be pointer typed", GV, Associated);
-
-          const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases();
-          Check(isa<GlobalObject>(Stripped) || isa<Constant>(Stripped),
-                "associated metadata must point to a GlobalObject", GO,
-                Stripped);
-          Check(Stripped != GO,
-                "global values should not associate to themselves", GO,
-                Associated);
-        }
       }
     }
 
@@ -780,6 +769,30 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
                               DL.getIntPtrType(GO->getType()),
                               RangeLikeMetadataKind::AbsoluteSymbol);
     }
+
+    if (GO->hasMetadata(LLVMContext::MD_ref)) {
+      SmallVector<MDNode *> MDs;
+      GO->getMetadata(LLVMContext::MD_ref, MDs);
+      for (const MDNode *MD : MDs) {
+        Check(MD->getNumOperands() == 1, "ref metadata must have one operand",
+              &GV, MD);
+        const Metadata *Op = MD->getOperand(0).get();
+        Check(Op, "ref metadata must have a global value", GO, MD);
+
+        const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
+        Check(VM, "ref metadata must be ValueAsMetadata", GO, MD);
+        if (VM) {
+          Check(isa<PointerType>(VM->getValue()->getType()),
+                "ref value must be pointer typed", GV, MD);
+
+          const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases();
+          Check(isa<GlobalObject>(Stripped) || isa<Constant>(Stripped),
+                "ref metadata must point to a GlobalObject", GO, Stripped);
+          Check(Stripped != GO, "values should not reference themselves", GO,
+                MD);
+        }
+      }
+    }
   }
 
   Check(!GV.hasAppendingLinkage() || isa<GlobalVariable>(GV),
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 14ed96bc07b0e..8e6a0a4f2e48b 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -306,7 +306,7 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter {
 
   void emitModuleCommandLines(Module &M) override;
 
-  void emitAssociatedMetadata(const GlobalObject *);
+  void emitRefMetadata(const GlobalObject *);
 };
 
 } // end anonymous namespace
@@ -2803,8 +2803,8 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) {
   // Switch to the containing csect.
   OutStreamer->switchSection(Csect);
 
-  if (GV->hasMetadata(LLVMContext::MD_associated)) {
-    emitAssociatedMetadata(GV);
+  if (GV->hasMetadata(LLVMContext::MD_ref)) {
+    emitRefMetadata(GV);
   }
 
   const DataLayout &DL = GV->getDataLayout();
@@ -3342,15 +3342,15 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV,
     OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding));
 }
 
-void PPCAIXAsmPrinter::emitAssociatedMetadata(const GlobalObject *GO) {
+void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) {
   SmallVector<MDNode *> MDs;
-  GO->getMetadata(LLVMContext::MD_associated, MDs);
+  GO->getMetadata(LLVMContext::MD_ref, MDs);
   assert(MDs.size() && "Expected asscoiated metadata nodes");
 
   for (const MDNode *MD : MDs) {
     const ValueAsMetadata *VAM = cast<ValueAsMetadata>(MD->getOperand(0).get());
-    const GlobalValue *Associated = cast<GlobalValue>(VAM->getValue());
-    MCSymbol *Referenced = TM.getSymbol(Associated);
+    const GlobalValue *GV = cast<GlobalValue>(VAM->getValue());
+    MCSymbol *Referenced = TM.getSymbol(GV);
     OutStreamer->emitXCOFFRefDirective(Referenced);
   }
 }
diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
index 8613b32f06e05..1eb7eaa3c58d2 100644
--- a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
+++ b/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
@@ -10,9 +10,9 @@
 @a = global i32 1
 @b = global i32 2
 @c = global i32 3, section "custom_section_c"
- at d = global i32 4, !associated !0
- at e = constant i32 5, !associated !1, !associated !2
- at f = global i32 6, section "custom_section_f", !associated !1
+ at d = global i32 4, !ref !0
+ at e = constant i32 5, !ref !1, !ref!2
+ at f = global i32 6, section "custom_section_f", !ref !1
 
 
 !0 = !{ptr @a}
diff --git a/llvm/test/Verifier/associated-multiple.ll b/llvm/test/Verifier/associated-multiple.ll
deleted file mode 100644
index 5a640fbe5efed..0000000000000
--- a/llvm/test/Verifier/associated-multiple.ll
+++ /dev/null
@@ -1,13 +0,0 @@
-; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s
-
-target triple = "unknown-unknown-linux-gnu"
-
- at a = global i32 1
- at b = global i32 2
- at c = global i32 3, !associated !0, !associated !1
-
-!0 = !{ptr @a}
-!1 = !{ptr @b}
-
-; CHECK: only a single associated metadata is supported
-; CHECK: ptr @c
diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll
new file mode 100644
index 0000000000000..5f5dca0f965d6
--- /dev/null
+++ b/llvm/test/Verifier/ref.ll
@@ -0,0 +1,28 @@
+; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s
+
+ at a = global i32 1, !ref !0
+ at b = global i32 2, !ref !1
+ at c = global i32 3, !ref !1, !ref !2
+ at d = global i32 4, !ref !3
+
+!0 = !{i32 1}
+!1 = !{ptr @b}
+!2 = !{!"Hello World!"}
+!3 = !{ptr @c, ptr @a}
+
+; CHECK: ref value must be pointer typed
+; CHECK: ptr @a
+; CHECK: !0 = !{i32 1}
+
+; CHECK: values should not reference themselves
+; CHECK: ptr @b
+; CHECK: !1 = !{ptr @b}
+
+; CHECK: ref metadata must be ValueAsMetadata
+; CHECK: ptr @c
+; CHECK: !2 = !{!"Hello World!"}
+
+; CHECK: ref metadata must have one operand
+; CHECK: ptr @d
+; CHECK: !3 = !{ptr @c, ptr @a}
+

>From d9ec86e472c0f0efb6fc2d9c9a6f30b5d3868704 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Tue, 30 Sep 2025 11:04:49 -0400
Subject: [PATCH 07/12] Address review comments and minor cleanup.

* Added support for emitting ref metadata on functions.
* Added testing for ref metadata on functions.
* Fixed up lang ref description.
* Renamed tests to match metadatas nameing.
---
 llvm/docs/LangRef.rst                         | 22 ++++++-------
 llvm/lib/IR/Verifier.cpp                      |  2 --
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp     |  8 ++++-
 llvm/test/CodeGen/PowerPC/aix-func-ref.ll     | 31 +++++++++++++++++++
 ...ciated-metadata.ll => aix-ref-metadata.ll} |  0
 llvm/test/Verifier/ref-func.ll                | 12 +++++++
 llvm/test/Verifier/ref.ll                     |  1 -
 7 files changed, 61 insertions(+), 15 deletions(-)
 create mode 100644 llvm/test/CodeGen/PowerPC/aix-func-ref.ll
 rename llvm/test/CodeGen/PowerPC/{aix-associated-metadata.ll => aix-ref-metadata.ll} (100%)
 create mode 100644 llvm/test/Verifier/ref-func.ll

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 2ad9a9b362f19..33b2fba50011b 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8673,21 +8673,21 @@ denoting if the type contains a pointer.
 '``ref``' Metadata
 ^^^^^^^^^^^^^^^^^^
 
-The ``ref`` metadata may be attached to a global variable definition with a
-single argument that references a global object. The metadata is lowered to a
-.ref directive which will emit a relocation introducing an explicit dependence
-to the referenced symbol. This is typically used when there is some implicit
-dependence between the symbols that is otherwise opaque to the linker. One such
-example is metadata which is accessed by a runtime with associated
-``__start_<section_name>`` and ``__stop_<section_name>`` symbols.
+The ``ref`` metadata may be attached to a function or global variable
+definition with a single argument that references a global object.
+This is typically used when there is some implicit dependence between the
+symbols that is otherwise opaque to the linker. One such example is metadata
+which is accessed by a runtime with associated ``__start_<section_name>`` and
+``__stop_<section_name>`` symbols.
+
+It does not have any effect on non-XCOFF targets.
 
 This metadata lowers to the .ref assembly directive which will add a relocation
 representing an implicit reference from the section the global belongs to, to
-the associated symbol. This link will keep the associated symbol alive if the
-section is not garbage collected. More than one associated node can be attached
-to the same global variable.
+the associated symbol. This link will keep the referenced symbol alive if the
+section is not garbage collected. More than one ref node can be attached
+to the same function or global variable.
 
-It does not have any effect on non-XCOFF targets.
 
 Example:
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index ea37bbcc13e11..5b25c7d2f647c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -777,8 +777,6 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
         Check(MD->getNumOperands() == 1, "ref metadata must have one operand",
               &GV, MD);
         const Metadata *Op = MD->getOperand(0).get();
-        Check(Op, "ref metadata must have a global value", GO, MD);
-
         const auto *VM = dyn_cast_or_null<ValueAsMetadata>(Op);
         Check(VM, "ref metadata must be ValueAsMetadata", GO, MD);
         if (VM) {
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 8e6a0a4f2e48b..e4d113ca660e5 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -2899,10 +2899,16 @@ void PPCAIXAsmPrinter::emitFunctionEntryLabel() {
   if (!TM.getFunctionSections() || MF->getFunction().hasSection())
     PPCAsmPrinter::emitFunctionEntryLabel();
 
+  const Function *F = &MF->getFunction();
+
   // Emit aliasing label for function entry point label.
-  for (const GlobalAlias *Alias : GOAliasMap[&MF->getFunction()])
+  for (const GlobalAlias *Alias : GOAliasMap[F])
     OutStreamer->emitLabel(
         getObjFileLowering().getFunctionEntryPointSymbol(Alias, TM));
+
+  if (F->hasMetadata(LLVMContext::MD_ref)) {
+    emitRefMetadata(F);
+  }
 }
 
 void PPCAIXAsmPrinter::emitPGORefs(Module &M) {
diff --git a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
new file mode 100644
index 0000000000000..d4a47fdafa781
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
@@ -0,0 +1,31 @@
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff < %s | \
+; RUN: FileCheck %s -check-prefixes=NOFSECTS,CHECK
+
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff --function-sections < %s | \
+; RUN: FileCheck %s -check-prefixes=FSECTS,CHECK
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global i32 3
+
+define i32 @foo() !ref !0 {
+  ret i32 0
+}
+
+define i32 @bar() !ref !1 !ref !2 {
+  ret i32 0
+}
+
+!0 = !{ptr @a}
+!1 = !{ptr @b}
+!2 = !{ptr @c}
+
+; NOFSECTS:  .foo:
+; FSECTS:    .csect .foo[PR]
+; CHECK:       .ref a[RW]
+
+; NOFSECTS:  .bar:
+; FSECTS:    .csect .bar[PR]
+; CHECK:       .ref b[RW]
+; CHECK:       .ref c[RW]
+
diff --git a/llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
similarity index 100%
rename from llvm/test/CodeGen/PowerPC/aix-associated-metadata.ll
rename to llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
diff --git a/llvm/test/Verifier/ref-func.ll b/llvm/test/Verifier/ref-func.ll
new file mode 100644
index 0000000000000..9a7c7b5d3405e
--- /dev/null
+++ b/llvm/test/Verifier/ref-func.ll
@@ -0,0 +1,12 @@
+; RUN: llvm-as < %s -o /dev/null 2>&1
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global i32 3, !ref !0
+
+define i32 @foo() !ref !1 {
+  ret i32 0
+}
+
+!0 = !{ptr @a}
+!1 = !{ptr @b}
diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll
index 5f5dca0f965d6..c00b4bf7577ab 100644
--- a/llvm/test/Verifier/ref.ll
+++ b/llvm/test/Verifier/ref.ll
@@ -25,4 +25,3 @@
 ; CHECK: ref metadata must have one operand
 ; CHECK: ptr @d
 ; CHECK: !3 = !{ptr @c, ptr @a}
-

>From 7228f5d9312d9b14f513a4310c1d6879b65d0ed0 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 1 Oct 2025 11:32:40 -0400
Subject: [PATCH 08/12] Add object file testing of ref reloc in functions.

---
 llvm/test/CodeGen/PowerPC/aix-func-ref.ll | 24 +++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
index d4a47fdafa781..72b24862b751f 100644
--- a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
+++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
@@ -4,6 +4,12 @@
 ; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff --function-sections < %s | \
 ; RUN: FileCheck %s -check-prefixes=FSECTS,CHECK
 
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -filetype=obj -o %t.o < %s
+; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=OBJ %s
+
+; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff --function-sections -filetype=obj -o %t.o < %s
+; RUN: llvm-objdump -D -r --symbol-description %t.o | FileCheck -check-prefix=FSECTOBJ %s
+
 @a = global i32 1
 @b = global i32 2
 @c = global i32 3
@@ -29,3 +35,21 @@ define i32 @bar() !ref !1 !ref !2 {
 ; CHECK:       .ref b[RW]
 ; CHECK:       .ref c[RW]
 
+; OBJ: Disassembly of section .text:
+; OBJ: .foo:
+; OBJ:   li 3, 0
+; OBJ:   R_REF {{.*}} a[RW]
+; OBJ:   R_REF {{.*}} b[RW]
+; OBJ:   R_REF {{.*}} c[RW]
+; OBJ:   blr
+; OBJ: .bar
+
+; FSECTOBJ: .foo[PR]:
+; FSECTOBJ:   li 3, 0
+; FSECTOBJ:   R_REF {{.*}} a[RW]
+; FSECTOBJ:   blr
+; FSECTOBJ: .bar[PR]:
+; FSECTOBJ:   li 3, 0
+; FSECTOBJ:   R_REF {{.*}} b[RW]
+; FSECTOBJ:   R_REF {{.*}} c[RW]
+; FSECTOBJ:   blr

>From 84f0277ed71e63d250c01b2e4622e612e6194ab9 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 1 Oct 2025 13:42:36 -0400
Subject: [PATCH 09/12] Verify ref metadata node is not placed on a
 declaration.

---
 llvm/lib/IR/Verifier.cpp  | 3 +++
 llvm/test/Verifier/ref.ll | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 5b25c7d2f647c..ba370252012fc 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -771,6 +771,9 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
     }
 
     if (GO->hasMetadata(LLVMContext::MD_ref)) {
+      Check(!GO->isDeclaration(),
+            "ref metadata must not be placed on a declaration", GO);
+
       SmallVector<MDNode *> MDs;
       GO->getMetadata(LLVMContext::MD_ref, MDs);
       for (const MDNode *MD : MDs) {
diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll
index c00b4bf7577ab..d3b5169062662 100644
--- a/llvm/test/Verifier/ref.ll
+++ b/llvm/test/Verifier/ref.ll
@@ -4,6 +4,7 @@
 @b = global i32 2, !ref !1
 @c = global i32 3, !ref !1, !ref !2
 @d = global i32 4, !ref !3
+ at e = external global i32, !ref !1
 
 !0 = !{i32 1}
 !1 = !{ptr @b}
@@ -25,3 +26,6 @@
 ; CHECK: ref metadata must have one operand
 ; CHECK: ptr @d
 ; CHECK: !3 = !{ptr @c, ptr @a}
+
+; CHECK: ref metadata must not be placed on a declaration
+; CHECK: @e

>From c9ed9d9e50aa59cca357607ac78aa554433e4e8d Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Mon, 6 Oct 2025 14:56:05 -0400
Subject: [PATCH 10/12] Rename metadata from ref to implicit.ref.

---
 llvm/docs/LangRef.rst                         |  9 +++++----
 llvm/include/llvm/IR/FixedMetadataKinds.def   |  2 +-
 llvm/lib/IR/Verifier.cpp                      |  4 ++--
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp     |  6 +++---
 llvm/test/CodeGen/PowerPC/aix-func-ref.ll     |  4 ++--
 llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll |  6 +++---
 llvm/test/Verifier/ref-func.ll                |  4 ++--
 llvm/test/Verifier/ref.ll                     | 10 +++++-----
 8 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 33b2fba50011b..518b0f9d48a54 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8670,10 +8670,11 @@ denoting if the type contains a pointer.
 
   !0 = !{!"<type-name>", i1 <contains-pointer>}
 
-'``ref``' Metadata
-^^^^^^^^^^^^^^^^^^
+'``implicit.ref``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 
-The ``ref`` metadata may be attached to a function or global variable
+The ``implicit.ref`` metadata may be attached to a function or global variable
 definition with a single argument that references a global object.
 This is typically used when there is some implicit dependence between the
 symbols that is otherwise opaque to the linker. One such example is metadata
@@ -8695,7 +8696,7 @@ Example:
 
     @a = global i32 1
     @b = global i32 2
-    @c = global i32 3, section "abc", !ref !0, !ref !1
+    @c = global i32 3, section "abc", !implicit.ref !0, !implicit.ref !1
     !0 = !{ptr @a}
     !1 = !{ptr @b}
 
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index b65a5bb8ab272..01d22bdd7c996 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -57,4 +57,4 @@ LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
 LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
 LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
-LLVM_FIXED_MD_KIND(MD_ref, "ref", 46)
+LLVM_FIXED_MD_KIND(MD_implicit_ref, "implicit.ref", 46)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index ba370252012fc..6f2b056be492e 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -770,12 +770,12 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
                               RangeLikeMetadataKind::AbsoluteSymbol);
     }
 
-    if (GO->hasMetadata(LLVMContext::MD_ref)) {
+    if (GO->hasMetadata(LLVMContext::MD_implicit_ref)) {
       Check(!GO->isDeclaration(),
             "ref metadata must not be placed on a declaration", GO);
 
       SmallVector<MDNode *> MDs;
-      GO->getMetadata(LLVMContext::MD_ref, MDs);
+      GO->getMetadata(LLVMContext::MD_implicit_ref, MDs);
       for (const MDNode *MD : MDs) {
         Check(MD->getNumOperands() == 1, "ref metadata must have one operand",
               &GV, MD);
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index e4d113ca660e5..718d51c5a0673 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -2803,7 +2803,7 @@ void PPCAIXAsmPrinter::emitGlobalVariableHelper(const GlobalVariable *GV) {
   // Switch to the containing csect.
   OutStreamer->switchSection(Csect);
 
-  if (GV->hasMetadata(LLVMContext::MD_ref)) {
+  if (GV->hasMetadata(LLVMContext::MD_implicit_ref)) {
     emitRefMetadata(GV);
   }
 
@@ -2906,7 +2906,7 @@ void PPCAIXAsmPrinter::emitFunctionEntryLabel() {
     OutStreamer->emitLabel(
         getObjFileLowering().getFunctionEntryPointSymbol(Alias, TM));
 
-  if (F->hasMetadata(LLVMContext::MD_ref)) {
+  if (F->hasMetadata(LLVMContext::MD_implicit_ref)) {
     emitRefMetadata(F);
   }
 }
@@ -3350,7 +3350,7 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV,
 
 void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) {
   SmallVector<MDNode *> MDs;
-  GO->getMetadata(LLVMContext::MD_ref, MDs);
+  GO->getMetadata(LLVMContext::MD_implicit_ref, MDs);
   assert(MDs.size() && "Expected asscoiated metadata nodes");
 
   for (const MDNode *MD : MDs) {
diff --git a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
index 72b24862b751f..5cb0575f370e1 100644
--- a/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
+++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
@@ -14,11 +14,11 @@
 @b = global i32 2
 @c = global i32 3
 
-define i32 @foo() !ref !0 {
+define i32 @foo() !implicit.ref !0 {
   ret i32 0
 }
 
-define i32 @bar() !ref !1 !ref !2 {
+define i32 @bar() !implicit.ref !1 !implicit.ref !2 {
   ret i32 0
 }
 
diff --git a/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
index 1eb7eaa3c58d2..7dc9466a7ba9b 100644
--- a/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
+++ b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
@@ -10,9 +10,9 @@
 @a = global i32 1
 @b = global i32 2
 @c = global i32 3, section "custom_section_c"
- at d = global i32 4, !ref !0
- at e = constant i32 5, !ref !1, !ref!2
- at f = global i32 6, section "custom_section_f", !ref !1
+ at d = global i32 4, !implicit.ref !0
+ at e = constant i32 5, !implicit.ref !1, !implicit.ref!2
+ at f = global i32 6, section "custom_section_f", !implicit.ref !1
 
 
 !0 = !{ptr @a}
diff --git a/llvm/test/Verifier/ref-func.ll b/llvm/test/Verifier/ref-func.ll
index 9a7c7b5d3405e..e3cddbb5ee20f 100644
--- a/llvm/test/Verifier/ref-func.ll
+++ b/llvm/test/Verifier/ref-func.ll
@@ -2,9 +2,9 @@
 
 @a = global i32 1
 @b = global i32 2
- at c = global i32 3, !ref !0
+ at c = global i32 3, !implicit.ref !0
 
-define i32 @foo() !ref !1 {
+define i32 @foo() !implicit.ref !1 {
   ret i32 0
 }
 
diff --git a/llvm/test/Verifier/ref.ll b/llvm/test/Verifier/ref.ll
index d3b5169062662..58e195320583e 100644
--- a/llvm/test/Verifier/ref.ll
+++ b/llvm/test/Verifier/ref.ll
@@ -1,10 +1,10 @@
 ; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s
 
- at a = global i32 1, !ref !0
- at b = global i32 2, !ref !1
- at c = global i32 3, !ref !1, !ref !2
- at d = global i32 4, !ref !3
- at e = external global i32, !ref !1
+ at a = global i32 1, !implicit.ref !0
+ at b = global i32 2, !implicit.ref !1
+ at c = global i32 3, !implicit.ref !1, !implicit.ref !2
+ at d = global i32 4, !implicit.ref !3
+ at e = external global i32, !implicit.ref !1
 
 !0 = !{i32 1}
 !1 = !{ptr @b}

>From 4d6f3655321ba10a8aa2089725aaf2833fb49cb5 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Wed, 29 Oct 2025 14:15:52 -0400
Subject: [PATCH 11/12] Propagate implicit.ref metadata when inlining.

Propagate the metadata from callee to caller on inlining. Also adds a
test verifying that the metadata is maintained when cloning a function.
---
 llvm/lib/Transforms/Utils/InlineFunction.cpp  |  9 ++++
 .../function-specialization-implicit-ref.ll   | 43 ++++++++++++++++
 .../Transforms/Inline/inline-ref-metadata.ll  | 50 +++++++++++++++++++
 3 files changed, 102 insertions(+)
 create mode 100644 llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll
 create mode 100644 llvm/test/Transforms/Inline/inline-ref-metadata.ll

diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp
index f49fbf8807bac..aa902f687d8aa 100644
--- a/llvm/lib/Transforms/Utils/InlineFunction.cpp
+++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp
@@ -2830,6 +2830,15 @@ void llvm::InlineFunctionImpl(CallBase &CB, InlineFunctionInfo &IFI,
     // Propagate metadata on the callsite if necessary.
     PropagateCallSiteMetadata(CB, FirstNewBlock, Caller->end());
 
+    // Propagate implicit ref metadata.
+    if (CalledFunc->hasMetadata(LLVMContext::MD_implicit_ref)) {
+      SmallVector<MDNode *> MDs;
+      CalledFunc->getMetadata(LLVMContext::MD_implicit_ref, MDs);
+      for (MDNode *MD : MDs) {
+        Caller->addMetadata(LLVMContext::MD_implicit_ref, *MD);
+      }
+    }
+
     // Register any cloned assumptions.
     if (IFI.GetAssumptionCache)
       for (BasicBlock &NewBlock :
diff --git a/llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll b/llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll
new file mode 100644
index 0000000000000..1e38e5c80fa5f
--- /dev/null
+++ b/llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll
@@ -0,0 +1,43 @@
+; RUN: opt -passes="ipsccp<func-spec>" -S -force-specialization < %s 2>&1 | FileCheck %s
+
+ at a = global i32 1
+
+define i64 @main(i64 %x, i1 %flag) {
+entry:
+  br i1 %flag, label %plus, label %minus
+
+plus:
+  %tmp0 = call i64 @compute(i64 %x, ptr @plus)
+  br label %merge
+
+minus:
+  %tmp1 = call i64 @compute(i64 %x, ptr @minus)
+  br label %merge
+
+merge:
+  %tmp2 = phi i64 [ %tmp0, %plus ], [ %tmp1, %minus]
+  ret i64 %tmp2
+}
+
+define internal i64 @compute(i64 %x, ptr %binop) !implicit.ref !0 {
+entry:
+  %tmp0 = call i64 %binop(i64 %x)
+  ret i64 %tmp0
+}
+
+define internal i64 @plus(i64 %x) {
+entry:
+  %tmp0 = add i64 %x, 1
+  ret i64 %tmp0
+}
+
+define internal i64 @minus(i64 %x) {
+entry:
+  %tmp0 = sub i64 %x, 1
+  ret i64 %tmp0
+}
+
+!0 = !{ptr @a}
+
+; CHECK: @compute.specialized.1(i64 %x, ptr %binop) !implicit.ref !0
+; CHECK: @compute.specialized.2(i64 %x, ptr %binop) !implicit.ref !0
diff --git a/llvm/test/Transforms/Inline/inline-ref-metadata.ll b/llvm/test/Transforms/Inline/inline-ref-metadata.ll
new file mode 100644
index 0000000000000..a252ee4ec36ce
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline-ref-metadata.ll
@@ -0,0 +1,50 @@
+; RUN: opt < %s -passes='cgscc(inline)' -S | FileCheck %s
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global double 3.141593e+00
+
+define i32 @callee1() !implicit.ref !0 {
+  ret i32 0
+}
+
+define i32 @callee2() !implicit.ref !1 {
+  ret i32 1
+}
+
+define i32 @callee3() {
+  %i = call i32 @callee2()
+  ret i32 %i
+}
+; CHECK: @callee3() !implicit.ref !1
+
+define i32 @caller1() {
+  %i = call i32 @callee1()
+  ret i32 %i
+}
+; CHECK: @caller1() !implicit.ref !0
+
+define i32 @caller2() !implicit.ref !2 {
+  %i = call i32 @callee1()
+  ret i32 %i
+}
+; CHECK: @caller2() !implicit.ref !2 !implicit.ref !0
+
+define i32 @caller3() {
+  %i = call i32 @caller4()
+  ret i32 %i
+}
+; CHECK: @caller3() !implicit.ref !0 !implicit.ref !1
+
+define i32 @caller4() {
+  %a = call i32 @callee1()
+  %b = call i32 @callee2()
+  %add = add i32 %a, %b
+  ret i32 %add
+}
+; CHECK: @caller4() !implicit.ref !0 !implicit.ref !1
+
+!0 = !{ptr @a}
+!1 = !{ptr @b}
+!2 = !{ptr @c}
+

>From e5449d56f6ac324fe82fa73a6b2a338a66fb4050 Mon Sep 17 00:00:00 2001
From: Sean Fertile <sd.fertile at gmail.com>
Date: Thu, 4 Dec 2025 11:07:49 -0500
Subject: [PATCH 12/12] Add implicit.ref metadata to the execptions that cannot
 be dropped.

---
 llvm/docs/LangRef.rst | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 518b0f9d48a54..868bf229ec3ae 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -6387,8 +6387,8 @@ multiple metadata attachments with the same identifier.
 A transformation is required to drop any metadata attachment that it
 does not recognize or cannot preserve. Currently there is an
 exception for metadata attachment to globals for ``!func_sanitize``,
-``!type``, ``!absolute_symbol`` and ``!associated`` which can't be
-unconditionally dropped unless the global is itself deleted.
+``!type``, ``!absolute_symbol``, ``!implicit.ref`` and ``!associated`` which
+can't be unconditionally dropped unless the global is itself deleted.
 
 Metadata attached to a module using named metadata may not be dropped, with
 the exception of debug metadata (named metadata with the name ``!llvm.dbg.*``).



More information about the llvm-commits mailing list