diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -111,6 +111,10 @@ /// of each call to runOnMachineFunction(). MCSymbol *CurrentFnSym = nullptr; + /// The symbol for the current function descriptor on AIX. This is created + /// at the beginning of each call to runOnMachineFunction(). + MCSymbol *CurrentFnDescSym = nullptr; + /// The symbol used to represent the start of the current function for the /// purpose of calculating its size (e.g. using the .size directive). By /// default, this is equal to CurrentFnSym. @@ -304,7 +308,7 @@ /// This should be called when a new MachineFunction is being processed from /// runOnMachineFunction. - void SetupMachineFunction(MachineFunction &MF); + virtual void SetupMachineFunction(MachineFunction &MF); /// This method emits the body and trailer for a function. void EmitFunctionBody(); 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 @@ -317,6 +317,10 @@ /// Defaults to false. bool HasLinkOnceDirective = false; + /// True if we have a .lglobl directive, which is used to emit the information + /// of a static symbol into the symbol table. Defaults to false. + bool HasDotLGloblDirective = false; + /// This attribute, if not MCSA_Invalid, is used to declare a symbol as having /// hidden visibility. Defaults to MCSA_Hidden. MCSymbolAttr HiddenVisibilityAttr = MCSA_Hidden; @@ -392,6 +396,9 @@ // %hi(), and similar unary operators. bool HasMipsExpressions = false; + // If true, emit function descriptor symbol on AIX. + bool NeedsFunctionDescriptors = false; + public: explicit MCAsmInfo(); virtual ~MCAsmInfo(); @@ -565,6 +572,8 @@ bool hasLinkOnceDirective() const { return HasLinkOnceDirective; } + bool hasDotLGloblDirective() const { return HasDotLGloblDirective; } + MCSymbolAttr getHiddenVisibilityAttr() const { return HiddenVisibilityAttr; } MCSymbolAttr getHiddenDeclarationVisibilityAttr() const { @@ -647,6 +656,7 @@ bool canRelaxRelocations() const { return RelaxELFRelocations; } void setRelaxELFRelocations(bool V) { RelaxELFRelocations = V; } bool hasMipsExpressions() const { return HasMipsExpressions; } + bool needsFunctionDescriptors() const { return NeedsFunctionDescriptors; } }; } // end namespace llvm diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h --- a/llvm/include/llvm/MC/MCDirectives.h +++ b/llvm/include/llvm/MC/MCDirectives.h @@ -28,6 +28,7 @@ MCSA_ELF_TypeNoType, ///< .type _foo, STT_NOTYPE # aka @notype MCSA_ELF_TypeGnuUniqueObject, /// .type _foo, @gnu_unique_object MCSA_Global, ///< .globl + MCSA_LGlobal, ///< .lglobl (XCOFF) MCSA_Hidden, ///< .hidden (ELF) MCSA_IndirectSymbol, ///< .indirect_symbol (MachO) MCSA_Internal, ///< .internal (ELF) diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -235,6 +235,7 @@ VK_PPC_TOC_LO, // symbol@toc@l VK_PPC_TOC_HI, // symbol@toc@h VK_PPC_TOC_HA, // symbol@toc@ha + VK_PPC_TOC_TC0, // symbol[tc0] VK_PPC_DTPMOD, // symbol@dtpmod VK_PPC_TPREL_LO, // symbol@tprel@l VK_PPC_TPREL_HI, // symbol@tprel@h 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 @@ -35,14 +35,14 @@ return StorageClass.getValue(); } - void setContainingCsect(const MCSectionXCOFF *C) { + void setContainingCsect(MCSectionXCOFF *C) { assert((!ContainingCsect || ContainingCsect == C) && "Trying to set a containing csect that doesn't match the one that" "this symbol is already mapped to."); ContainingCsect = C; } - const MCSectionXCOFF *getContainingCsect() const { + MCSectionXCOFF *getContainingCsect() const { assert(ContainingCsect && "Trying to get containing csect but none was set."); return ContainingCsect; @@ -50,7 +50,7 @@ private: Optional StorageClass; - const MCSectionXCOFF *ContainingCsect = nullptr; + MCSectionXCOFF *ContainingCsect = nullptr; }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -91,10 +91,12 @@ #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSectionXCOFF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCValue.h" #include "llvm/MC/SectionKind.h" @@ -426,7 +428,10 @@ OutStreamer->EmitSymbolAttribute(GVSym, MCSA_Global); return; case GlobalValue::PrivateLinkage: + return; case GlobalValue::InternalLinkage: + if (MAI->hasDotLGloblDirective()) + OutStreamer->EmitSymbolAttribute(GVSym, MCSA_LGlobal); return; case GlobalValue::AppendingLinkage: case GlobalValue::AvailableExternallyLinkage: @@ -662,6 +667,10 @@ OutStreamer->SwitchSection(getObjFileLowering().SectionForGlobal(&F, TM)); EmitVisibility(CurrentFnSym, F.getVisibility()); + if (MAI->needsFunctionDescriptors() && + F.getLinkage() != GlobalValue::InternalLinkage) + EmitLinkage(&F, CurrentFnDescSym); + EmitLinkage(&F, CurrentFnSym); if (MAI->hasFunctionAlignment()) EmitAlignment(MF->getAlignment(), &F); diff --git a/llvm/lib/MC/MCAsmInfoXCOFF.cpp b/llvm/lib/MC/MCAsmInfoXCOFF.cpp --- a/llvm/lib/MC/MCAsmInfoXCOFF.cpp +++ b/llvm/lib/MC/MCAsmInfoXCOFF.cpp @@ -20,5 +20,7 @@ UseDotAlignForAlignment = true; AsciiDirective = nullptr; // not supported AscizDirective = nullptr; // not supported + NeedsFunctionDescriptors = true; + HasDotLGloblDirective = true; Data64bitsDirective = "\t.llong\t"; } 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 @@ -650,6 +650,7 @@ case MCSA_Global: // .globl/.global OS << MAI->getGlobalDirective(); break; + case MCSA_LGlobal: OS << "\t.lglobl\t"; break; case MCSA_Hidden: OS << "\t.hidden\t"; break; case MCSA_IndirectSymbol: OS << "\t.indirect_symbol\t"; break; case MCSA_Internal: OS << "\t.internal\t"; break; diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -277,6 +277,9 @@ case MCSA_AltEntry: llvm_unreachable("ELF doesn't support the .alt_entry attribute"); + + case MCSA_LGlobal: + llvm_unreachable("ELF doesn't support the .lglobl attribute"); } return true; diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -259,6 +259,7 @@ case VK_PPC_TOC_LO: return "toc@l"; case VK_PPC_TOC_HI: return "toc@h"; case VK_PPC_TOC_HA: return "toc@ha"; + case VK_PPC_TOC_TC0: return "tc0"; case VK_PPC_DTPMOD: return "dtpmod"; case VK_PPC_TPREL_LO: return "tprel@l"; case VK_PPC_TPREL_HI: return "tprel@h"; @@ -373,6 +374,7 @@ .Case("toc@l", VK_PPC_TOC_LO) .Case("toc@h", VK_PPC_TOC_HI) .Case("toc@ha", VK_PPC_TOC_HA) + .Case("tc0", VK_PPC_TOC_TC0) .Case("tls", VK_PPC_TLS) .Case("dtpmod", VK_PPC_DTPMOD) .Case("tprel@l", VK_PPC_TPREL_LO) @@ -440,7 +442,9 @@ } void MCSymbolRefExpr::printVariantKind(raw_ostream &OS) const { - if (UseParensForSymbolVariant) + if (getKind() == MCSymbolRefExpr::VK_PPC_TOC_TC0) + OS << '[' << MCSymbolRefExpr::getVariantKindName(getKind()) << ']'; + else if (UseParensForSymbolVariant) OS << '(' << MCSymbolRefExpr::getVariantKindName(getKind()) << ')'; else OS << '@' << MCSymbolRefExpr::getVariantKindName(getKind()); diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -330,6 +330,7 @@ case MCSA_Protected: case MCSA_Weak: case MCSA_Local: + case MCSA_LGlobal: return false; case MCSA_Global: 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 @@ -15,26 +15,46 @@ MCSectionXCOFF::~MCSectionXCOFF() = default; +static StringRef getMappingClassString(XCOFF::StorageMappingClass SMC) { + switch (SMC) { + case XCOFF::XMC_DS: + return "DS"; + case XCOFF::XMC_RW: + return "RW"; + case XCOFF::XMC_PR: + return "PR"; + default: + report_fatal_error("Unhandled storage-mapping class."); + } +} + void MCSectionXCOFF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, raw_ostream &OS, const MCExpr *Subsection) const { if (getKind().isText()) { if (getMappingClass() != XCOFF::XMC_PR) - llvm_unreachable("Unsupported storage-mapping class for .text csect"); + report_fatal_error("Unhandled storage-mapping class for .text csect"); OS << "\t.csect " << getSectionName() << "[" - << "PR" + << getMappingClassString(getMappingClass()) << "]" << '\n'; return; } if (getKind().isData()) { - assert(getMappingClass() == XCOFF::XMC_RW && - "Unhandled storage-mapping class for data section."); - - OS << "\t.csect " << getSectionName() << "[" - << "RW" - << "]" << '\n'; + switch (getMappingClass()) { + case XCOFF::XMC_RW: + case XCOFF::XMC_DS: + OS << "\t.csect " << getSectionName() << "[" + << getMappingClassString(getMappingClass()) << "]" << '\n'; + break; + case XCOFF::XMC_TC0: + OS << "\t.toc\n"; + break; + default: + report_fatal_error( + "Unhandled storage-mapping class for .data csect."); + } return; } diff --git a/llvm/lib/MC/XCOFFObjectWriter.cpp b/llvm/lib/MC/XCOFFObjectWriter.cpp --- a/llvm/lib/MC/XCOFFObjectWriter.cpp +++ b/llvm/lib/MC/XCOFFObjectWriter.cpp @@ -235,6 +235,9 @@ break; } report_fatal_error("Unhandled mapping of read-write csect to section."); + case XCOFF::XMC_TC0: + // TODO FIXME Handle emiting the TOC-base. + break; case XCOFF::XMC_BS: assert(XCOFF::XTY_CM == MCSec->getCSectType() && "Mapping invalid csect. CSECT with bss storage class must be " 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 @@ -39,6 +39,7 @@ #include "llvm/CodeGen/MachineModuleInfoImpls.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" #include "llvm/CodeGen/StackMaps.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/IR/DataLayout.h" @@ -167,7 +168,13 @@ StringRef getPassName() const override { return "AIX PPC Assembly Printer"; } + void SetupMachineFunction(MachineFunction &MF) override; + void EmitGlobalVariable(const GlobalVariable *GV) override; + + void EmitFunctionEntryLabel() override; + + void EmitEndOfAsmFile(Module &) override; }; } // end anonymous namespace @@ -1648,6 +1655,29 @@ return AsmPrinter::doFinalization(M); } +void PPCAIXAsmPrinter::SetupMachineFunction(MachineFunction &MF) { + this->MF = &MF; + const Function &F = MF.getFunction(); + + // Get the function descriptor symbol. + CurrentFnDescSym = getSymbol(&F); + // Set the containing csect. + StringRef FnDescSymName(CurrentFnDescSym->getName()); + MCSectionXCOFF *FnDescSec = OutStreamer->getContext().getXCOFFSection( + FnDescSymName, XCOFF::XMC_DS, XCOFF::XTY_SD, XCOFF::C_HIDEXT, + SectionKind::getData()); + cast(CurrentFnDescSym)->setContainingCsect(FnDescSec); + + // Get the function entry point symbol. + CurrentFnSym = OutContext.getOrCreateSymbol("." + FnDescSymName); + MCSectionXCOFF *FnEntryPointSec = + cast(getObjFileLowering().SectionForGlobal(&F, TM)); + // Set the containing csect. + cast(CurrentFnSym)->setContainingCsect(FnEntryPointSec); + + ORE = &getAnalysis().getORE(); +} + void PPCAIXAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { // Early error checking limiting what is supported. if (GV->isThreadLocal()) @@ -1697,6 +1727,48 @@ EmitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); } +void PPCAIXAsmPrinter::EmitFunctionEntryLabel() { + const DataLayout &DL = getDataLayout(); + const unsigned PointerSize = DL.getPointerSizeInBits() == 64 ? 8 : 4; + + MCSectionSubPair Current = OutStreamer->getCurrentSection(); + // Emit function descriptor. + OutStreamer->SwitchSection( + cast(CurrentFnDescSym)->getContainingCsect()); + OutStreamer->EmitLabel(CurrentFnDescSym); + // Emit TOC base address. + OutStreamer->EmitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), + PointerSize); + MCSymbol *TOCBaseSym = OutContext.getOrCreateSymbol(StringRef("TOC")); + OutStreamer->EmitValue( + MCSymbolRefExpr::create(TOCBaseSym, MCSymbolRefExpr::VK_PPC_TOC_TC0, + OutContext), + PointerSize); + // Emit a null environment pointer. + OutStreamer->EmitIntValue(0, PointerSize); + + OutStreamer->SwitchSection(Current.first, Current.second); + OutStreamer->EmitLabel(CurrentFnSym); +} + +void PPCAIXAsmPrinter::EmitEndOfAsmFile(Module &M) { + // If there are no functions in this module, we will never need to reference + // the TOC base. + if (M.empty()) + return; + + // Emit TOC base. + StringRef TOC("TOC"); + MCSymbol *TOCBaseSym = OutContext.getOrCreateSymbol(TOC); + MCSectionXCOFF *TOCBaseSection = OutStreamer->getContext().getXCOFFSection( + TOC, XCOFF::XMC_TC0, XCOFF::XTY_SD, XCOFF::C_HIDEXT, + SectionKind::getData()); + cast(TOCBaseSym)->setContainingCsect(TOCBaseSection); + // Switch to section to emit TOC base. + OutStreamer->SwitchSection(TOCBaseSection); +} + + /// createPPCAsmPrinterPass - Returns a pass that prints the PPC assembly code /// for a MachineFunction to the given output stream, in a format that the /// Darwin assembler can deal with. diff --git a/llvm/test/CodeGen/PowerPC/aix-xcoff-common.ll b/llvm/test/CodeGen/PowerPC/aix-xcoff-common.ll --- a/llvm/test/CodeGen/PowerPC/aix-xcoff-common.ll +++ b/llvm/test/CodeGen/PowerPC/aix-xcoff-common.ll @@ -21,6 +21,8 @@ @array = common local_unnamed_addr global [33 x i8] zeroinitializer, align 1 +; CHECK-NOT: .toc + ; CHECK: .csect .text[PR] ; CHECK-NEXT: .file ; CHECK-NEXT: .comm a,4,2 diff --git a/llvm/test/CodeGen/PowerPC/test_func_desc.ll b/llvm/test/CodeGen/PowerPC/test_func_desc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/test_func_desc.ll @@ -0,0 +1,73 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefix=32BIT %s + +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefix=64BIT %s + + +define i32 @foo() { +entry: + ret i32 3 +} + +define i32 @main() { +entry: + %0 = call i32 @foo() + %1 = call i32 bitcast (i32 (...)* @extern_foo to i32 ()*)() + %2 = call i32 @static_foo() + %3 = add nsw i32 %0, %1 + %4 = add nsw i32 %3, %2 + ret i32 %4 +} + +declare i32 @extern_foo(...) + +define internal i32 @static_foo() { +entry: + ret i32 3 +} + +; CHECK: .globl foo +; CHECK: .globl .foo +; CHECK: .csect foo[DS] +; CHECK-NEXT: foo: +; 32BIT: .long .foo +; 32BIT-NEXT: .long TOC[tc0] +; 32BIT-NEXT: .long 0 +; 64BIT: .llong .foo +; 64BIT-NEXT: .llong TOC[tc0] +; 64BIT-NEXT: .llong 0 +; CHECK-NEXT: .csect .text[PR] +; CHECK-LABEL: .foo: + +; CHECK: .globl main +; CHECK: .globl .main +; CHECK: .csect main[DS] +; CHECK-NEXT: main: +; 32BIT: .long .main +; 32BIT-NEXT: .long TOC[tc0] +; 32BIT-NEXT: .long 0 +; 64BIT: .llong .main +; 64BIT-NEXT: .llong TOC[tc0] +; 64BIT-NEXT: .llong 0 +; CHECK-NEXT: .csect .text[PR] +; CHECK-LABEL: .main: +; CHECK: bl .foo +; CHECK: bl .extern_foo +; CHECK: bl .static_foo + +; CHECK: .lglobl .static_foo +; CHECK: .csect static_foo[DS] +; CHECK-NEXT: static_foo: +; 32BIT: .long .static_foo +; 32BIT-NEXT: .long TOC[tc0] +; 32BIT-NEXT: .long 0 +; 64BIT: .llong .static_foo +; 64BIT-NEXT: .llong TOC[tc0] +; 64BIT-NEXT: .llong 0 +; CHECK-NEXT: .csect .text[PR] +; CHECK-LABEL: .static_foo: + +; CHECK-NOT: .csect extern_foo + +; CHECK: .toc