diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -26,6 +26,7 @@ #include "llvm/MC/MCRegister.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSectionXCOFF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/MC/TargetRegistry.h" @@ -945,6 +946,17 @@ } void MCAsmStreamer::emitXCOFFRefDirective(const MCSymbol *Symbol) { + assert([&]() -> bool { + MCSectionXCOFF *CS = cast(getCurrentSectionOnly()); + return CS && CS->getCSectType() == XCOFF::XTY_SD && + CS->getMappingClass() != XCOFF::XMC_BS && + CS->getMappingClass() != XCOFF::XMC_UC; + }() && "The .ref pseudo-op cannot be included in a csect with a storage " + "mapping class of BS or UC"); + assert( + !(Symbol->getName().endswith_insensitive("[bs]") || + Symbol->getName().endswith_insensitive("[uc]")) && + ".ref operand cannot be a name with storage mapping class of BS or UC"); OS << "\t.ref "; Symbol->print(OS, MAI); EmitEOL(); diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -153,6 +153,8 @@ TOC; const PPCSubtarget *Subtarget = nullptr; + virtual void trackTOCRef(const MCSymbol *Sym) {} + public: explicit PPCAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -242,6 +244,20 @@ DenseMap> GOAliasMap; + // To support `-bcdtors:csect` on AIX, clang handles .ref regarding variable + // and corresponding init/term functions. In addition, we need to generate + // .ref from program code to internal global variables for safety reason + // consider following case: one internal global variable with init function + // which produces side effect, but that variable is not referenced by any + // function; without .ref from program code to the variable, the side effect + // could be missed after link. We put all target internal GV in + // InternalGlobalRW, keep track of function touched GV in FuncBodyTouchedTOC, + // and finally generate .ref for those not touched. + SmallVector InternalGlobalRW; + SmallSet FuncBodyTouchedTOC; + bool IsTextEmpty = true; + bool IsFuncBodyTOC = false; + uint16_t getNumberOfVRSaved(); void emitTracebackTable(); @@ -252,6 +268,14 @@ // Get the offset of an alias based on its AliaseeObject. uint64_t getAliasOffset(const Constant *C); + void emitAssociatedRefs(const GlobalObject &); + + void trackTOCRef(const MCSymbol *Sym) override { + if (IsFuncBodyTOC) + if (const auto *S = dyn_cast(Sym)) + FuncBodyTouchedTOC.insert(S); + } + public: PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : PPCAsmPrinter(TM, std::move(Streamer)) { @@ -275,6 +299,8 @@ void emitFunctionEntryLabel() override; + void emitFunctionBodyStart() override { IsFuncBodyTOC = true; } + void emitFunctionBodyEnd() override; void emitPGORefs(); @@ -471,6 +497,7 @@ if (!TOC.contains({Sym, Kind})) collectTOCStats(Type); + trackTOCRef(Sym); MCSymbol *&TOCEntry = TOC[{Sym, Kind}]; if (!TOCEntry) TOCEntry = createTempSymbol("C"); @@ -2043,6 +2070,15 @@ } void PPCAIXAsmPrinter::emitFunctionBodyEnd() { + IsTextEmpty = false; + if (TM.getFunctionSections()) { + for (auto I = InternalGlobalRW.begin(), E = InternalGlobalRW.end(); I != E; + ++I) + if (!FuncBodyTouchedTOC.contains(*I)) + OutStreamer->emitXCOFFRefDirective((*I)); + FuncBodyTouchedTOC.clear(); + } + IsFuncBodyTOC = false; if (!TM.getXCOFFTracebackTable()) return; @@ -2535,6 +2571,8 @@ MCSymbol *EmittedInitSym = GVSym; + emitAssociatedRefs(*GV); + // Emit linkage for the global variable and its aliases. emitLinkage(GV, EmittedInitSym); for (const GlobalAlias *GA : GOAliasMap[GV]) @@ -2564,6 +2602,26 @@ &AliasList); } +void PPCAIXAsmPrinter::emitAssociatedRefs(const GlobalObject &GO) { + MDNode *MD = GO.getMetadata(LLVMContext::MD_associated); + if (!MD) + return; + + for (unsigned i = 0, e = MD->getNumOperands(); i != e; ++i) { + const MDOperand &Op = MD->getOperand(i); + if (!Op.get()) + continue; + + const auto *VM = dyn_cast(Op); + if (!VM) + report_fatal_error("MD_associated operand is not ValueAsMetadata"); + + if (const auto *OtherGV = dyn_cast(VM->getValue())) + if (OtherGV != &GO) + OutStreamer->emitXCOFFRefDirective(getSymbol(OtherGV)); + } +} + void PPCAIXAsmPrinter::emitFunctionDescriptor() { const DataLayout &DL = getDataLayout(); const unsigned PointerSize = DL.getPointerSizeInBits() == 64 ? 8 : 4; @@ -2577,6 +2635,9 @@ for (const GlobalAlias *Alias : GOAliasMap[&MF->getFunction()]) OutStreamer->emitLabel(getSymbol(Alias)); + // Emit .ref from dtor to term function + emitAssociatedRefs(MF->getFunction()); + // Emit function entry point address. OutStreamer->emitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), PointerSize); @@ -2738,6 +2799,15 @@ GOAliasMap[Base].push_back(&Alias); } + // Collect all LGLOBAL located inside {RW} mapping class for .ref + for (const auto &G : M.globals()) + if (G.getLinkage() == GlobalValue::InternalLinkage || + G.getLinkage() == GlobalValue::PrivateLinkage) { + SectionKind GVKind = getObjFileLowering().getKindForGlobal(&G, TM); + if (GVKind.isData() || GVKind.isReadOnlyWithRel()) + InternalGlobalRW.push_back(cast(getSymbol(&G))); + } + return Result; } @@ -2821,6 +2891,17 @@ for (MCSymbol *Sym : ExtSymSDNodeSymbols) OutStreamer->emitSymbolAttribute(Sym, MCSA_Extern); + + // Do not generate .ref for empty .text{PR} + if (!TM.getFunctionSections() && !IsTextEmpty) { + for (auto I = InternalGlobalRW.begin(), E = InternalGlobalRW.end(); I != E; + ++I) + if (!FuncBodyTouchedTOC.contains(*I)) + OutStreamer->emitXCOFFRefDirective((*I)); + } + InternalGlobalRW.clear(); + FuncBodyTouchedTOC.clear(); + return PPCAsmPrinter::doFinalization(M); } diff --git a/llvm/lib/Target/TargetLoweringObjectFile.cpp b/llvm/lib/Target/TargetLoweringObjectFile.cpp --- a/llvm/lib/Target/TargetLoweringObjectFile.cpp +++ b/llvm/lib/Target/TargetLoweringObjectFile.cpp @@ -233,9 +233,14 @@ // Most non-mergeable zero data can be put in the BSS section unless otherwise // specified. if (isSuitableForBSS(GVar) && !TM.Options.NoZerosInBSS) { - if (GVar->hasLocalLinkage()) + if (GVar->hasLocalLinkage()) { + // AIX XCOFF with associated metadata is part of .ref pseudo-op, should be + // classified as Data. + if (TM.getTargetTriple().isOSAIX() && + GVar->getMetadata(LLVMContext::MD_associated)) + return SectionKind::getData(); return SectionKind::getBSSLocal(); - else if (GVar->hasExternalLinkage()) + } else if (GVar->hasExternalLinkage()) return SectionKind::getBSSExtern(); return SectionKind::getBSS(); } diff --git a/llvm/test/CodeGen/PowerPC/aix-xcoff-ref-empty-text.ll b/llvm/test/CodeGen/PowerPC/aix-xcoff-ref-empty-text.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-xcoff-ref-empty-text.ll @@ -0,0 +1,8 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr9 --filetype=asm < %s | FileCheck %s --check-prefix=CHECK + +@a = internal global i32 3, align 4 +@b = internal global i32 6, align 4 +@c = internal global i32 9, align 4 + +; CHECK: .csect .text[PR],5 +; CHECK-NOT: .ref diff --git a/llvm/test/CodeGen/PowerPC/aix-xcoff-ref.ll b/llvm/test/CodeGen/PowerPC/aix-xcoff-ref.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-xcoff-ref.ll @@ -0,0 +1,71 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr9 --function-sections --filetype=asm < %s | FileCheck %s --check-prefixes=CHECK-FS,CHECK +; RUN: llc -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr9 --filetype=asm < %s | FileCheck %s --check-prefixes=CHECK-NFS,CHECK + +@a = internal global i32 0, align 4, !associated !0 +@b = internal global i32 3, align 4 +@c = internal global i32 0, align 4 +@d = internal global i32 3, align 4, !associated !2 + +define i32 @foo() { +entry: + %0 = load i32, ptr @a, align 4 + ret i32 %0 +} + +define void @bar() { +entry: + ret void +} + +define void @baz() !associated !1 { +entry: + ret void +} + +!0 = !{i32 ()* @foo, void ()* @bar} +!1 = !{void ()* @bar} +!2 = distinct !{null} + +; CHECK-FS: .csect .foo[PR],5 +; CHECK-FS: .csect .foo[PR],5 +; CHECK-FS-NEXT: # %bb.0: # %entry +; CHECK-FS-NEXT: lwz 3, L..C0(2) # @a +; CHECK-FS-NEXT: lwz 3, 0(3) +; CHECK-FS-NEXT: blr +; CHECK-FS-NEXT: .ref b[RW] +; CHECK-FS-NEXT: .ref d[RW] + +; CHECK-FS: .csect .bar[PR],5 +; CHECK-FS: .csect .bar[PR],5 +; CHECK-FS-NEXT: # %bb.0: # %entry +; CHECK-FS-NEXT: blr +; CHECK-FS-NEXT: .ref a[RW] +; CHECK-FS-NEXT: .ref b[RW] +; CHECK-FS-NEXT: .ref d[RW] + +; CHECK-FS: .csect baz[DS],2 +; CHECK-FS-NEXT: .ref bar[DS] # @baz +; CHECK-FS: .csect .baz[PR],5 +; CHECK-FS-NEXT: # %bb.0: # %entry +; CHECK-FS-NEXT: blr +; CHECK-FS-NEXT: .ref a[RW] +; CHECK-FS-NEXT: .ref b[RW] +; CHECK-FS-NEXT: .ref d[RW] + +; CHECK-NFS: .csect baz[DS],2 +; CHECK-NFS-NEXT: .ref bar[DS] # @baz + +; The whole .text{PR} has one .ref points to b[RW] and d[RW] +; CHECK-NFS: .byte "baz" # Function Name +; CHECK-NFS-NEXT: # -- End function +; CHECK-NFS-NEXT: .ref b[RW] +; CHECK-NFS-NEXT: .ref d[RW] + +; CHECK: .csect a[RW],2 +; CHECK-NEXT: .ref foo[DS] # @a +; CHECK-NEXT: .ref bar[DS] + +; CHECK: .lcomm c,4,c[BS],2 # @c + +; CHECK: .csect d[RW],2 +; CHECK: .lglobl d[RW] # @d