forked from OSchip/llvm-project
ELF: Create unique SHF_GNU_RETAIN sections for llvm.used global objects
If a global object is listed in `@llvm.used`, place it in a unique section with the `SHF_GNU_RETAIN` flag. The section is a GC root under `ld --gc-sections` with LLD>=13 or GNU ld>=2.36. For front ends which do not expect to see multiple sections of the same name, consider emitting `@llvm.compiler.used` instead of `@llvm.used`. SHF_GNU_RETAIN is restricted to ELFOSABI_GNU and ELFOSABI_FREEBSD in binutils. We don't do the restriction - see the rationale in D95749. The integrated assembler has supported SHF_GNU_RETAIN since D95730. GNU as>=2.36 supports section flag 'R'. We don't need to worry about GNU ld support because older GNU ld just ignores the unknown SHF_GNU_RETAIN. With this change, `__attribute__((retain))` functions/variables emitted by clang will get the SHF_GNU_RETAIN flag. Differential Revision: https://reviews.llvm.org/D97448
This commit is contained in:
parent
8afdacba9d
commit
47c5576d7d
|
@ -14,6 +14,7 @@
|
|||
#ifndef LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H
|
||||
#define LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H
|
||||
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/BinaryFormat/XCOFF.h"
|
||||
#include "llvm/Target/TargetLoweringObjectFile.h"
|
||||
|
||||
|
@ -32,6 +33,7 @@ class TargetMachine;
|
|||
class TargetLoweringObjectFileELF : public TargetLoweringObjectFile {
|
||||
bool UseInitArray = false;
|
||||
mutable unsigned NextUniqueID = 1; // ID 0 is reserved for execute-only sections
|
||||
SmallPtrSet<GlobalObject *, 2> Used;
|
||||
|
||||
protected:
|
||||
MCSymbolRefExpr::VariantKind PLTRelativeVariantKind =
|
||||
|
@ -43,6 +45,8 @@ public:
|
|||
|
||||
void Initialize(MCContext &Ctx, const TargetMachine &TM) override;
|
||||
|
||||
void getModuleMetadata(Module &M) override;
|
||||
|
||||
/// Emit Obj-C garbage collection and linker options.
|
||||
void emitModuleMetadata(MCStreamer &Streamer, Module &M) const override;
|
||||
|
||||
|
|
|
@ -293,6 +293,14 @@ void TargetLoweringObjectFileELF::Initialize(MCContext &Ctx,
|
|||
}
|
||||
}
|
||||
|
||||
void TargetLoweringObjectFileELF::getModuleMetadata(Module &M) {
|
||||
SmallVector<GlobalValue *, 4> Vec;
|
||||
collectUsedGlobalVariables(M, Vec, false);
|
||||
for (GlobalValue *GV : Vec)
|
||||
if (auto *GO = dyn_cast<GlobalObject>(GV))
|
||||
Used.insert(GO);
|
||||
}
|
||||
|
||||
void TargetLoweringObjectFileELF::emitModuleMetadata(MCStreamer &Streamer,
|
||||
Module &M) const {
|
||||
auto &C = getContext();
|
||||
|
@ -687,9 +695,15 @@ MCSection *TargetLoweringObjectFileELF::getExplicitSectionGlobal(
|
|||
// 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 = Used.count(GO);
|
||||
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)) {
|
||||
|
@ -802,13 +816,18 @@ static MCSectionELF *selectELFSectionForGlobal(
|
|||
|
||||
static MCSection *selectELFSectionForGlobal(
|
||||
MCContext &Ctx, const GlobalObject *GO, SectionKind Kind, Mangler &Mang,
|
||||
const TargetMachine &TM, bool EmitUniqueSection, unsigned Flags,
|
||||
unsigned *NextUniqueID) {
|
||||
const TargetMachine &TM, bool Retain, bool EmitUniqueSection,
|
||||
unsigned Flags, unsigned *NextUniqueID) {
|
||||
const MCSymbolELF *LinkedToSym = getLinkedToSymbol(GO, TM);
|
||||
if (LinkedToSym) {
|
||||
EmitUniqueSection = true;
|
||||
Flags |= ELF::SHF_LINK_ORDER;
|
||||
}
|
||||
if (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,
|
||||
|
@ -832,16 +851,17 @@ MCSection *TargetLoweringObjectFileELF::SelectSectionForGlobal(
|
|||
}
|
||||
EmitUniqueSection |= GO->hasComdat();
|
||||
return selectELFSectionForGlobal(getContext(), GO, Kind, getMangler(), TM,
|
||||
EmitUniqueSection, Flags, &NextUniqueID);
|
||||
Used.count(GO), EmitUniqueSection, Flags,
|
||||
&NextUniqueID);
|
||||
}
|
||||
|
||||
MCSection *TargetLoweringObjectFileELF::getUniqueSectionForFunction(
|
||||
const Function &F, const TargetMachine &TM) const {
|
||||
SectionKind Kind = SectionKind::getText();
|
||||
unsigned Flags = getELFSectionFlags(Kind);
|
||||
return selectELFSectionForGlobal(getContext(), &F, Kind, getMangler(), TM,
|
||||
/* EmitUniqueSection = */ true, Flags,
|
||||
&NextUniqueID);
|
||||
return selectELFSectionForGlobal(
|
||||
getContext(), &F, Kind, getMangler(), TM, Used.count(&F),
|
||||
/*EmitUniqueSection=*/true, Flags, &NextUniqueID);
|
||||
}
|
||||
|
||||
MCSection *TargetLoweringObjectFileELF::getSectionForJumpTable(
|
||||
|
|
|
@ -84,6 +84,7 @@ MCSection *RISCVELFTargetObjectFile::SelectSectionForGlobal(
|
|||
}
|
||||
|
||||
void RISCVELFTargetObjectFile::getModuleMetadata(Module &M) {
|
||||
TargetLoweringObjectFileELF::getModuleMetadata(M);
|
||||
SmallVector<Module::ModuleFlagEntry, 8> ModuleFlags;
|
||||
M.getModuleFlagsMetadata(ModuleFlags);
|
||||
|
||||
|
|
|
@ -12,5 +12,5 @@ entry:
|
|||
ret void
|
||||
}
|
||||
|
||||
; CHECK: .section gsection,"aw",@progbits
|
||||
; CHECK: .section hsection,"aw",@progbits
|
||||
; CHECK: .section gsection,"awR",@progbits
|
||||
; CHECK: .section hsection,"awR",@progbits
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
; RUN: llc -verify-machineinstrs -mtriple=powerpc-unknown-linux-gnu < %s | FileCheck %s
|
||||
|
||||
; CHECK: .section .bss,"aw",@nobits
|
||||
; CHECK: .section .bss.X,"awR",@nobits
|
||||
; CHECK: .weak X
|
||||
; CHECK-LABEL: X:
|
||||
; CHECK: .long 0
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
;; Place a global object in the llvm.used list in a unique 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
|
||||
|
||||
@llvm.used = appending global [10 x i8*] [
|
||||
i8* bitcast (void ()* @fa to i8*), i8* bitcast (void ()* @fb to i8*), i8* bitcast (void ()* @fc to i8*),
|
||||
i8* bitcast (i32* @ga to i8*), i8* bitcast (i32* @gb to i8*), i8* bitcast (i32* @gc to i8*), i8* bitcast (i32* @gd to i8*), i8* bitcast (i32* @ge to i8*),
|
||||
i8* bitcast (i32* @aa to i8*), i8* bitcast (i32* @ab to i8*) ], section "llvm.metadata"
|
||||
|
||||
; CHECK: .section .text.fa,"axR",@progbits{{$}}
|
||||
; OLDGAS-NOT: .section .text
|
||||
; NOUNIQUE: .section .text,"axR",@progbits,unique,1
|
||||
define dso_local void @fa() {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: .section .text.fb,"axR",@progbits{{$}}
|
||||
; NOUNIQUE: .section .text,"axR",@progbits,unique,2
|
||||
define internal void @fb() {
|
||||
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" {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: .section .bss.ga,"awR",@nobits{{$}}
|
||||
; OLDGAS: .bss{{$}}
|
||||
; NOUNIQUE: .section .bss,"awR",@nobits,unique,4
|
||||
@ga = global i32 0
|
||||
|
||||
; CHECK: .section .data.gb,"awR",@progbits{{$}}
|
||||
; OLDGAS: .data{{$}}
|
||||
; NOUNIQUE: .section .data,"awR",@progbits,unique,5
|
||||
@gb = internal global i32 2
|
||||
|
||||
; CHECK: .section .rodata.gc,"aR",@progbits{{$}}
|
||||
; OLDGAS: .section .rodata,"a",@progbits{{$}}
|
||||
; NOUNIQUE: .section .rodata,"aR",@progbits,unique,6
|
||||
@gc = constant i32 3
|
||||
|
||||
;; 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"
|
||||
|
||||
;; 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
|
||||
|
||||
;; Aliases in llvm.used are ignored.
|
||||
; CHECK: .section fff,"aw",@progbits{{$}}
|
||||
; OLDGAS: .section fff,"aw",@progbits{{$}}
|
||||
; NOUNIQUE: .section fff,"aw",@progbits{{$}}
|
||||
@gf = global i32 1, section "fff"
|
||||
|
||||
@aa = alias i32, i32* @gf
|
||||
@ab = internal alias i32, i32* @gf
|
||||
|
||||
!0 = !{i32* @gc}
|
Loading…
Reference in New Issue