diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -333,6 +333,10 @@ /// protected visibility. Defaults to MCSA_Protected MCSymbolAttr ProtectedVisibilityAttr = MCSA_Protected; + // This attribute is used to indicate symbols such as commons on AIX may have + // a storage mapping class embedded in the name. + bool SymbolsHaveSMC = false; + //===--- Dwarf Emission Directives -----------------------------------===// /// True if target supports emission of debugging information. Defaults to @@ -587,6 +591,8 @@ return ProtectedVisibilityAttr; } + bool getSymbolsHaveSMC() const { return SymbolsHaveSMC; } + bool doesSupportDebugInformation() const { return SupportsDebugInformation; } bool doesSupportExceptionHandling() const { diff --git a/llvm/include/llvm/MC/MCSymbolXCOFF.h b/llvm/include/llvm/MC/MCSymbolXCOFF.h --- a/llvm/include/llvm/MC/MCSymbolXCOFF.h +++ b/llvm/include/llvm/MC/MCSymbolXCOFF.h @@ -9,6 +9,7 @@ #define LLVM_MC_MCSYMBOLXCOFF_H #include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/XCOFF.h" #include "llvm/MC/MCSymbol.h" @@ -50,6 +51,17 @@ bool hasContainingCsect() const { return ContainingCsect != nullptr; } + StringRef getUnqualifiedName() const { + const StringRef name = getName(); + if (name.back() == ']') { + StringRef lhs, rhs; + std::tie(lhs, rhs) = name.rsplit('['); + assert(!rhs.empty() && "Invalid SMC format in XCOFF symbol."); + return lhs; + } + return name; + } + private: Optional StorageClass; MCSectionXCOFF *ContainingCsect = nullptr; diff --git a/llvm/lib/BinaryFormat/XCOFF.cpp b/llvm/lib/BinaryFormat/XCOFF.cpp --- a/llvm/lib/BinaryFormat/XCOFF.cpp +++ b/llvm/lib/BinaryFormat/XCOFF.cpp @@ -24,6 +24,10 @@ return "BS"; case XCOFF::XMC_RO: return "RO"; + case XCOFF::XMC_UA: + return "UA"; + case XCOFF::XMC_TC: + return "TC"; default: report_fatal_error("Unhandled storage-mapping class."); } 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 @@ -40,6 +40,8 @@ case XCOFF::XMC_DS: OS << "\t.csect " << QualName->getName() << '\n'; break; + case XCOFF::XMC_TC: + break; case XCOFF::XMC_TC0: OS << "\t.toc\n"; break; diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCAsmInfo.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCAsmInfo.cpp --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCAsmInfo.cpp +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCAsmInfo.cpp @@ -87,4 +87,5 @@ assert(!IsLittleEndian && "Little-endian XCOFF not supported."); CodePointerSize = CalleeSaveStackSlotSize = Is64Bit ? 8 : 4; ZeroDirective = "\t.space\t"; + SymbolsHaveSMC = true; } diff --git a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp --- a/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp +++ b/llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp @@ -30,6 +30,7 @@ #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/ErrorHandling.h" @@ -108,8 +109,11 @@ : PPCTargetStreamer(S), OS(OS) {} void emitTCEntry(const MCSymbol &S) override { + const MCAsmInfo *MAI = Streamer.getContext().getAsmInfo(); OS << "\t.tc "; - OS << S.getName(); + OS << (MAI->getSymbolsHaveSMC() + ? cast(S).getUnqualifiedName() + : S.getName()); OS << "[TC],"; OS << S.getName(); OS << '\n'; @@ -243,7 +247,7 @@ PPCTargetXCOFFStreamer(MCStreamer &S) : PPCTargetStreamer(S) {} void emitTCEntry(const MCSymbol &S) override { - report_fatal_error("TOC entries not supported yet."); + // Object writing TOC entries not supported yet. } void emitMachine(StringRef CPU) override { 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 @@ -43,6 +43,7 @@ #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalValue.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -82,6 +83,8 @@ const PPCSubtarget *Subtarget = nullptr; StackMaps SM; + virtual MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO); + public: explicit PPCAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -161,6 +164,11 @@ }; class PPCAIXAsmPrinter : public PPCAsmPrinter { +private: + static void ValidateGV(const GlobalVariable *GV); +protected: + MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO) override; + public: PPCAIXAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : PPCAsmPrinter(TM, std::move(Streamer)) {} @@ -514,17 +522,16 @@ /// Map a machine operand for a TOC pseudo-machine instruction to its /// corresponding MCSymbol. -static MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO, - AsmPrinter &AP) { +MCSymbol *PPCAsmPrinter::getMCSymbolForTOCPseudoMO(const MachineOperand &MO) { switch (MO.getType()) { case MachineOperand::MO_GlobalAddress: - return AP.getSymbol(MO.getGlobal()); + return getSymbol(MO.getGlobal()); case MachineOperand::MO_ConstantPoolIndex: - return AP.GetCPISymbol(MO.getIndex()); + return GetCPISymbol(MO.getIndex()); case MachineOperand::MO_JumpTableIndex: - return AP.GetJTISymbol(MO.getIndex()); + return GetJTISymbol(MO.getIndex()); case MachineOperand::MO_BlockAddress: - return AP.GetBlockAddressSymbol(MO.getBlockAddress()); + return GetBlockAddressSymbol(MO.getBlockAddress()); default: llvm_unreachable("Unexpected operand type to get symbol."); } @@ -688,7 +695,7 @@ "Invalid operand for LWZtoc."); // Map the operand to its corresponding MCSymbol. - const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + const MCSymbol *const MOSymbol = getMCSymbolForTOCPseudoMO(MO); // Create a reference to the GOT entry for the symbol. The GOT entry will be // synthesized later. @@ -749,7 +756,7 @@ // global address operand to be a reference to the TOC entry we will // synthesize later. MCSymbol *TOCEntry = - lookUpOrCreateTOCEntry(getMCSymbolForTOCPseudoMO(MO, *this)); + lookUpOrCreateTOCEntry(getMCSymbolForTOCPseudoMO(MO)); const MCSymbolRefExpr::VariantKind VK = IsAIX ? MCSymbolRefExpr::VK_None : MCSymbolRefExpr::VK_PPC_TOC; @@ -775,7 +782,7 @@ "Invalid operand for ADDIStocHA."); // Map the machine operand to its corresponding MCSymbol. - MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO); // Always use TOC on AIX. Map the global address operand to be a reference // to the TOC entry we will synthesize later. 'TOCEntry' is a label used to @@ -805,7 +812,7 @@ "Invalid operand for LWZtocL."); // Map the machine operand to its corresponding MCSymbol. - MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO); // Always use TOC on AIX. Map the global address operand to be a reference // to the TOC entry we will synthesize later. 'TOCEntry' is a label used to @@ -835,7 +842,7 @@ assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) && "Invalid operand for ADDIStocHA8!"); - const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO); const bool GlobalToc = MO.isGlobal() && Subtarget->isGVIndirectSymbol(MO.getGlobal()); @@ -881,7 +888,7 @@ "LDtocL used on symbol that could be accessed directly is " "invalid. Must match ADDIStocHA8.")); - const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO, *this); + const MCSymbol *MOSymbol = getMCSymbolForTOCPseudoMO(MO); if (!MO.isCPI() || TM.getCodeModel() == CodeModel::Large) MOSymbol = lookUpOrCreateTOCEntry(MOSymbol); @@ -911,7 +918,7 @@ "Interposable definitions must use indirect access.")); const MCExpr *Exp = - MCSymbolRefExpr::create(getMCSymbolForTOCPseudoMO(MO, *this), + MCSymbolRefExpr::create(getMCSymbolForTOCPseudoMO(MO), MCSymbolRefExpr::VK_PPC_TOC_LO, OutContext); TmpInst.getOperand(2) = MCOperand::createExpr(Exp); EmitToStreamer(*OutStreamer, TmpInst); @@ -1736,7 +1743,7 @@ return AsmPrinter::SetupMachineFunction(MF); } -void PPCAIXAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { +void PPCAIXAsmPrinter::ValidateGV(const GlobalVariable *GV) { // Early error checking limiting what is supported. if (GV->isThreadLocal()) report_fatal_error("Thread local not yet supported on AIX."); @@ -1746,6 +1753,19 @@ if (GV->hasComdat()) report_fatal_error("COMDAT not yet supported by AIX."); +} + +void PPCAIXAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { + ValidateGV(GV); + + // External global variables are already handled. + if (!GV->hasInitializer()) + return; + + // Create the symbol, set its storage class. + MCSymbolXCOFF *GVSym = cast(getSymbol(GV)); + GVSym->setStorageClass( + TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GV)); SectionKind GVKind = getObjFileLowering().getKindForGlobal(GV, TM); if ((!GVKind.isCommon() && !GVKind.isBSS() && !GVKind.isData() && @@ -1759,11 +1779,6 @@ MCSectionXCOFF *Csect = cast( getObjFileLowering().SectionForGlobal(GV, GVKind, TM)); OutStreamer->SwitchSection(Csect); - - // Create the symbol, set its storage class, and emit it. - MCSymbolXCOFF *GVSym = cast(getSymbol(GV)); - GVSym->setStorageClass( - TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GV)); GVSym->setContainingCsect(Csect); const DataLayout &DL = GV->getParent()->getDataLayout(); @@ -1802,7 +1817,10 @@ OutStreamer->EmitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), PointerSize); // Emit TOC base address. - MCSymbol *TOCBaseSym = OutContext.getOrCreateSymbol(StringRef("TOC[TC0]")); + const MCSectionXCOFF *TOCBaseSec = OutStreamer->getContext().getXCOFFSection( + StringRef("TOC"), XCOFF::XMC_TC0, XCOFF::XTY_SD, XCOFF::C_HIDEXT, + SectionKind::getData()); + const MCSymbol *TOCBaseSym = TOCBaseSec->getQualNameSymbol(); OutStreamer->EmitValue(MCSymbolRefExpr::create(TOCBaseSym, OutContext), PointerSize); // Emit a null environment pointer. @@ -1823,8 +1841,80 @@ SectionKind::getData()); // Switch to section to emit TOC base. OutStreamer->SwitchSection(TOCBaseSection); + + PPCTargetStreamer &TS = + static_cast(*OutStreamer->getTargetStreamer()); + + for (auto &I : TOC) { + // Setup the csect for the current TC entry. + MCSectionXCOFF *TCEntry = OutStreamer->getContext().getXCOFFSection( + cast(I.first)->getUnqualifiedName(), XCOFF::XMC_TC, + XCOFF::XTY_SD, XCOFF::C_HIDEXT, SectionKind::getData()); + cast(I.second)->setContainingCsect(TCEntry); + OutStreamer->SwitchSection(TCEntry); + + OutStreamer->EmitLabel(I.second); + TS.emitTCEntry(*I.first); + } } +MCSymbol * +PPCAIXAsmPrinter::getMCSymbolForTOCPseudoMO(const MachineOperand &MO) { + const GlobalObject *GO = nullptr; + + // If the MO is a function or certain kind of globals, we want to make sure to + // refer to the csect symbol, otherwise we can just do the default handling. + if (MO.getType() != MachineOperand::MO_GlobalAddress || + !(GO = dyn_cast(MO.getGlobal()))) + return PPCAsmPrinter::getMCSymbolForTOCPseudoMO(MO); + + // Do an early error check for globals we don't support. This will go away + // eventually. + const auto *GV = dyn_cast(GO); + if (GV) { + ValidateGV(GV); + } + + MCSymbolXCOFF *XSym = cast(getSymbol(GO)); + + // If the global object is a global variable without initializer or is a + // declaration of a function, then XSym is an external referenced symbol. + // Hence we may need to explictly create a MCSectionXCOFF for it so that we + // can return its symbol later. + if (GO->isDeclaration() && !XSym->hasContainingCsect()) { + // Make sure the storage class is set. + const XCOFF::StorageClass SC = + TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO); + XSym->setStorageClass(SC); + + MCSectionXCOFF *Csect = OutStreamer->getContext().getXCOFFSection( + XSym->getName(), isa(GO) ? XCOFF::XMC_DS : XCOFF::XMC_UA, + XCOFF::XTY_ER, SC, SectionKind::getMetadata()); + XSym->setContainingCsect(Csect); + + return Csect->getQualNameSymbol(); + } + + // Handle initialized global variables. + if (GV) { + SectionKind GVKind = getObjFileLowering().getKindForGlobal(GV, TM); + + // If the operand is a common then we should refer to the csect symbol. + if (GVKind.isCommon() || GVKind.isBSSLocal()) { + MCSectionXCOFF *Csect = cast( + getObjFileLowering().SectionForGlobal(GV, GVKind, TM)); + return Csect->getQualNameSymbol(); + } + + // Other global variables are refered to by labels inside of a single csect, + // so refer to the label directly. + return getSymbol(GV); + } + + // If the MO is a function, we want to make sure to refer to the function + // descriptor csect. + return XSym->getContainingCsect()->getQualNameSymbol(); +} /// createPPCAsmPrinterPass - Returns a pass that prints the PPC assembly code /// for a MachineFunction to the given output stream, in a format that the diff --git a/llvm/test/CodeGen/PowerPC/aix-lower-block-address.ll b/llvm/test/CodeGen/PowerPC/aix-lower-block-address.ll --- a/llvm/test/CodeGen/PowerPC/aix-lower-block-address.ll +++ b/llvm/test/CodeGen/PowerPC/aix-lower-block-address.ll @@ -69,4 +69,4 @@ ; 64LARGE-ASM: ld [[REG2:[0-9]+]], LC0@l([[REG1]]) ; CHECK: .toc -; CHECK-NOT: .tc +; CHECK: .tc Ltmp0[TC],Ltmp0 diff --git a/llvm/test/CodeGen/PowerPC/aix-lower-constant-pool-index.ll b/llvm/test/CodeGen/PowerPC/aix-lower-constant-pool-index.ll --- a/llvm/test/CodeGen/PowerPC/aix-lower-constant-pool-index.ll +++ b/llvm/test/CodeGen/PowerPC/aix-lower-constant-pool-index.ll @@ -84,4 +84,4 @@ ; 64LARGE-ASM: blr ; CHECK: .toc -; CHECK-NOT: .tc +; CHECK: .tc .LCPI0_0[TC],.LCPI0_0 diff --git a/llvm/test/CodeGen/PowerPC/aix-lower-jump-table.ll b/llvm/test/CodeGen/PowerPC/aix-lower-jump-table.ll --- a/llvm/test/CodeGen/PowerPC/aix-lower-jump-table.ll +++ b/llvm/test/CodeGen/PowerPC/aix-lower-jump-table.ll @@ -185,4 +185,4 @@ ; 64LARGE-ASM: .long LBB0_5-.LJTI0_0 ; CHECK: .toc -; CHECK-NOT: .tc +; CHECK: .tc .LJTI0_0[TC],.LJTI0_0 diff --git a/llvm/test/CodeGen/PowerPC/aix-xcoff-data-only-notoc.ll b/llvm/test/CodeGen/PowerPC/aix-xcoff-data-only-notoc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-xcoff-data-only-notoc.ll @@ -0,0 +1,12 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck %s + +@a = external global i32, align 4 +@b = external global i64, align 8 +@c = external global i16, align 2 +@globa = common global i32 0, align 4 + +@ptr = internal global void (...)* null, align 4 + +; CHECK-NOT: .toc + diff --git a/llvm/test/CodeGen/PowerPC/aix-xcoff-toc.ll b/llvm/test/CodeGen/PowerPC/aix-xcoff-toc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-xcoff-toc.ll @@ -0,0 +1,48 @@ +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck --check-prefixes CHECK,CHECK32 %s +; RUN: llc -verify-machineinstrs -mcpu=pwr7 -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck --check-prefixes CHECK,CHECK64 %s + +@a = external global i32, align 4 +@b = external global i64, align 8 +@c = external global i16, align 2 +@globa = common global i32 0, align 4 + +@ptr = internal global void (...)* null, align 4 + +declare void @foo() + +define void @bar() { + %1 = alloca i8*, align 8 + store i32 0, i32* @a, align 4 + store i64 0, i64* @b, align 8 + store i16 0, i16* @c, align 2 + store i32 0, i32* @globa, align 4 + store void (...)* bitcast (void ()* @bar to void (...)*), void (...)** @ptr, align 4 + store i8* bitcast (void ()* @foo to i8*), i8** %1, align 8 + ret void +} + +; CHECK-NOT: .comm a +; CHECK-NOT: .lcomm a +; CHECK-NOT: .comm b +; CHECK-NOT: .lcomm b +; CHECK-NOT: .comm c +; CHECK-NOT: .lcomm c +; CHECK: .comm globa[RW],4,2 +; CHECK32: .lcomm ptr,4,ptr[BS],2 +; CHECK64: .lcomm ptr,8,ptr[BS],2 +; CHECK: .toc +; CHECK-NEXT: LC0: +; CHECK-NEXT: .tc a[TC],a[UA] +; CHECK-NEXT: LC1: +; CHECK-NEXT: .tc b[TC],b[UA] +; CHECK-NEXT: LC2: +; CHECK-NEXT: .tc c[TC],c[UA] +; CHECK-NEXT: LC3: +; CHECK-NEXT: .tc globa[TC],globa[RW] +; CHECK-NEXT: LC4: +; CHECK-NEXT: .tc ptr[TC],ptr[BS] +; CHECK-NEXT: LC5: +; CHECK-NEXT: .tc bar[TC],bar[DS] +; CHECK-NEXT: LC6: +; CHECK-NEXT: .tc foo[TC],foo[DS] + diff --git a/llvm/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll b/llvm/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll --- a/llvm/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll +++ b/llvm/test/CodeGen/PowerPC/lower-globaladdr32-aix-asm.ll @@ -41,5 +41,5 @@ ; LARGE: stw [[REG3:[0-9]+]], 0([[REG2]]) ; LARGE: blr -; TODO Update test when TOC-entry emission lands. -; CHECK-NOT: .tc +; CHECK: .tc a[TC],a +; CHECK: .tc b[TC],b diff --git a/llvm/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll b/llvm/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll --- a/llvm/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll +++ b/llvm/test/CodeGen/PowerPC/lower-globaladdr64-aix-asm.ll @@ -41,5 +41,5 @@ ; LARGE: stw [[REG3:[0-9]+]], 0([[REG2]]) ; LARGE: blr -; TODO Update test when TOC-entry emission lands. -; CHECK-NOT: .tc +; CHECK: .tc a[TC],a +; CHECK: .tc b[TC],b