[llvm] b6212a4 - XCOFF associated metadata (#159096)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 9 10:49:25 PST 2026
Author: Sean Fertile
Date: 2026-01-09T13:49:21-05:00
New Revision: b6212a4caf9fb029382f26dd224e54da58714fa5
URL: https://github.com/llvm/llvm-project/commit/b6212a4caf9fb029382f26dd224e54da58714fa5
DIFF: https://github.com/llvm/llvm-project/commit/b6212a4caf9fb029382f26dd224e54da58714fa5.diff
LOG: XCOFF associated metadata (#159096)
Add a new metadata node `!implicit.ref` to represent an implicit
dependency between 2 symbols. The metadata is unique to AIX and gets
lowered to a relocation that adds an explicit link between the section
the global that the metadata is placed on is allocated in, to the
asscoiated symbol. This relocation will cause the associated symbol to
remain live if the section is not garbage collected. This is used mainly
for compiler features where there is some hidden runtime dependency
between the symbols that isn't otherwise obvious to the linker.
Added:
llvm/test/CodeGen/PowerPC/aix-func-ref.ll
llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
llvm/test/Transforms/FunctionSpecialization/function-specialization-implicit-ref.ll
llvm/test/Transforms/Inline/inline-ref-metadata.ll
llvm/test/Verifier/ref-func.ll
llvm/test/Verifier/ref.ll
Modified:
llvm/docs/LangRef.rst
llvm/include/llvm/IR/FixedMetadataKinds.def
llvm/lib/IR/Verifier.cpp
llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
llvm/lib/Transforms/Utils/InlineFunction.cpp
Removed:
################################################################################
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 041a526b6729f..c17dd3f707be7 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -6394,8 +6394,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.*``).
@@ -8694,6 +8694,36 @@ of requiring a stack protector.
!0 = !{i32 0}
+'``implicit.ref``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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
+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 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.
+
+
+Example:
+
+.. code-block:: text
+
+ @a = global i32 1
+ @b = global i32 2
+ @c = global i32 3, section "abc", !implicit.ref !0, !implicit.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..01d22bdd7c996 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_implicit_ref, "implicit.ref", 46)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index a2ad95eb5abc4..0b9a23f217218 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -769,6 +769,31 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
DL.getIntPtrType(GO->getType()),
RangeLikeMetadataKind::AbsoluteSymbol);
}
+
+ 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_implicit_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();
+ 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 122738caa6827..718d51c5a0673 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 emitRefMetadata(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_implicit_ref)) {
+ emitRefMetadata(GV);
+ }
+
const DataLayout &DL = GV->getDataLayout();
// Handle common and zero-initialized local symbols.
@@ -2893,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_implicit_ref)) {
+ emitRefMetadata(F);
+ }
}
void PPCAIXAsmPrinter::emitPGORefs(Module &M) {
@@ -3336,6 +3348,19 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV,
OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding));
}
+void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) {
+ SmallVector<MDNode *> MDs;
+ GO->getMetadata(LLVMContext::MD_implicit_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 *GV = cast<GlobalValue>(VAM->getValue());
+ MCSymbol *Referenced = TM.getSymbol(GV);
+ 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/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/CodeGen/PowerPC/aix-func-ref.ll b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
new file mode 100644
index 0000000000000..5cb0575f370e1
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-func-ref.ll
@@ -0,0 +1,55 @@
+; 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
+
+; 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
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global i32 3
+
+define i32 @foo() !implicit.ref !0 {
+ ret i32 0
+}
+
+define i32 @bar() !implicit.ref !1 !implicit.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]
+
+; 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
diff --git a/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
new file mode 100644
index 0000000000000..7dc9466a7ba9b
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-ref-metadata.ll
@@ -0,0 +1,77 @@
+; 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
+
+ at a = global i32 1
+ at b = global i32 2
+ at c = global i32 3, section "custom_section_c"
+ 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}
+!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]
+
+; 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
+
+; 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
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}
+
diff --git a/llvm/test/Verifier/ref-func.ll b/llvm/test/Verifier/ref-func.ll
new file mode 100644
index 0000000000000..e3cddbb5ee20f
--- /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, !implicit.ref !0
+
+define i32 @foo() !implicit.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
new file mode 100644
index 0000000000000..58e195320583e
--- /dev/null
+++ b/llvm/test/Verifier/ref.ll
@@ -0,0 +1,31 @@
+; RUN: not llvm-as -disable-output < %s -o /dev/null 2>&1 | FileCheck %s
+
+ 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}
+!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}
+
+; CHECK: ref metadata must not be placed on a declaration
+; CHECK: @e
More information about the llvm-commits
mailing list