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 @@ -27,6 +27,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" @@ -937,6 +938,17 @@ } void MCAsmStreamer::emitXCOFFRefDirective(StringRef Name) { + 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( + !(Name.endswith_insensitive("[bs]") || + Name.endswith_insensitive("[uc]")) && + ".ref operand cannot be a name with storage mapping class of BS or UC"); OS << "\t.ref " << Name; 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 @@ -142,6 +142,8 @@ const PPCSubtarget *Subtarget = nullptr; StackMaps SM; + virtual void trackTOCRef(const MCSymbol *Sym) {} + public: explicit PPCAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -223,6 +225,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(); @@ -233,6 +249,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)) { @@ -256,6 +280,8 @@ void emitFunctionEntryLabel() override; + void emitFunctionBodyStart() override { IsFuncBodyTOC = true; } + void emitFunctionBodyEnd() override; void emitPGORefs(); @@ -421,6 +447,7 @@ MCSymbol * PPCAsmPrinter::lookUpOrCreateTOCEntry(const MCSymbol *Sym, MCSymbolRefExpr::VariantKind Kind) { + trackTOCRef(Sym); MCSymbol *&TOCEntry = TOC[{Sym, Kind}]; if (!TOCEntry) TOCEntry = createTempSymbol("C"); @@ -1953,6 +1980,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)->getName()); + FuncBodyTouchedTOC.clear(); + } + IsFuncBodyTOC = false; if (!TM.getXCOFFTracebackTable()) return; @@ -2444,6 +2480,8 @@ MCSymbol *EmittedInitSym = GVSym; + emitAssociatedRefs(*GV); + // Emit linkage for the global variable and its aliases. emitLinkage(GV, EmittedInitSym); for (const GlobalAlias *GA : GOAliasMap[GV]) @@ -2473,6 +2511,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)->getName()); + } +} + void PPCAIXAsmPrinter::emitFunctionDescriptor() { const DataLayout &DL = getDataLayout(); const unsigned PointerSize = DL.getPointerSizeInBits() == 64 ? 8 : 4; @@ -2486,6 +2544,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); @@ -2643,6 +2704,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; } @@ -2707,6 +2777,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)->getName()); + } + 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