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 @@ -2330,11 +2330,20 @@ if (Kind.isBSSLocal() || GO->hasCommonLinkage() || Kind.isThreadBSSLocal()) { SmallString<128> Name; getNameWithPrefix(Name, GO, TM); - XCOFF::StorageMappingClass SMC = Kind.isBSSLocal() ? XCOFF::XMC_BS - : Kind.isCommon() ? XCOFF::XMC_RW - : XCOFF::XMC_UL; + bool EmitREF = false; + // Static global variable has associated initializer emits .ref to indicate + // the relationship. However csect with BS storage mapping class cannot + // contain .ref. + if (Kind.isBSSLocal() && GO->getMetadata(LLVMContext::MD_associated)) + EmitREF = true; + + XCOFF::StorageMappingClass SMC = + Kind.isBSSLocal() ? (EmitREF ? XCOFF::XMC_RW : XCOFF::XMC_BS) + : Kind.isCommon() ? XCOFF::XMC_RW + : XCOFF::XMC_UL; return getContext().getXCOFFSection( - Name, Kind, XCOFF::CsectProperties(SMC, XCOFF::XTY_CM)); + Name, Kind, + XCOFF::CsectProperties(SMC, EmitREF ? XCOFF::XTY_SD : XCOFF::XTY_CM)); } if (Kind.isMergeableCString()) { 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 @@ -933,6 +933,10 @@ } void MCAsmStreamer::emitXCOFFRefDirective(StringRef Name) { + 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/MC/MCSectionXCOFF.cpp b/llvm/lib/MC/MCSectionXCOFF.cpp --- a/llvm/lib/MC/MCSectionXCOFF.cpp +++ b/llvm/lib/MC/MCSectionXCOFF.cpp @@ -116,6 +116,13 @@ return; } + // Static global variable with initializer should map to RW + if (getKind().isBSSLocal() && isCsect() && + getMappingClass() == XCOFF::XMC_RW && getCSectType() == XCOFF::XTY_SD) { + printCsectDirective(OS); + return; + } + report_fatal_error("Printing for this SectionKind is unimplemented."); } 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 @@ -230,6 +230,8 @@ void emitGlobalVariableHelper(const GlobalVariable *); + void emitAssociatedRefs(const GlobalObject &); + public: PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : PPCAsmPrinter(TM, std::move(Streamer)) { @@ -2400,7 +2402,8 @@ const DataLayout &DL = GV->getParent()->getDataLayout(); // Handle common and zero-initialized local symbols. - if (GV->hasCommonLinkage() || GVKind.isBSSLocal() || + if (GV->hasCommonLinkage() || + (GVKind.isBSSLocal() && !GV->getMetadata(LLVMContext::MD_associated)) || GVKind.isThreadBSSLocal()) { Align Alignment = GV->getAlign().getValueOr(DL.getPreferredAlign(GV)); uint64_t Size = DL.getTypeAllocSize(GV->getValueType()); @@ -2417,6 +2420,7 @@ } MCSymbol *EmittedInitSym = GVSym; + emitAssociatedRefs(*GV); emitLinkage(GV, EmittedInitSym); emitAlignment(getGVAlignment(GV, DL), GV); @@ -2434,6 +2438,25 @@ emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); } +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()) + report_fatal_error("MD_associated operand not found"); + + 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())) + OutStreamer->emitXCOFFRefDirective(getSymbol(OtherGV)->getName()); + } +} + void PPCAIXAsmPrinter::emitFunctionDescriptor() { const DataLayout &DL = getDataLayout(); const unsigned PointerSize = DL.getPointerSizeInBits() == 64 ? 8 : 4; @@ -2449,6 +2472,8 @@ OutStreamer->emitLabel(getSymbol(Alias)); }); + // Emit reference to function or static variables + emitAssociatedRefs(MF->getFunction()); // Emit function entry point address. OutStreamer->emitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), PointerSize); 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,44 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff --filetype=asm < %s | FileCheck %s + +@a = internal global i32 0, align 4, !associated !0 +@b = internal global i32 0, align 4, !associated !0 +@c = internal global i32 0, align 4 + +define i32 @foo() !associated !1 { +entry: + %0 = load i32, i32* @a, align 4 + ret i32 %0 +} + +define void @bar() !associated !2 { +entry: + ret void +} + +define void @baz() !associated !3 { +entry: + ret void +} + +!0 = !{i32 ()* @foo, void ()* @bar} +!1 = !{i32* @b} +!2 = !{i32* @a, i32* @b} +!3 = !{void ()* @bar} + +; CHECK: .csect foo[DS],2 +; CHECK-NEXT: .ref b[RW] # @foo + +; CHECK: .csect bar[DS],2 +; CHECK-NEXT: .ref a[RW] # @bar +; CHECK-NEXT: .ref b[RW] + +; CHECK: .csect baz[DS],2 +; CHECK-NEXT: .ref bar[DS] # @baz + +; CHECK: .csect a[RW],2 +; CHECK-NEXT: .ref foo[DS] # @a +; CHECK-NEXT: .ref bar[DS] + +; CHECK: .csect b[RW],2 +; CHECK-NEXT: .ref foo[DS] # @b +; CHECK-NEXT: .ref bar[DS]