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. 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/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: @@ -665,6 +670,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); @@ -1645,7 +1654,31 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) { this->MF = &MF; // Get the function symbol. - CurrentFnSym = getSymbol(&MF.getFunction()); + if (MAI->needsFunctionDescriptors()) { + assert(TM.getTargetTriple().isOSAIX() && "Function descriptor is only" + " supported on AIX."); + + // Get the function descriptor symbol. + CurrentFnDescSym = getSymbol(&MF.getFunction()); + // Set the containing csect. + StringRef SecName(CurrentFnDescSym->getName()); + MCSectionXCOFF *FnDescSec = OutStreamer->getContext().getXCOFFSection( + SecName, XCOFF::XMC_DS, XCOFF::XTY_SD, XCOFF::C_HIDEXT, + SectionKind::getData()); + cast(CurrentFnDescSym)->setContainingCsect(FnDescSec); + + // Get the function entry point symbol. + CurrentFnSym = + OutContext.getOrCreateSymbol("." + CurrentFnDescSym->getName()); + // Set the containing csect. + const Function &F = MF.getFunction(); + MCSectionXCOFF *FnEntryPointSec = + cast(getObjFileLowering().SectionForGlobal(&F, TM)); + cast(CurrentFnSym)->setContainingCsect(FnEntryPointSec); + } else { + CurrentFnSym = getSymbol(&MF.getFunction()); + } + CurrentFnSymForSize = CurrentFnSym; CurrentFnBegin = nullptr; CurExceptionSym = nullptr; 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 @@ -649,6 +649,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; default: report_fatal_error("Unhandled mapping of csect to section."); } 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 @@ -168,6 +168,10 @@ StringRef getPassName() const override { return "AIX PPC Assembly Printer"; } void EmitGlobalVariable(const GlobalVariable *GV) override; + + void EmitFunctionEntryLabel() override; + + void EmitStartOfAsmFile(Module &M) override; }; } // end anonymous namespace @@ -1699,6 +1703,43 @@ EmitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); } +void PPCAIXAsmPrinter::EmitFunctionEntryLabel() { + const DataLayout &DL = getDataLayout(); + unsigned PointerSize = DL.getPointerSizeInBits() == 64 ? 8 : 4; + + MCSectionSubPair Current = OutStreamer->getCurrentSection(); + // Emit function descriptor. + OutStreamer->SwitchSection(const_cast(cast + (CurrentFnDescSym)->getContainingCsect())); + OutStreamer->EmitLabel(CurrentFnDescSym); + OutStreamer->EmitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), + Size); + MCSymbol *TOCBaseSym = OutContext.getOrCreateSymbol(StringRef("TOC")); + OutStreamer->EmitValue( + MCSymbolRefExpr::create(TOCBaseSym, MCSymbolRefExpr::VK_PPC_TOC_TC0, + OutContext), + Size); + // Emit a null environment pointer. + OutStreamer->EmitIntValue(0, Size); + + OutStreamer->SwitchSection(Current.first, Current.second); + OutStreamer->EmitLabel(CurrentFnSym); +} + +void PPCAIXAsmPrinter::EmitStartOfAsmFile(Module &M) { + // Since we will almost always need it, map the TOC anchor to its containing + // csect. That way, other methods can freely query it, assuming that it is + // set. + StringRef TOC("TOC"); + MCSymbol *TOCBaseSym = OutContext.getOrCreateSymbol(TOC); + MCSectionXCOFF *TOCAnchor = OutStreamer->getContext().getXCOFFSection( + TOC, XCOFF::XMC_TC0, XCOFF::XTY_SD, XCOFF::C_HIDEXT, + SectionKind::getData()); + cast(TOCBaseSym)->setContainingCsect(TOCAnchor); + // Switch to section to emit TOC-base. + OutStreamer->SwitchSection(TOCAnchor); +} + /// 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,7 @@ @array = common local_unnamed_addr global [33 x i8] zeroinitializer, align 1 ; CHECK: .csect .text[PR] +; CHECK: .toc ; CHECK-NEXT: .file ; CHECK-NEXT: .comm a,4,2 ; CHECK-NEXT: .comm b,8,3 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,84 @@ +; 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: + %retval = alloca i32, align 4 + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 0, i32* %retval, align 4 + %call = call i32 @foo() + store i32 %call, i32* %a, align 4 + %call1 = call i32 bitcast (i32 (...)* @extern_foo to i32 ()*)() + store i32 %call1, i32* %b, align 4 + %call2 = call i32 @static_foo() + store i32 %call2, i32* %c, align 4 + %0 = load i32, i32* %a, align 4 + %1 = load i32, i32* %b, align 4 + %add = add nsw i32 %0, %1 + %2 = load i32, i32* %c, align 4 + %add3 = add nsw i32 %add, %2 + ret i32 %add3 +} + +declare i32 @extern_foo(...) + +define internal i32 @static_foo() { +entry: + ret i32 3 +} + +;CHECK: .toc + +; 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