diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 22b58bf0f5735..0a70a8a7c5b28 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8577,6 +8577,35 @@ Example: The ``nofree`` metadata indicates the memory pointed by the pointer will not be freed after the attached instruction. +'``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_`` and +``__stop_`` 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 0603abcd6a4da..c7d8aea878025 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -56,3 +56,4 @@ LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41) 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_implicit_ref, "implicit.ref", 45) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 6b3cd27b77a7a..c9eff85e0b3ea 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 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(Op); + Check(VM, "ref metadata must be ValueAsMetadata", GO, MD); + if (VM) { + Check(isa(VM->getValue()->getType()), + "ref value must be pointer typed", GV, MD); + + const Value *Stripped = VM->getValue()->stripPointerCastsAndAliases(); + Check(isa(Stripped) || isa(Stripped), + "ref metadata must point to a GlobalObject", GO, Stripped); + Check(Stripped != GO, "values should not reference themselves", GO, + MD); + } + } + } } Check(!GV.hasAppendingLinkage() || isa(GV), diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index 023fd147535ec..5e5c3344d2517 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 @@ -2797,6 +2799,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. @@ -2889,10 +2895,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) { @@ -3332,6 +3344,19 @@ void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV, OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding)); } +void PPCAIXAsmPrinter::emitRefMetadata(const GlobalObject *GO) { + SmallVector MDs; + GO->getMetadata(LLVMContext::MD_implicit_ref, MDs); + assert(MDs.size() && "Expected asscoiated metadata nodes"); + + for (const MDNode *MD : MDs) { + const ValueAsMetadata *VAM = cast(MD->getOperand(0).get()); + const GlobalValue *GV = cast(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/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 + +@a = global i32 1 +@b = global i32 2 +@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 + +@a = global i32 1 +@b = global i32 2 +@c = global i32 3, section "custom_section_c" +@d = global i32 4, !implicit.ref !0 +@e = constant i32 5, !implicit.ref !1, !implicit.ref!2 +@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/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 + +@a = global i32 1 +@b = global i32 2 +@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 + +@a = global i32 1, !implicit.ref !0 +@b = global i32 2, !implicit.ref !1 +@c = global i32 3, !implicit.ref !1, !implicit.ref !2 +@d = global i32 4, !implicit.ref !3 +@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