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(); @@ -415,6 +419,10 @@ virtual void EmitFunctionEntryLabel(); + virtual void EmitFunctionDescriptor() { + llvm_unreachable("Function descriptor is target-specific."); + } + virtual void EmitMachineConstantPoolValue(MachineConstantPoolValue *MCPV); /// Targets can override this to change how global constants that are part of 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/MCAsmInfoXCOFF.h b/llvm/include/llvm/MC/MCAsmInfoXCOFF.h --- a/llvm/include/llvm/MC/MCAsmInfoXCOFF.h +++ b/llvm/include/llvm/MC/MCAsmInfoXCOFF.h @@ -18,6 +18,11 @@ protected: MCAsmInfoXCOFF(); + +public: + // Return true only when the identifier Name does not need quotes to be + // syntactically correct for XCOFF. + bool isValidUnquotedName(StringRef Name) const override; }; } // 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/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 @@ -426,7 +426,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 +668,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->getLogAlignment(), &F); @@ -700,8 +707,13 @@ } } - // Emit the CurrentFnSym. This is a virtual function to allow targets to - // do their wild and crazy things as required. + // Emit the function descriptor. This is a virtual function to allow targets + // to emit their specific function descriptor. + if (MAI->needsFunctionDescriptors()) + EmitFunctionDescriptor(); + + // Emit the CurrentFnSym. This is a virtual function to allow targets to do + // their wild and crazy things as required. EmitFunctionEntryLabel(); // If the function had address-taken blocks that got deleted, then we have 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,17 @@ UseDotAlignForAlignment = true; AsciiDirective = nullptr; // not supported AscizDirective = nullptr; // not supported + NeedsFunctionDescriptors = true; + HasDotLGloblDirective = true; Data64bitsDirective = "\t.llong\t"; + SupportsQuotedNames = false; +} + +bool MCAsmInfoXCOFF::isValidUnquotedName(StringRef Name) const { + // FIXME: remove this function when we don't use "TOC[TC0]" as symbol name + // any more. + if (Name.equals("TOC[TC0]")) + return true; + + return MCAsmInfo::isValidUnquotedName(Name); } 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/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 EmitFunctionDescriptor() override; + + void EmitEndOfAsmFile(Module &) override; }; } // end anonymous namespace @@ -1648,6 +1655,32 @@ return AsmPrinter::doFinalization(M); } +void PPCAIXAsmPrinter::SetupMachineFunction(MachineFunction &MF) { + // On AIX, we skip the base class version of SetupMachineFunction to + // initialize the special function descriptor symbol and the function + // entry point symbol. + 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()) @@ -1700,6 +1733,46 @@ EmitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); } +void PPCAIXAsmPrinter::EmitFunctionDescriptor() { + 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 function entry point address. + OutStreamer->EmitValue(MCSymbolRefExpr::create(CurrentFnSym, OutContext), + PointerSize); + // Emit TOC base address. + MCSymbol *TOCBaseSym = OutContext.getOrCreateSymbol(StringRef("TOC[TC0]")); + OutStreamer->EmitValue(MCSymbolRefExpr::create(TOCBaseSym, OutContext), + PointerSize); + // Emit a null environment pointer. + OutStreamer->EmitIntValue(0, PointerSize); + + OutStreamer->SwitchSection(Current.first, Current.second); +} + +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[TC0]"); + 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,74 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefixes=CHECK,32BIT %s + +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s | \ +; RUN: FileCheck --check-prefixes=CHECK,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 +; CHECK-NOT: .tc