Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -5101,6 +5101,31 @@ See :doc:`TypeMetadata`. +'``associated``' Metadata +^^^^^^^^^^^^^^^^^^^ + +The ``associated`` metadata may be attached to a global object +declaration with a single argument that references another global object. + +This metadata prevents discarding of the global object in linker GC +unless the referenced object is also discarded. The linker support for +this feature is spotty. For best compatibility, globals carrying this +metadata may also: + +- Be in a comdat with the referenced global. +- Be in @llvm.compiler.used. +- Have an explicit section with a name which is a valid C identifier. + +It does not have any effect on non-ELF targets. + +Example: + +.. code-block:: llvm + $a = comdat any + @a = global i32 1, comdat $a + @b = internal global i32 2, comdat $a, section "abc", !associated !0 + !0 = !{i32* @a} + Module Flags Metadata ===================== Index: include/llvm/IR/LLVMContext.h =================================================================== --- include/llvm/IR/LLVMContext.h +++ include/llvm/IR/LLVMContext.h @@ -78,6 +78,7 @@ MD_type = 19, // "type" MD_section_prefix = 20, // "section_prefix" MD_absolute_symbol = 21, // "absolute_symbol" + MD_associated = 22, // "associated" }; /// Known operand bundle tag IDs, which always have the same value. All Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -211,6 +211,15 @@ return C; } +static const MCSymbolELF *getAssociatedSymbol(MDNode *MD, const TargetMachine &TM) { + auto *VM = dyn_cast(MD->getOperand(0)); + if (!VM) + report_fatal_error("MD_associated operand is not ValueAsMetadata"); + + GlobalObject *GO = dyn_cast(VM->getValue()); + return GO ? dyn_cast(TM.getSymbol(GO)) : nullptr; +} + MCSection *TargetLoweringObjectFileELF::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { StringRef SectionName = GO->getSection(); @@ -224,9 +233,24 @@ Group = C->getName(); Flags |= ELF::SHF_GROUP; } - return getContext().getELFSection(SectionName, - getELFSectionType(SectionName, Kind), Flags, - /*EntrySize=*/0, Group); + + // A section can have at most one associated section. Put each global with + // MD_associated in a unique section. + unsigned UniqueID = MCContext::GenericSectionID; + const MCSymbolELF *AssociatedSymbol = nullptr; + if (MDNode *MD = GO->getMetadata(LLVMContext::MD_associated)) { + UniqueID = NextUniqueID++; + Flags |= ELF::SHF_LINK_ORDER; + AssociatedSymbol = getAssociatedSymbol(MD, TM); + } + + MCSectionELF *Section = getContext().getELFSection( + SectionName, getELFSectionType(SectionName, Kind), Flags, + /*EntrySize=*/0, Group, UniqueID, AssociatedSymbol); + // Make sure that we did not get some other section with incompatible sh_link. + // This should not be possible due to UniqueID code above. + assert(Section->getAssociatedSymbol() == AssociatedSymbol); + return Section; } /// Return the section prefix name used by options FunctionsSections and @@ -248,11 +272,10 @@ return ".data.rel.ro"; } -static MCSectionELF * -selectELFSectionForGlobal(MCContext &Ctx, const GlobalObject *GO, - SectionKind Kind, Mangler &Mang, - const TargetMachine &TM, bool EmitUniqueSection, - unsigned Flags, unsigned *NextUniqueID) { +static MCSectionELF *selectELFSectionForGlobal( + MCContext &Ctx, const GlobalObject *GO, SectionKind Kind, Mangler &Mang, + const TargetMachine &TM, bool EmitUniqueSection, unsigned Flags, + unsigned *NextUniqueID, const MCSymbolELF *AssociatedSymbol) { unsigned EntrySize = 0; if (Kind.isMergeableCString()) { if (Kind.isMergeable2ByteCString()) { @@ -319,7 +342,7 @@ if (Kind.isExecuteOnly()) UniqueID = 0; return Ctx.getELFSection(Name, getELFSectionType(Name, Kind), Flags, - EntrySize, Group, UniqueID); + EntrySize, Group, UniqueID, AssociatedSymbol); } MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal( @@ -337,8 +360,20 @@ } EmitUniqueSection |= GO->hasComdat(); - return selectELFSectionForGlobal(getContext(), GO, Kind, getMangler(), TM, - EmitUniqueSection, Flags, &NextUniqueID); + const MCSymbolELF *AssociatedSymbol = nullptr; + if (MDNode *MD = GO->getMetadata(LLVMContext::MD_associated)) { + EmitUniqueSection = true; + Flags |= ELF::SHF_LINK_ORDER; + AssociatedSymbol = getAssociatedSymbol(MD, TM); + } + + EmitUniqueSection |= GO->getMetadata(LLVMContext::MD_associated) != nullptr; + + MCSectionELF *Section = selectELFSectionForGlobal( + getContext(), GO, Kind, getMangler(), TM, EmitUniqueSection, Flags, + &NextUniqueID, AssociatedSymbol); + assert(Section->getAssociatedSymbol() == AssociatedSymbol); + return Section; } MCSection *TargetLoweringObjectFileELF::getSectionForJumpTable( @@ -351,8 +386,9 @@ return ReadOnlySection; return selectELFSectionForGlobal(getContext(), &F, SectionKind::getReadOnly(), - getMangler(), TM, EmitUniqueSection, ELF::SHF_ALLOC, - &NextUniqueID); + getMangler(), TM, EmitUniqueSection, + ELF::SHF_ALLOC, &NextUniqueID, + /* AssociatedSymbol */ nullptr); } bool TargetLoweringObjectFileELF::shouldPutJumpTableInFunctionSection( Index: lib/IR/LLVMContext.cpp =================================================================== --- lib/IR/LLVMContext.cpp +++ lib/IR/LLVMContext.cpp @@ -58,6 +58,7 @@ {MD_type, "type"}, {MD_section_prefix, "section_prefix"}, {MD_absolute_symbol, "absolute_symbol"}, + {MD_associated, "associated"}, }; for (auto &MDKind : MDKinds) { Index: test/CodeGen/X86/elf-associated.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/elf-associated.ll @@ -0,0 +1,34 @@ +; RUN: llc -data-sections=1 -mtriple x86_64-pc-linux-gnu < %s | FileCheck %s +; RUN: llc -data-sections=0 -mtriple x86_64-pc-linux-gnu < %s | FileCheck %s + +@a = global i32 1 +@b = global i32 2, !associated !0 +!0 = !{i32* @a} +; CHECK-DAG: .section .data.b,"awm",@progbits,a + +; Loop is OK. Also, normally -data-sections=0 would place @c and @d in the same section. !associated prevents that. +@c = global i32 2, !associated !2 +@d = global i32 2, !associated !1 +!1 = !{i32* @c} +!2 = !{i32* @d} +; CHECK-DAG: .section .data.c,"awm",@progbits,d +; CHECK-DAG: .section .data.d,"awm",@progbits,c + +; BSS is OK. +@e = global i32 0 +@f = global i32 0, !associated !3 +@g = global i32 1, !associated !3 +!3 = !{i32* @e} +; CHECK-DAG: .section .bss.f,"awm",@nobits,e +; CHECK-DAG: .section .data.g,"awm",@progbits,e + +; Explicit sections. +@h = global i32 1, section "aaa" +@i = global i32 1, section "bbb", !associated !4 +@j = global i32 1, section "bbb", !associated !4 +@k = global i32 1, !associated !4 +!4 = !{i32* @h} +; CHECK-DAG: .section aaa,"aw",@progbits +; CHECK-DAG: .section bbb,"awm",@progbits,h,unique,1 +; CHECK-DAG: .section bbb,"awm",@progbits,h,unique,2 +; CHECK-DAG: .section .data.k,"awm",@progbits,h Index: test/ThinLTO/X86/lazyload_metadata.ll =================================================================== --- test/ThinLTO/X86/lazyload_metadata.ll +++ test/ThinLTO/X86/lazyload_metadata.ll @@ -10,13 +10,13 @@ ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=LAZY -; LAZY: 49 bitcode-reader - Number of Metadata records loaded +; LAZY: 51 bitcode-reader - Number of Metadata records loaded ; LAZY: 2 bitcode-reader - Number of MDStrings loaded ; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \ ; RUN: -o /dev/null -disable-ondemand-mds-loading -stats \ ; RUN: 2>&1 | FileCheck %s -check-prefix=NOTLAZY -; NOTLAZY: 58 bitcode-reader - Number of Metadata records loaded +; NOTLAZY: 60 bitcode-reader - Number of Metadata records loaded ; NOTLAZY: 7 bitcode-reader - Number of MDStrings loaded @@ -55,4 +55,4 @@ !6 = !{!9} !7 = !{!"7"} !8 = !{!"8"} -!9 = !{!6} \ No newline at end of file +!9 = !{!6}