diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -6553,6 +6553,34 @@ @b = internal global i32 2, comdat $a, section "abc", !associated !0 !0 = !{i32* @a} +'``retain``' Metadata +^^^^^^^^^^^^^^^^^^^^^^^^^ + +This metadata, when attached to a global object definition, indicates that +the global object needs to be retained under linker garbage collection. + +This metadata only has effects on COFF and ELF targets. + +On ELF targets, a separate section with the ``SHF_GNU_RETAIN`` flag is created. + +If an explicit section is specified, the separate section will have the +specified name, but different from the selected section when the metadata is +absent. I.e. if two global objects have the same explicit section but only +one has the ``retain`` metadata, there will be two sections of the same name, +one with the ``SHF_GNU_RETAIN`` flag. + +On COFF targets, this metadata only works for non-local linkage global objects. + +Example: + +.. code-block:: llvm + + @a = global i32 1, !retain !{} + @b = constant i32 2, !retain !{} + + define void @f() !retain !{} { + ... + } '``prof``' Metadata ^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -42,3 +42,4 @@ LLVM_FIXED_MD_KIND(MD_vcall_visibility, "vcall_visibility", 28) LLVM_FIXED_MD_KIND(MD_noundef, "noundef", 29) LLVM_FIXED_MD_KIND(MD_annotation, "annotation", 30) +LLVM_FIXED_MD_KIND(MD_retain, "retain", 31) diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -691,9 +691,15 @@ // MD_associated in a unique section. unsigned UniqueID = MCContext::GenericSectionID; const MCSymbolELF *LinkedToSym = getLinkedToSymbol(GO, TM); - if (GO->getMetadata(LLVMContext::MD_associated)) { + const bool Associated = GO->getMetadata(LLVMContext::MD_associated); + const bool Retain = GO->getMetadata(LLVMContext::MD_retain); + if (Associated || Retain) { UniqueID = NextUniqueID++; - Flags |= ELF::SHF_LINK_ORDER; + if (Associated) + Flags |= ELF::SHF_LINK_ORDER; + if (Retain && (getContext().getAsmInfo()->useIntegratedAssembler() || + getContext().getAsmInfo()->binutilsIsAtLeast(2, 36))) + Flags |= ELF::SHF_GNU_RETAIN; } else { if (getContext().getAsmInfo()->useIntegratedAssembler() || getContext().getAsmInfo()->binutilsIsAtLeast(2, 35)) { @@ -813,6 +819,12 @@ EmitUniqueSection = true; Flags |= ELF::SHF_LINK_ORDER; } + if (GO->getMetadata(LLVMContext::MD_retain) && + (Ctx.getAsmInfo()->useIntegratedAssembler() || + Ctx.getAsmInfo()->binutilsIsAtLeast(2, 36))) { + EmitUniqueSection = true; + Flags |= ELF::SHF_GNU_RETAIN; + } MCSectionELF *Section = selectELFSectionForGlobal( Ctx, GO, Kind, Mang, TM, EmitUniqueSection, Flags, @@ -1728,6 +1740,24 @@ Flags.clear(); } + // Emit /INCLUDE: flags for each non-local-linkage global value with the + // !retain metadata. + raw_string_ostream OS(Flags); + auto EmitLinkerFlags = [&](const GlobalValue &GV) { + emitLinkerFlagsForUsedCOFF(OS, &GV, getTargetTriple(), getMangler()); + OS.flush(); + if (!Flags.empty()) { + Streamer.SwitchSection(getDrectveSection()); + Streamer.emitBytes(Flags); + } + Flags.clear(); + }; + + for (const GlobalValue &GV : M.global_values()) + if (auto *GO = dyn_cast(&GV)) + if (!GV.hasLocalLinkage() && GO->hasMetadata(LLVMContext::MD_retain)) + EmitLinkerFlags(GV); + // Emit /INCLUDE: flags for each used global as necessary. if (const auto *LU = M.getNamedGlobal("llvm.used")) { assert(LU->hasInitializer() && "expected llvm.used to have an initializer"); @@ -1742,15 +1772,7 @@ if (GV->hasLocalLinkage()) continue; - raw_string_ostream OS(Flags); - emitLinkerFlagsForUsedCOFF(OS, GV, getTargetTriple(), getMangler()); - OS.flush(); - - if (!Flags.empty()) { - Streamer.SwitchSection(getDrectveSection()); - Streamer.emitBytes(Flags); - } - Flags.clear(); + EmitLinkerFlags(*GV); } } } diff --git a/llvm/test/CodeGen/X86/coff-retain.ll b/llvm/test/CodeGen/X86/coff-retain.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/coff-retain.ll @@ -0,0 +1,33 @@ +;; Add /INCLUDE: linker options for non-local-linkage global values with the +;; !retain metadata. +; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s + +; CHECK: .ascii " /INCLUDE:fa" +; CHECK-NEXT: .ascii " /INCLUDE:fb" +; CHECK-NEXT: .ascii " /INCLUDE:ga" +; CHECK-NEXT: .ascii " /INCLUDE:gb" +; CHECK-NEXT: .ascii " /INCLUDE:ge" +; CHECK-NOT: .ascii + +define dso_local void @fa() !retain !{} { +entry: + ret void +} +define weak_odr dso_local void @fb() !retain !{} { +entry: + ret void +} +define internal void @fc() !retain !{} { +entry: + ret void +} + +@ga = global i32 0, !retain !{} +@gb = weak global i32 0, !retain !{} +@gc = internal global i32 0, !retain !{} +@gd = private global i32 0, !retain !{} +@ge = constant i32 3, !retain !{} + +;; Aliases do not get /INCLUDE:. This is to be consistent with ELF. +@aa = alias i32, i32* @gc +@ab = internal alias i32, i32* @gc diff --git a/llvm/test/CodeGen/X86/elf-retain.ll b/llvm/test/CodeGen/X86/elf-retain.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/elf-retain.ll @@ -0,0 +1,60 @@ +;; Place a global object with !retain in a separate section with the SHF_GNU_RETAIN flag. +; RUN: llc -mtriple=x86_64 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64 -data-sections=1 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64 -no-integrated-as -binutils-version=2.36 < %s | FileCheck %s +; RUN: llc -mtriple=x86_64 -no-integrated-as -binutils-version=2.35 < %s | FileCheck %s --check-prefix=OLDGAS + +; RUN: llc -mtriple=x86_64 -data-sections=1 -unique-section-names=0 < %s | FileCheck %s --check-prefix=NOUNIQUE + +; CHECK: .section .text.fa,"axR",@progbits{{$}} +; OLDGAS-NOT: .section .text +; NOUNIQUE: .section .text,"axR",@progbits,unique,1 +define dso_local void @fa() !retain !{} { +entry: + ret void +} + +; CHECK: .section .text.fb,"axR",@progbits{{$}} +; NOUNIQUE: .section .text,"axR",@progbits,unique,2 +define internal void @fb() !retain !{} { +entry: + ret void +} + +;; Explicit section. +; CHECK: .section ccc,"axR",@progbits,unique,1 +; OLDGAS: .section ccc,"ax",@progbits,unique,1 +; NOUNIQUE: .section ccc,"axR",@progbits,unique,3 +define dso_local void @fc() section "ccc" !retain !{} { +entry: + ret void +} + +; CHECK: .section .bss.ga,"awR",@nobits{{$}} +; OLDGAS: .bss{{$}} +; NOUNIQUE: .section .bss,"awR",@nobits,unique,4 +@ga = global i32 0, !retain !{} + +; CHECK: .section .data.gb,"awR",@progbits{{$}} +; OLDGAS: .data{{$}} +; NOUNIQUE: .section .data,"awR",@progbits,unique,5 +@gb = internal global i32 2, !retain !{} + +; CHECK: .section .rodata.gc,"aR",@progbits{{$}} +; OLDGAS: .section .rodata,"a",@progbits{{$}} +; NOUNIQUE: .section .rodata,"aR",@progbits,unique,6 +@gc = constant i32 3, !retain !{} + +;; Explicit section. +; CHECK: .section ddd,"awR",@progbits,unique,2 +; OLDGAS: .section ddd,"aw",@progbits,unique,2 +; NOUNIQUE: .section ddd,"awR",@progbits,unique,7 +@gd = global i32 1, section "ddd", !retain !{} + +;; Used together with !associated. +; CHECK: .section .data.ge,"awoR",@progbits,gc +; OLDGAS: .section .data.ge,"awo",@progbits,gc +; NOUNIQUE: .section .data,"awoR",@progbits,gc,unique,8 +@ge = global i32 1, !associated !0, !retain !{} + +!0 = !{i32* @gc}