diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/GOFF.h @@ -0,0 +1,31 @@ +//===-- llvm/BinaryFormat/GOFF.h - GOFF definitions --------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This header contains common, non-processor-specific data structures and +// constants for the GOFF file format. +// +// GOFF specifics can be found in MVS Program Management: Advanced Facilities +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_GOFF_H +#define LLVM_BINARYFORMAT_GOFF_H + +namespace llvm { + +namespace GOFF { + +// \brief Subsections of the primary C_CODE section in the object file. +enum SubsectionKind : uint8_t { + SK_PPA1 = 2, +}; + +} // end namespace GOFF + +} // end namespace llvm + +#endif // LLVM_BINARYFORMAT_GOFF_H diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -613,7 +613,8 @@ unsigned Flags, unsigned EntrySize); - MCSectionGOFF *getGOFFSection(StringRef Section, SectionKind Kind); + MCSectionGOFF *getGOFFSection(StringRef Section, SectionKind Kind, + MCSection *Parent, const MCExpr *SubsectionId); MCSectionCOFF *getCOFFSection(StringRef Section, unsigned Characteristics, SectionKind Kind, StringRef COMDATSymName, diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -225,6 +225,9 @@ MCSection *GIATsSection = nullptr; MCSection *GLJMPSection = nullptr; + // GOFF specific sections. + MCSection *PPA1Section = nullptr; + // XCOFF specific sections MCSection *TOCBaseSection = nullptr; MCSection *ReadOnly8Section = nullptr; @@ -423,6 +426,9 @@ MCSection *getGIATsSection() const { return GIATsSection; } MCSection *getGLJMPSection() const { return GLJMPSection; } + // GOFF specific sections. + MCSection *getPPA1Section() const { return PPA1Section; } + // XCOFF specific sections MCSection *getTOCBaseSection() const { return TOCBaseSection; } diff --git a/llvm/include/llvm/MC/MCSectionGOFF.h b/llvm/include/llvm/MC/MCSectionGOFF.h --- a/llvm/include/llvm/MC/MCSectionGOFF.h +++ b/llvm/include/llvm/MC/MCSectionGOFF.h @@ -15,6 +15,7 @@ #ifndef LLVM_MC_MCSECTIONGOFF_H #define LLVM_MC_MCSECTIONGOFF_H +#include "llvm/BinaryFormat/GOFF.h" #include "llvm/MC/MCSection.h" #include "llvm/Support/raw_ostream.h" @@ -24,9 +25,12 @@ class MCSectionGOFF final : public MCSection { private: + MCSection *Parent; + const MCExpr *SubsectionId; + friend class MCContext; - MCSectionGOFF(StringRef Name, SectionKind K) - : MCSection(SV_GOFF, Name, K, nullptr) {} + MCSectionGOFF(StringRef Name, SectionKind K, MCSection *P, const MCExpr *Sub) + : MCSection(SV_GOFF, Name, K, nullptr), Parent(P), SubsectionId(Sub) {} public: void printSwitchToSection(const MCAsmInfo &MAI, const Triple &T, @@ -39,6 +43,9 @@ bool isVirtualSection() const override { return false; } + MCSection *getParent() const { return Parent; } + const MCExpr *getSubsectionId() const { return SubsectionId; } + static bool classof(const MCSection *S) { return S->getVariant() == SV_GOFF; } }; } // end namespace llvm 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 @@ -2593,8 +2593,8 @@ const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { auto *Symbol = TM.getSymbol(GO); if (Kind.isBSS()) - return getContext().getGOFFSection(Symbol->getName(), - SectionKind::getBSS()); + return getContext().getGOFFSection(Symbol->getName(), SectionKind::getBSS(), + nullptr, nullptr); return getContext().getObjectFileInfo()->getTextSection(); } diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/lib/MC/MCContext.cpp @@ -634,11 +634,14 @@ return (I != ELFEntrySizeMap.end()) ? Optional(I->second) : None; } -MCSectionGOFF *MCContext::getGOFFSection(StringRef Section, SectionKind Kind) { +MCSectionGOFF *MCContext::getGOFFSection(StringRef Section, SectionKind Kind, + MCSection *Parent, + const MCExpr *SubsectionId) { // Do the lookup. If we don't have a hit, return a new section. auto &GOFFSection = GOFFUniquingMap[Section.str()]; if (!GOFFSection) - GOFFSection = new (GOFFAllocator.Allocate()) MCSectionGOFF(Section, Kind); + GOFFSection = new (GOFFAllocator.Allocate()) + MCSectionGOFF(Section, Kind, Parent, SubsectionId); return GOFFSection; } diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -523,8 +523,13 @@ } void MCObjectFileInfo::initGOFFMCObjectFileInfo(const Triple &T) { - TextSection = Ctx->getGOFFSection(".text", SectionKind::getText()); - BSSSection = Ctx->getGOFFSection(".bss", SectionKind::getBSS()); + TextSection = + Ctx->getGOFFSection(".text", SectionKind::getText(), nullptr, nullptr); + BSSSection = + Ctx->getGOFFSection(".bss", SectionKind::getBSS(), nullptr, nullptr); + PPA1Section = + Ctx->getGOFFSection(".ppa1", SectionKind::getMetadata(), TextSection, + MCConstantExpr::create(GOFF::SK_PPA1, *Ctx)); } void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h --- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h +++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h @@ -26,6 +26,8 @@ class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter { private: StackMaps SM; + MCSymbol *CurrentFnPPA1Sym; // PPA1 Symbol. + MCSymbol *CurrentFnEPMarkerSym; // Entry Point Marker. SystemZTargetStreamer *getTargetStreamer() { MCTargetStreamer *TS = OutStreamer->getTargetStreamer(); @@ -45,9 +47,12 @@ BASR33 = 7, // b'x111' == BASR r3,r3 }; + void emitPPA1(MCSymbol *FnEndSym); + public: SystemZAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) - : AsmPrinter(TM, std::move(Streamer)), SM(*this) {} + : AsmPrinter(TM, std::move(Streamer)), SM(*this), + CurrentFnPPA1Sym(nullptr), CurrentFnEPMarkerSym(nullptr) {} // Override AsmPrinter. StringRef getPassName() const override { return "SystemZ Assembly Printer"; } @@ -64,6 +69,7 @@ return AsmPrinter::doInitialization(M); } void emitFunctionEntryLabel() override; + void emitFunctionBodyEnd() override; private: void emitCallInformation(CallType CT); diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp --- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp +++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp @@ -819,13 +819,253 @@ emitStackMaps(SM); } +void SystemZAsmPrinter::emitFunctionBodyEnd() { + if (TM.getTargetTriple().isOSzOS()) { + // Emit symbol for the end of function if the z/OS target streamer + // is used. This is needed to calculate the size of the function. + MCSymbol *FnEndSym = createTempSymbol("func_end"); + OutStreamer->emitLabel(FnEndSym); + + OutStreamer->PushSection(); + OutStreamer->SwitchSection(getObjFileLowering().getPPA1Section()); + emitPPA1(FnEndSym); + OutStreamer->PopSection(); + + CurrentFnPPA1Sym = nullptr; + CurrentFnEPMarkerSym = nullptr; + } +} + +static void emitPPA1Flags(std::unique_ptr &OutStreamer, bool VarArg, + bool StackProtector, bool FPRMask, bool VRMask) { + enum class PPA1Flag1 : uint8_t { + DSA64Bit = (0x80 >> 0), + VarArg = (0x80 >> 7), + LLVM_MARK_AS_BITMASK_ENUM(DSA64Bit) + }; + enum class PPA1Flag2 : uint8_t { + ExternalProcedure = (0x80 >> 0), + STACKPROTECTOR = (0x80 >> 3), + LLVM_MARK_AS_BITMASK_ENUM(ExternalProcedure) + }; + enum class PPA1Flag3 : uint8_t { + FPRMask = (0x80 >> 2), + LLVM_MARK_AS_BITMASK_ENUM(FPRMask) + }; + enum class PPA1Flag4 : uint8_t { + EPMOffsetPresent = (0x80 >> 0), + VRMask = (0x80 >> 2), + ProcedureNamePresent = (0x80 >> 7), + LLVM_MARK_AS_BITMASK_ENUM(EPMOffsetPresent) + }; + + // Declare optional section flags that can be modified. + auto Flags1 = PPA1Flag1(0); + auto Flags2 = PPA1Flag2::ExternalProcedure; + auto Flags3 = PPA1Flag3(0); + auto Flags4 = PPA1Flag4::EPMOffsetPresent | PPA1Flag4::ProcedureNamePresent; + + Flags1 |= PPA1Flag1::DSA64Bit; + + if (VarArg) + Flags1 |= PPA1Flag1::VarArg; + + if (StackProtector) + Flags2 |= PPA1Flag2::STACKPROTECTOR; + + // SavedGPRMask, SavedFPRMask, and SavedVRMask are precomputed in. + if (FPRMask) + Flags3 |= PPA1Flag3::FPRMask; // Add emit FPR mask flag. + + if (VRMask) + Flags4 |= PPA1Flag4::VRMask; // Add emit VR mask flag. + + OutStreamer->AddComment("PPA1 Flags 1"); + if ((Flags1 & PPA1Flag1::DSA64Bit) == PPA1Flag1::DSA64Bit) + OutStreamer->AddComment(" Bit 0: 1 = 64-bit DSA"); + else + OutStreamer->AddComment(" Bit 0: 0 = 32-bit DSA"); + if ((Flags1 & PPA1Flag1::VarArg) == PPA1Flag1::VarArg) + OutStreamer->AddComment(" Bit 7: 1 = Vararg function"); + OutStreamer->emitInt8(static_cast(Flags1)); // Flags 1. + + OutStreamer->AddComment("PPA1 Flags 2"); + if ((Flags2 & PPA1Flag2::ExternalProcedure) == PPA1Flag2::ExternalProcedure) + OutStreamer->AddComment(" Bit 0: 1 = External procedure"); + if ((Flags2 & PPA1Flag2::STACKPROTECTOR) == PPA1Flag2::STACKPROTECTOR) + OutStreamer->AddComment(" Bit 3: 1 = STACKPROTECT is enabled"); + else + OutStreamer->AddComment(" Bit 3: 0 = STACKPROTECT is not enabled"); + OutStreamer->emitInt8(static_cast(Flags2)); // Flags 2. + + OutStreamer->AddComment("PPA1 Flags 3"); + if ((Flags3 & PPA1Flag3::FPRMask) == PPA1Flag3::FPRMask) + OutStreamer->AddComment(" Bit 2: 1 = FP Reg Mask is in optional area"); + OutStreamer->emitInt8( + static_cast(Flags3)); // Flags 3 (optional sections). + + OutStreamer->AddComment("PPA1 Flags 4"); + if ((Flags4 & PPA1Flag4::VRMask) == PPA1Flag4::VRMask) + OutStreamer->AddComment(" Bit 2: 1 = Vector Reg Mask is in optional area"); + OutStreamer->emitInt8(static_cast( + Flags4)); // Flags 4 (optional sections, always emit these). +} + +void SystemZAsmPrinter::emitPPA1(MCSymbol *FnEndSym) { + const TargetRegisterInfo *TRI = MF->getRegInfo().getTargetRegisterInfo(); + const SystemZSubtarget &Subtarget = MF->getSubtarget(); + const auto TargetHasVector = Subtarget.hasVector(); + + const SystemZMachineFunctionInfo *ZFI = + MF->getInfo(); + const auto *ZFL = static_cast( + Subtarget.getFrameLowering()); + const MachineFrameInfo &MFFrame = MF->getFrameInfo(); + + // Get saved GPR/FPR/VPR masks. + const std::vector &CSI = MFFrame.getCalleeSavedInfo(); + uint16_t SavedGPRMask = 0; + uint16_t SavedFPRMask = 0; + uint8_t SavedVRMask = 0; + int64_t OffsetFPR = 0; + int64_t OffsetVR = 0; + const int64_t TopOfStack = + MFFrame.getOffsetAdjustment() + MFFrame.getStackSize(); + + // Loop over the spilled registers. The CalleeSavedInfo can't be used because + // it does not contain all spilled registers. + for (unsigned I = ZFI->getSpillGPRRegs().LowGPR, + E = ZFI->getSpillGPRRegs().HighGPR; + I && E && I <= E; ++I) { + unsigned V = TRI->getEncodingValue((Register)I); + assert(V < 16 && "GPR index out of range"); + SavedGPRMask |= 1 << (15 - V); + } + + for (auto &CS : CSI) { + unsigned Reg = CS.getReg(); + unsigned I = TRI->getEncodingValue(Reg); + + if (SystemZ::FP64BitRegClass.contains(Reg)) { + assert(I < 16 && "FPR index out of range"); + SavedFPRMask |= 1 << (15 - I); + int64_t Temp = MFFrame.getObjectOffset(CS.getFrameIdx()); + if (Temp < OffsetFPR) + OffsetFPR = Temp; + } else if (SystemZ::VR128BitRegClass.contains(Reg)) { + assert(I >= 16 && I <= 23 && "VPR index out of range"); + unsigned BitNum = I - 16; + SavedVRMask |= 1 << (7 - BitNum); + int64_t Temp = MFFrame.getObjectOffset(CS.getFrameIdx()); + if (Temp < OffsetVR) + OffsetVR = Temp; + } + } + + // Adjust the offset. + OffsetFPR += (OffsetFPR < 0) ? TopOfStack : 0; + OffsetVR += (OffsetVR < 0) ? TopOfStack : 0; + + // Get alloca register. + uint8_t FrameReg = TRI->getEncodingValue(TRI->getFrameRegister(*MF)); + uint8_t AllocaReg = ZFL->hasFP(*MF) ? FrameReg : 0; + assert(AllocaReg < 16 && "Can't have alloca register larger than 15"); + + // Build FPR save area offset. + uint32_t FrameAndFPROffset = 0; + if (SavedFPRMask) { + uint64_t FPRSaveAreaOffset = OffsetFPR; + assert(FPRSaveAreaOffset < 0x10000000 && "Offset out of range"); + + FrameAndFPROffset = FPRSaveAreaOffset & 0x0FFFFFFF; // Lose top 4 bits. + FrameAndFPROffset |= FrameReg << 28; // Put into top 4 bits. + } + + // Build VR save area offset. + uint32_t FrameAndVROffset = 0; + if (TargetHasVector && SavedVRMask) { + uint64_t VRSaveAreaOffset = OffsetVR; + assert(VRSaveAreaOffset < 0x10000000 && "Offset out of range"); + + FrameAndVROffset = VRSaveAreaOffset & 0x0FFFFFFF; // Lose top 4 bits. + FrameAndVROffset |= FrameReg << 28; // Put into top 4 bits. + } + + // Emit PPA1 section. + OutStreamer->AddComment("PPA1"); + OutStreamer->emitLabel(CurrentFnPPA1Sym); + OutStreamer->AddComment("Version"); + OutStreamer->emitInt8(0x02); // Version. + OutStreamer->AddComment("LE Signature X'CE'"); + OutStreamer->emitInt8(0xCE); // CEL signature. + OutStreamer->AddComment("Saved GPR Mask"); + OutStreamer->emitInt16(SavedGPRMask); + + emitPPA1Flags(OutStreamer, MF->getFunction().isVarArg(), + MFFrame.hasStackProtectorIndex(), SavedFPRMask != 0, + TargetHasVector && SavedVRMask != 0); + + OutStreamer->AddComment("Length/4 of Parms"); + OutStreamer->emitInt16( + static_cast(MFFrame.getMaxCallFrameSize() / 4)); // Parms/4. + OutStreamer->AddComment("Length of Code"); + OutStreamer->emitAbsoluteSymbolDiff(FnEndSym, CurrentFnEPMarkerSym, 4); + + // Emit saved FPR mask and offset to FPR save area (0x20 of flags 3). + if (SavedFPRMask) { + OutStreamer->AddComment("FPR mask"); + OutStreamer->emitInt16(SavedFPRMask); + OutStreamer->AddComment("AR mask"); + OutStreamer->emitInt16(0); // AR Mask, unused currently. + OutStreamer->AddComment("FPR Save Area Locator"); + OutStreamer->AddComment(Twine(" Bit 0-3: Register R") + .concat(utostr(FrameAndFPROffset >> 28)) + .str()); + OutStreamer->AddComment(Twine(" Bit 4-31: Offset ") + .concat(utostr(FrameAndFPROffset & 0x0FFFFFFF)) + .str()); + OutStreamer->emitInt32(FrameAndFPROffset); // Offset to FPR save area with + // register to add value to + // (alloca reg). + } + + // Emit saved VR mask to VR save area. + if (TargetHasVector && SavedVRMask) { + OutStreamer->AddComment("VR mask"); + OutStreamer->emitInt8(SavedVRMask); + OutStreamer->emitInt8(0); // Reserved. + OutStreamer->emitInt16(0); // Also reserved. + OutStreamer->AddComment("VR Save Area Locator"); + OutStreamer->AddComment(Twine(" Bit 0-3: Register R") + .concat(utostr(FrameAndVROffset >> 28)) + .str()); + OutStreamer->AddComment(Twine(" Bit 4-31: Offset ") + .concat(utostr(FrameAndVROffset & 0x0FFFFFFF)) + .str()); + OutStreamer->emitInt32(FrameAndVROffset); + } + + // Emit offset to entry point optional section (0x80 of flags 4). + OutStreamer->emitAbsoluteSymbolDiff(CurrentFnEPMarkerSym, CurrentFnPPA1Sym, + 4); +} + void SystemZAsmPrinter::emitFunctionEntryLabel() { const SystemZSubtarget &Subtarget = static_cast(MF->getSubtarget()); if (Subtarget.getTargetTriple().isOSzOS()) { MCContext &OutContext = OutStreamer->getContext(); - MCSymbol *EPMarkerSym = OutContext.createTempSymbol("CM_", true); + + // Save information for later use. + std::string N(MF->getFunction().hasName() + ? Twine(MF->getFunction().getName()).concat("_").str() + : ""); + + CurrentFnEPMarkerSym = + OutContext.createTempSymbol(Twine("EPM_").concat(N).str(), true); + CurrentFnPPA1Sym = + OutContext.createTempSymbol(Twine("PPA1_").concat(N).str(), true); // EntryPoint Marker const MachineFrameInfo &MFFrame = MF->getFrameInfo(); @@ -844,11 +1084,14 @@ // Emit entry point marker section. OutStreamer->AddComment("XPLINK Routine Layout Entry"); - OutStreamer->emitLabel(EPMarkerSym); + OutStreamer->emitLabel(CurrentFnEPMarkerSym); OutStreamer->AddComment("Eyecatcher 0x00C300C500C500"); OutStreamer->emitIntValueInHex(0x00C300C500C500, 7); // Eyecatcher. OutStreamer->AddComment("Mark Type C'1'"); OutStreamer->emitInt8(0xF1); // Mark Type. + OutStreamer->AddComment("Offset to PPA1"); + OutStreamer->emitAbsoluteSymbolDiff(CurrentFnPPA1Sym, CurrentFnEPMarkerSym, + 4); if (OutStreamer->isVerboseAsm()) { OutStreamer->AddComment("DSA Size 0x" + Twine::utohexstr(DSASize)); OutStreamer->AddComment("Entry Flags"); diff --git a/llvm/test/MC/GOFF/ppa1.ll b/llvm/test/MC/GOFF/ppa1.ll --- a/llvm/test/MC/GOFF/ppa1.ll +++ b/llvm/test/MC/GOFF/ppa1.ll @@ -1,7 +1,7 @@ ; RUN: llc -mtriple s390x-ibm-zos < %s | FileCheck %s ; REQUIRES: systemz-registered-target -; CHECK: @@CM_0: * @void_test +; CHECK: @@EPM_void_test_0: * @void_test ; CHECK: * XPLINK Routine Layout Entry ; CHECK: .long 12779717 * Eyecatcher 0x00C300C500C500 ; CHECK: .short 197 @@ -10,6 +10,24 @@ ; CHECK: .long 128 * DSA Size 0x80 ; CHECK: * Entry Flags ; CHECK: * Bit 2: 0 = Does not use alloca +; CHECK: @@func_end0: +; CHECK: .section ".ppa1" +; CHECK: @@PPA1_void_test_0: * PPA1 +; CHECK: .byte 2 * Version +; CHECK: .byte 206 * LE Signature X'CE' +; CHECK: .short 768 * Saved GPR Mask +; CHECK: .byte 128 * PPA1 Flags 1 +; CHECK: * Bit 0: 1 = 64-bit DSA +; CHECK: .byte 128 * PPA1 Flags 2 +; CHECK: * Bit 0: 1 = External procedure +; CHECK: * Bit 3: 0 = STACKPROTECT is not enabled +; CHECK: .byte 0 * PPA1 Flags 3 +; CHECK: .byte 129 * PPA1 Flags 4 +; CHECK: .short 0 * Length/4 of Parms +; CHECK: .long @@func_end0-@@EPM_void_test_0 * Length of Code +; CHECK: .long @@EPM_void_test_0-@@PPA1_void_test_0 +; CHECK: .section ".text" +; CHECK: * -- End function define void @void_test() { entry: ret void