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 @@ -227,6 +227,7 @@ // GOFF specific sections. MCSection *PPA1Section = nullptr; + MCSection *ADASection = nullptr; // XCOFF specific sections MCSection *TOCBaseSection = nullptr; @@ -430,6 +431,7 @@ // GOFF specific sections. MCSection *getPPA1Section() const { return PPA1Section; } + MCSection *getADASection() const { return ADASection; } // XCOFF specific sections MCSection *getTOCBaseSection() const { return TOCBaseSection; } 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 @@ -542,6 +542,8 @@ PPA1Section = Ctx->getGOFFSection(".ppa1", SectionKind::getMetadata(), TextSection, MCConstantExpr::create(GOFF::SK_PPA1, *Ctx)); + ADASection = + Ctx->getGOFFSection(".ada", SectionKind::getData(), nullptr, nullptr); } void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) { diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt --- a/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt @@ -3,6 +3,7 @@ SystemZMCAsmBackend.cpp SystemZMCAsmInfo.cpp SystemZMCCodeEmitter.cpp + SystemZMCExpr.cpp SystemZMCObjectWriter.cpp SystemZMCTargetDesc.cpp diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h @@ -0,0 +1,66 @@ +//===-- SystemZMCExpr.h - SystemZ specific MC expression classes -*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SystemZ_MCTARGETDESC_SystemZMCEXPR_H +#define LLVM_LIB_TARGET_SystemZ_MCTARGETDESC_SystemZMCEXPR_H + +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCValue.h" + +namespace llvm { + +class SystemZMCExpr : public MCTargetExpr { +public: +// HLASM docs for address constants: +// https://www.ibm.com/docs/en/hla-and-tf/1.6?topic=value-address-constants + enum VariantKind { + VK_SystemZ_None, + VK_SystemZ_RCon, // Address of ADA of symbol. + VK_SystemZ_VCon, // Address of external function symbol. + }; + +private: + const VariantKind Kind; + const MCExpr *Expr; + + explicit SystemZMCExpr(VariantKind Kind, const MCExpr *Expr) + : Kind(Kind), Expr(Expr) {} + +public: + static const SystemZMCExpr *create(VariantKind Kind, const MCExpr *Expr, + MCContext &Ctx); + + /// getOpcode - Get the kind of this expression. + VariantKind getKind() const { return Kind; } + + /// getSubExpr - Get the child of this expression. + const MCExpr *getSubExpr() const { return Expr; } + + StringRef getVariantKindName() const; + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + void visitUsedExpr(MCStreamer &Streamer) const override { + Streamer.visitUsedExpr(*getSubExpr()); + } + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + // There are no TLS SystemZMCExprs at the moment. + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } +}; +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp @@ -0,0 +1,49 @@ +//===-- SystemZMCExpr.cpp - SystemZ specific MC expression classes --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "SystemZMCExpr.h" +#include "llvm/MC/MCContext.h" +using namespace llvm; + +#define DEBUG_TYPE "systemzmcexpr" + +const SystemZMCExpr *SystemZMCExpr::create(VariantKind Kind, const MCExpr *Expr, + MCContext &Ctx) { + return new (Ctx) SystemZMCExpr(Kind, Expr); +} + +StringRef SystemZMCExpr::getVariantKindName() const { + switch (static_cast(getKind())) { + case VK_SystemZ_None: + return "A"; + case VK_SystemZ_RCon: + return "R"; + case VK_SystemZ_VCon: + return "V"; + default: + llvm_unreachable("Invalid kind"); + } +} + +void SystemZMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + OS << getVariantKindName() << '('; + Expr->print(OS, MAI); + OS << ')'; +} + +bool SystemZMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + Res = + MCValue::get(Res.getSymA(), Res.getSymB(), Res.getConstant(), getKind()); + + return true; +} 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 @@ -46,12 +46,56 @@ BASR33 = 7, // b'x111' == BASR r3,r3 }; + // The Associated Data Area (ADA) contains descriptors which help locating + // external symbols. For each symbol and type, the displacement into the ADA + // is stored. + class AssociatedDataAreaTable { + public: + using DisplacementTable = + MapVector, uint32_t>; + + private: + const uint64_t PointerSize; + + /// The mapping of name/slot type pairs to displacements. + DisplacementTable Displacements; + + /// The next available displacement value. Incremented when new entries into + /// the ADA are created. + uint32_t NextDisplacement = 0; + + public: + AssociatedDataAreaTable(uint64_t PointerSize) : PointerSize(PointerSize) {} + + /// @brief Add a function descriptor to the ADA. + /// @param MI Pointer to an ADA_ENTRY instruction. + /// @return The displacement of the descriptor into the ADA. + uint32_t insert(const MachineOperand MO); + + /// @brief Get the displacement into associated data area (ADA) for a name. + /// If no displacement is already associated with the name, assign one and + /// return it. + /// @param Sym The symbol for which the displacement should be returned. + /// @param SlotKind The ADA type. + /// @return The displacement of the descriptor into the ADA. + uint32_t insert(const MCSymbol *Sym, unsigned SlotKind); + + /// Get the table of GOFF displacements. This is 'const' since it should + /// never be modified by anything except the APIs on this class. + const DisplacementTable &getTable() const { return Displacements; } + + uint32_t getNextDisplacement() const { return NextDisplacement; } + }; + + AssociatedDataAreaTable ADATable; + void emitPPA1(MCSymbol *FnEndSym); + void emitADASection(); public: SystemZAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) : AsmPrinter(TM, std::move(Streamer)), CurrentFnPPA1Sym(nullptr), - CurrentFnEPMarkerSym(nullptr) {} + CurrentFnEPMarkerSym(nullptr), ADATable(TM.getPointerSize(0)) {} // Override AsmPrinter. StringRef getPassName() const override { return "SystemZ Assembly Printer"; } 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 @@ -13,6 +13,7 @@ #include "SystemZAsmPrinter.h" #include "MCTargetDesc/SystemZInstPrinter.h" +#include "MCTargetDesc/SystemZMCExpr.h" #include "SystemZConstantPoolValue.h" #include "SystemZMCInstLower.h" #include "TargetInfo/SystemZTargetInfo.h" @@ -143,6 +144,50 @@ .addReg(SystemZMC::GR64Regs[static_cast(CT)])); } +uint32_t SystemZAsmPrinter::AssociatedDataAreaTable::insert(const MCSymbol *Sym, + unsigned SlotKind) { + auto Key = std::make_pair(Sym, SlotKind); + auto It = Displacements.find(Key); + + if (It != Displacements.end()) + return (*It).second; + + // Determine length of descriptor. + uint32_t Length; + switch (SlotKind) { + case SystemZII::MO_ADA_DIRECT_FUNC_DESC: + Length = 2 * PointerSize; + break; + default: + Length = PointerSize; + break; + } + + uint32_t Displacement = NextDisplacement; + Displacements[std::make_pair(Sym, SlotKind)] = NextDisplacement; + NextDisplacement += Length; + + return Displacement; +} + +uint32_t +SystemZAsmPrinter::AssociatedDataAreaTable::insert(const MachineOperand MO) { + MCSymbol *Sym; + if (MO.getType() == MachineOperand::MO_GlobalAddress) { + const GlobalValue *GV = MO.getGlobal(); + Sym = MO.getParent()->getMF()->getTarget().getSymbol(GV); + assert(Sym && "No symbol"); + } else if (MO.getType() == MachineOperand::MO_ExternalSymbol) { + const char *SymName = MO.getSymbolName(); + Sym = MO.getParent()->getMF()->getContext().getOrCreateSymbol(SymName); + assert(Sym && "No symbol"); + } else + llvm_unreachable("Unexpected operand type"); + + unsigned ADAslotType = MO.getTargetFlags(); + return insert(Sym, ADAslotType); +} + void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) { SystemZ_MC::verifyInstructionPredicates(MI->getOpcode(), getSubtargetInfo().getFeatureBits()); @@ -273,6 +318,43 @@ emitCallInformation(CallType::BASR33); return; + case SystemZ::ADA_ENTRY_VALUE: + case SystemZ::ADA_ENTRY: { + const SystemZSubtarget &Subtarget = MF->getSubtarget(); + const SystemZInstrInfo *TII = Subtarget.getInstrInfo(); + uint32_t Disp = ADATable.insert(MI->getOperand(1)); + Register TargetReg = MI->getOperand(0).getReg(); + + Register ADAReg = MI->getOperand(2).getReg(); + Disp += MI->getOperand(3).getImm(); + bool LoadAddr = MI->getOpcode() == SystemZ::ADA_ENTRY; + + unsigned Op0 = LoadAddr ? SystemZ::LA : SystemZ::LG; + unsigned Op = TII->getOpcodeForOffset(Op0, Disp); + + Register IndexReg = 0; + if (!Op) { + if (TargetReg != ADAReg) { + IndexReg = TargetReg; + // Use TargetReg to store displacement. + EmitToStreamer( + *OutStreamer, + MCInstBuilder(SystemZ::LLILF).addReg(TargetReg).addImm(Disp)); + } else + EmitToStreamer( + *OutStreamer, + MCInstBuilder(SystemZ::ALGFI).addReg(TargetReg).addImm(Disp)); + Disp = 0; + Op = Op0; + } + EmitToStreamer(*OutStreamer, MCInstBuilder(Op) + .addReg(TargetReg) + .addReg(IndexReg) + .addImm(Disp) + .addReg(ADAReg)); + + return; + } case SystemZ::CallBRASL: LoweredMI = MCInstBuilder(SystemZ::BRASL) .addReg(SystemZ::R14D) @@ -867,9 +949,81 @@ } void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) { + auto TT = OutContext.getTargetTriple(); + if (TT.isOSzOS()) { + emitADASection(); + } emitAttributes(M); } +void SystemZAsmPrinter::emitADASection() { + OutStreamer->pushSection(); + + const unsigned PointerSize = getDataLayout().getPointerSize(); + OutStreamer->switchSection(getObjFileLowering().getADASection()); + + unsigned EmittedBytes = 0; + for (auto &Entry : ADATable.getTable()) { + const MCSymbol *Sym; + unsigned SlotKind; + std::tie(Sym, SlotKind) = Entry.first; + unsigned Offset = Entry.second; + assert(Offset == EmittedBytes && "Offset not as expected"); +#define EMIT_COMMENT(Str) \ + OutStreamer->AddComment(Twine("Offset ") \ + .concat(utostr(Offset)) \ + .concat(" " Str " ") \ + .concat(Sym->getName())); + switch (SlotKind) { + case SystemZII::MO_ADA_DIRECT_FUNC_DESC: + // Language Environment DLL logic requires function descriptors, for + // imported functions, that are placed in the ADA to be 8 byte aligned. + EMIT_COMMENT("function descriptor of"); + OutStreamer->emitValue( + SystemZMCExpr::create(SystemZMCExpr::VK_SystemZ_RCon, + MCSymbolRefExpr::create(Sym, OutContext), + OutContext), + PointerSize); + OutStreamer->emitValue( + SystemZMCExpr::create(SystemZMCExpr::VK_SystemZ_VCon, + MCSymbolRefExpr::create(Sym, OutContext), + OutContext), + PointerSize); + EmittedBytes += PointerSize * 2; + break; + case SystemZII::MO_ADA_DATA_SYMBOL_ADDR: + EMIT_COMMENT("pointer to data symbol"); + OutStreamer->emitValue( + SystemZMCExpr::create(SystemZMCExpr::VK_SystemZ_None, + MCSymbolRefExpr::create(Sym, OutContext), + OutContext), + PointerSize); + EmittedBytes += PointerSize; + break; + case SystemZII::MO_ADA_INDIRECT_FUNC_DESC: { + MCSymbol *Alias = OutContext.createTempSymbol( + Twine(Sym->getName()).concat("@indirect")); + OutStreamer->emitAssignment(Alias, + MCSymbolRefExpr::create(Sym, OutContext)); + OutStreamer->emitSymbolAttribute(Alias, MCSA_IndirectSymbol); + + EMIT_COMMENT("pointer to function descriptor"); + OutStreamer->emitValue( + SystemZMCExpr::create(SystemZMCExpr::VK_SystemZ_VCon, + MCSymbolRefExpr::create(Alias, OutContext), + OutContext), + PointerSize); + EmittedBytes += PointerSize; + break; + } + default: + llvm_unreachable("Unexpected slot kind"); + } +#undef EMIT_COMMENT + } + OutStreamer->popSection(); +} + void SystemZAsmPrinter::emitFunctionBodyEnd() { if (TM.getTargetTriple().isOSzOS()) { // Emit symbol for the end of function if the z/OS target streamer diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h @@ -1,4 +1,3 @@ - //===-- SystemZISelLowering.h - SystemZ DAG lowering interface --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. @@ -284,6 +283,16 @@ // Operand 1: the bit mask TDC, + // z/OS XPLINK ADA Entry + // Wraps a TargetGlobalAddress that should be loaded from a function's + // AssociatedData Area (ADA). Tha ADA is passed to the function by the + // caller in the XPLink ABI defined register R5. + // Operand 0: the GlobalValue/External Symbol + // Operand 1: the ADA register + // Operand 2: the offset (0 for the first and 8 for the second element in the + // function descriptor) + ADA_ENTRY, + // Strict variants of scalar floating-point comparisons. // Quiet and signaling versions. STRICT_FCMP = ISD::FIRST_TARGET_STRICTFP_OPCODE, diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -1642,8 +1642,15 @@ } } - // FIXME: For XPLINK64, Add in support for handling incoming "ADA" special - // register (R5) + if (Subtarget.isTargetXPLINK64()) { + // Create virual register for handling incoming "ADA" special register (R5) + const TargetRegisterClass *RC = &SystemZ::ADDR64BitRegClass; + Register ADAvReg = MRI.createVirtualRegister(RC); + auto *Regs = static_cast( + Subtarget.getSpecialRegisters()); + MRI.addLiveIn(Regs->getADARegister(), ADAvReg); + FuncInfo->setADAVirtualRegister(ADAvReg); + } return Chain; } @@ -1668,6 +1675,94 @@ return true; } +static SDValue getADAEntry(SelectionDAG &DAG, SDValue Val, SDLoc DL, + unsigned Offset, bool LoadAdr = false) { + MachineFunction &MF = DAG.getMachineFunction(); + SystemZMachineFunctionInfo *MFI = MF.getInfo(); + unsigned ADAvReg = MFI->getADAVirtualRegister(); + EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + + SDValue Reg = DAG.getRegister(ADAvReg, PtrVT); + SDValue Ofs = DAG.getTargetConstant(Offset, DL, PtrVT); + + SDValue Result = DAG.getNode(SystemZISD::ADA_ENTRY, DL, PtrVT, Val, Reg, Ofs); + if (!LoadAdr) + Result = DAG.getLoad( + PtrVT, DL, DAG.getEntryNode(), Result, MachinePointerInfo(), Align(8), + MachineMemOperand::MODereferenceable | MachineMemOperand::MOInvariant); + + return Result; +} + +// ADA access using Global value +// Note: for functions, address of descriptor is returned +static SDValue getADAEntry(SelectionDAG &DAG, const GlobalValue *GV, SDLoc DL, + EVT PtrVT) { + unsigned ADAtype; + bool LoadAddr = false; + const GlobalAlias *GA = dyn_cast(GV); + bool IsFunction = + (isa(GV)) || (GA && isa(GA->getAliaseeObject())); + bool IsInternal = (GV->hasInternalLinkage() || GV->hasPrivateLinkage()); + + if (IsFunction) { + if (IsInternal) { + ADAtype = SystemZII::MO_ADA_DIRECT_FUNC_DESC; + LoadAddr = true; + } else + ADAtype = SystemZII::MO_ADA_INDIRECT_FUNC_DESC; + } else { + ADAtype = SystemZII::MO_ADA_DATA_SYMBOL_ADDR; + } + SDValue Val = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, ADAtype); + + return getADAEntry(DAG, Val, DL, 0, LoadAddr); +} + +static bool getzOSCalleeAndADA(SelectionDAG &DAG, SDValue &Callee, SDValue &ADA, + SDLoc &DL, SDValue &Chain) { + unsigned ADADelta = 0; // ADA offset in desc. + unsigned EPADelta = 8; // EPA offset in desc. + MachineFunction &MF = DAG.getMachineFunction(); + EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + + // XPLink calling convention. + if (auto *G = dyn_cast(Callee)) { + bool IsInternal = (G->getGlobal()->hasInternalLinkage() || + G->getGlobal()->hasPrivateLinkage()); + if (IsInternal) { + SystemZMachineFunctionInfo *MFI = + MF.getInfo(); + unsigned ADAvReg = MFI->getADAVirtualRegister(); + ADA = DAG.getCopyFromReg(Chain, DL, ADAvReg, PtrVT); + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVT); + Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee); + return true; + } else { + SDValue GA = DAG.getTargetGlobalAddress( + G->getGlobal(), DL, PtrVT, 0, SystemZII::MO_ADA_DIRECT_FUNC_DESC); + ADA = getADAEntry(DAG, GA, DL, ADADelta); + Callee = getADAEntry(DAG, GA, DL, EPADelta); + } + } else if (auto *E = dyn_cast(Callee)) { + SDValue ES = DAG.getTargetExternalSymbol( + E->getSymbol(), PtrVT, SystemZII::MO_ADA_DIRECT_FUNC_DESC); + ADA = getADAEntry(DAG, ES, DL, ADADelta); + Callee = getADAEntry(DAG, ES, DL, EPADelta); + } else { + // Function pointer case + ADA = DAG.getNode(ISD::ADD, DL, PtrVT, Callee, + DAG.getConstant(ADADelta, DL, PtrVT)); + ADA = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), ADA, + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + Callee = DAG.getNode(ISD::ADD, DL, PtrVT, Callee, + DAG.getConstant(EPADelta, DL, PtrVT)); + Callee = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Callee, + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + } + return false; +} + SDValue SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { @@ -1814,17 +1909,31 @@ // associated Target* opcodes. Force %r1 to be used for indirect // tail calls. SDValue Glue; - // FIXME: Add support for XPLINK using the ADA register. - if (auto *G = dyn_cast(Callee)) { - Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVT); - Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee); - } else if (auto *E = dyn_cast(Callee)) { - Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT); - Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee); - } else if (IsTailCall) { - Chain = DAG.getCopyToReg(Chain, DL, SystemZ::R1D, Callee, Glue); - Glue = Chain.getValue(1); - Callee = DAG.getRegister(SystemZ::R1D, Callee.getValueType()); + + if (Subtarget.isTargetXPLINK64()) { + SDValue ADA; + bool IsBRASL = getzOSCalleeAndADA(DAG, Callee, ADA, DL, Chain); + if (!IsBRASL) { + unsigned CalleeReg = static_cast(Regs) + ->getAddressOfCalleeRegister(); + Chain = DAG.getCopyToReg(Chain, DL, CalleeReg, Callee, Glue); + Glue = Chain.getValue(1); + Callee = DAG.getRegister(CalleeReg, Callee.getValueType()); + } + RegsToPass.push_back(std::make_pair( + static_cast(Regs)->getADARegister(), ADA)); + } else { + if (auto *G = dyn_cast(Callee)) { + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVT); + Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee); + } else if (auto *E = dyn_cast(Callee)) { + Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT); + Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee); + } else if (IsTailCall) { + Chain = DAG.getCopyToReg(Chain, DL, SystemZ::R1D, Callee, Glue); + Glue = Chain.getValue(1); + Callee = DAG.getRegister(SystemZ::R1D, Callee.getValueType()); + } } // Build a sequence of copy-to-reg nodes, chained and glued together. @@ -3251,12 +3360,15 @@ Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT); Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result); } - } else { + } else if (Subtarget.isTargetELF()) { Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, SystemZII::MO_GOT); Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result); Result = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), Result, MachinePointerInfo::getGOT(DAG.getMachineFunction())); - } + } else if (Subtarget.isTargetzOS()) { + Result = getADAEntry(DAG, GV, DL, PtrVT); + } else + llvm_unreachable("Unexpected Subtarget"); // If there was a non-zero offset that we didn't fold, create an explicit // addition for it. @@ -6042,6 +6154,7 @@ OPCODE(VLER); OPCODE(VSTER); OPCODE(PREFETCH); + OPCODE(ADA_ENTRY); } return nullptr; #undef OPCODE diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h @@ -74,6 +74,17 @@ MO_INDNTPOFF = (2 << 0) }; +// z/OS XPLink specific: classifies the types of +// accesses to the ADA (Associated Data Area). +// These enums contains values that overlap with the above MO_ enums, +// but that's fine since the above enums are used with ELF, +// while these values are used with z/OS. +enum { + MO_ADA_DATA_SYMBOL_ADDR = 1, + MO_ADA_INDIRECT_FUNC_DESC, + MO_ADA_DIRECT_FUNC_DESC, +}; + // Classifies a branch. enum BranchType { // An instruction that branches on the current value of CC. diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td --- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td +++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td @@ -281,6 +281,9 @@ def BASR : CallRR <"basr", 0x0D>; } +// A symbol in the ADA (z/OS only). +def adasym : Operand; + // z/OS XPLINK let Predicates = [IsTargetXPLINK64] in { let isCall = 1, Defs = [R7D, CC], Uses = [FPC] in { @@ -293,6 +296,19 @@ let isCall = 1, Defs = [R3D, CC], Uses = [FPC] in { def CallBASR_STACKEXT : Alias<4, (outs), (ins ADDR64:$R2), []>; } + + let hasNoSchedulingInfo = 1, Defs = [CC] in { + def ADA_ENTRY : Alias<12, (outs GR64:$Reg), (ins adasym:$addr, + ADDR64:$ADA, imm64:$Offset), + [(set i64:$Reg, (z_ada_entry i64:$addr, + i64:$ADA, i64:$Offset))]>; + } + let mayLoad = 1, AddedComplexity = 20, hasNoSchedulingInfo = 1, Defs = [CC] in { + def ADA_ENTRY_VALUE : Alias<12, (outs GR64:$Reg), (ins adasym:$addr, + ADDR64:$ADA, imm64:$Offset), + [(set i64:$Reg, (load (z_ada_entry + iPTR:$addr, iPTR:$ADA, i64:$Offset)))]>; + } } // Regular calls. diff --git a/llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h b/llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h --- a/llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h +++ b/llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h @@ -38,6 +38,8 @@ unsigned RegSaveFrameIndex; int FramePointerSaveIndex; unsigned NumLocalDynamics; + /// z/OS XPLINK ABI: incoming ADA virtual register. + Register VRegADA; public: SystemZMachineFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) @@ -100,6 +102,11 @@ // Count number of local-dynamic TLS symbols used. unsigned getNumLocalDynamicTLSAccesses() const { return NumLocalDynamics; } void incNumLocalDynamicTLSAccesses() { ++NumLocalDynamics; } + + // Get and set the function's incoming special XPLINK ABI defined ADA + // register. + Register getADAVirtualRegister() const { return VRegADA; } + void setADAVirtualRegister(Register Reg) { VRegADA = Reg; } }; } // end namespace llvm diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td b/llvm/lib/Target/SystemZ/SystemZOperators.td --- a/llvm/lib/Target/SystemZ/SystemZOperators.td +++ b/llvm/lib/Target/SystemZ/SystemZOperators.td @@ -127,6 +127,11 @@ [SDTCisVT<0, i32>, SDTCisPtrTy<1>, SDTCisVT<2, i32>]>; +def SDT_ZADAENTRY : SDTypeProfile<1, 3, + [SDTCisPtrTy<0>, + SDTCisPtrTy<1>, + SDTCisPtrTy<2>, + SDTCisVT<3, i64>]>; def SDT_ZTEnd : SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>; def SDT_ZInsertVectorElt : SDTypeProfile<1, 3, @@ -433,6 +438,9 @@ def z_tend : SDNode<"SystemZISD::TEND", SDT_ZTEnd, [SDNPHasChain, SDNPSideEffect]>; +def z_ada_entry : SDNode<"SystemZISD::ADA_ENTRY", + SDT_ZADAENTRY>; + def z_vshl : SDNode<"ISD::SHL", SDT_ZVecBinary>; def z_vsra : SDNode<"ISD::SRA", SDT_ZVecBinary>; def z_vsrl : SDNode<"ISD::SRL", SDT_ZVecBinary>; diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h --- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h +++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h @@ -89,6 +89,8 @@ int getAddressOfCalleeRegister() { return SystemZ::R6D; }; + int getADARegister() { return SystemZ::R5D; } + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const final; const uint32_t *getCallPreservedMask(const MachineFunction &MF, diff --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.h b/llvm/lib/Target/SystemZ/SystemZSubtarget.h --- a/llvm/lib/Target/SystemZ/SystemZSubtarget.h +++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.h @@ -106,6 +106,8 @@ bool GETTER() const { return ATTRIBUTE; } #include "SystemZGenSubtargetInfo.inc" + bool isAddressedViaADA(const GlobalValue *GV) const; + // Return true if GV can be accessed using LARL for reloc model RM // and code model CM. bool isPC32DBLSymbol(const GlobalValue *GV, CodeModel::Model CM) const; diff --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp --- a/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp +++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp @@ -8,6 +8,7 @@ #include "SystemZSubtarget.h" #include "MCTargetDesc/SystemZMCTargetDesc.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/IR/GlobalValue.h" #include "llvm/Target/TargetMachine.h" @@ -77,8 +78,42 @@ return UseSubRegLiveness; } +bool SystemZSubtarget::isAddressedViaADA(const GlobalValue *GV) const { + if (const auto *GO = dyn_cast(GV)) { + // A R/O variable is placed in code section. If the R/O variable has as + // least two byte alignment, then generated code can use relative + // instructions to address the variable. Otherwise, use the ADA to address + // the variable. + if (GO->getAlignment() & 0x1) { + return true; + } + + // getKindForGlobal only works with definitions + if (GO->isDeclaration()) { + return true; + } + + // check AvailableExternallyLinkage here as getKindForGlobal() asserts + if (GO->hasAvailableExternallyLinkage()) { + return true; + } + + SectionKind GOKind = TargetLoweringObjectFile::getKindForGlobal( + GO, TLInfo.getTargetMachine()); + if (!GOKind.isReadOnly()) { + return true; + } + + return false; // R/O variable with multiple of 2 byte alignment + } + return true; +} + bool SystemZSubtarget::isPC32DBLSymbol(const GlobalValue *GV, CodeModel::Model CM) const { + if (isTargetzOS()) + return !isAddressedViaADA(GV); + // PC32DBL accesses require the low bit to be clear. // // FIXME: Explicitly check for functions: the datalayout is currently diff --git a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll --- a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll +++ b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll @@ -66,7 +66,7 @@ ; CHECK: larl 1, @CPI5_0 ; CHECK-NEXT: ld 0, 0(1) ; CHECK-NEXT: ld 2, 8(1) -; CHECK-NEXT: lgdr 3, 0 +; CHECK: lgdr 3, 0 ; CHECK: lghi 1, 1 ; CHECK: lghi 2, 2 ; CHECK: std 0, 2192(4) diff --git a/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll b/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll @@ -0,0 +1,67 @@ +; Test the ADA section in the assembly output for all cases. +; +; RUN: llc < %s -mtriple=s390x-ibm-zos | FileCheck %s + +; CHECK-LABEL: DoIt: +; CHECK: stmg 6, 7, 1840(4) +; CHECK: aghi 4, -224 +; CHECK: lg 1, 0(5) +; CHECK: lg 6, 16(5) +; CHECK: lg 5, 8(5) +; CHECK: stg 1, 2264(4) +; CHECK: basr 7, 6 +; CHECK: bcr 0, 0 +; CHECK: lg 7, 2072(4) +; CHECK: aghi 4, 224 +; CHECK: b 2(7) +define hidden void @DoIt() { +entry: + %F = alloca ptr, align 8 + store ptr @DoFunc, ptr %F, align 8 + %0 = load ptr, ptr %F, align 8 + call void @Caller(ptr noundef %0) + ret void +} +declare void @DoFunc() +declare void @Caller(ptr noundef) + +; CHECK-LABEL: get_i: +; CHECK: stmg 6, 8, 1872(4) +; CHECK: aghi 4, -192 +; CHECK: lg 1, 24(5) +; CHECK: lg 2, 32(5) +; CHECK: lgf 1, 0(1) +; CHECK: lg 6, 48(5) +; CHECK: lg 5, 40(5) +; CHECK: l 8, 0(2) +; CHECK: basr 7, 6 +; CHECK: bcr 0, 0 +; CHECK: ar 3, 8 +; CHECK: lgfr 3, 3 +; CHECK: lmg 7, 8, 2072(4) +; CHECK: aghi 4, 192 +; CHECK: b 2(7) +@i = external global i32, align 4 +@i2 = external global i32, align 4 + +define signext i32 @get_i() { +entry: + %0 = load i32, ptr @i, align 4 + %1 = load i32, ptr @i2, align 4 + %call = call signext i32 @callout(i32 signext %1) + %add = add nsw i32 %0, %call + ret i32 %add +} + +declare signext i32 @callout(i32 signext) + +; CHECK: .section ".ada" +; CHECK: .set @@DoFunc@indirect0, DoFunc +; CHECK: .indirect_symbol @@DoFunc@indirect0 +; CHECK: .quad V(@@DoFunc@indirect0) * Offset 0 pointer to function descriptor DoFunc +; CHECK: .quad R(Caller) * Offset 8 function descriptor of Caller +; CHECK: .quad V(Caller) +; CHECK: .quad A(i2) * Offset 24 pointer to data symbol i2 +; CHECK: .quad A(i) * Offset 32 pointer to data symbol i +; CHECK: .quad R(callout) * Offset 40 function descriptor of callout +; CHECK: .quad V(callout) diff --git a/llvm/test/CodeGen/SystemZ/zos-ada.ll b/llvm/test/CodeGen/SystemZ/zos-ada.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/zos-ada.ll @@ -0,0 +1,31 @@ +; Test the setup of the environment area, or associated data area (ADA) +; +; RUN: llc < %s -mtriple=s390x-ibm-zos -mcpu=z10 | FileCheck %s + +; CHECK-LABEL: caller: +; CHECK: stmg 6, 8, 1872(4) +; CHECK-NEXT: aghi 4, -192 +; CHECK-NEXT: lgr 8, 5 +; CHECK-NEXT: brasl 7, callee_internal@PLT +; CHECK-NEXT: bcr 0, 3 +; CHECK-NEXT: lg 6, 8(8) +; CHECK-NEXT: lg 5, 0(8) +; CHECK-NEXT: lgr 8, 3 +; CHECK-NEXT: basr 7, 6 +; CHECK-NEXT: bcr 0, 0 +; CHECK-NEXT: la 3, 0(3,8) +; CHECK-NEXT: lmg 7, 8, 2072(4) +; CHECK-NEXT: aghi 4, 192 +; CHECK-NEXT: b 2(7) +define i64 @caller() { + %r1 = call i64 () @callee_internal() + %r2 = call i64 () @callee_external() + %r3 = add i64 %r1, %r2 + ret i64 %r3 +} + +define internal i64 @callee_internal() { + ret i64 10 +} + +declare i64 @callee_external() diff --git a/llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll b/llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll --- a/llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll +++ b/llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll @@ -286,13 +286,14 @@ ; Requires the saving of r4 due to variable sized ; object in stack frame. (Eg: VLA) Sets up frame pointer in r8 -; CHECK64: stmg 4, 9, 1856(4) +; CHECK64: stmg 4, 10, 1856(4) ; CHECK64: aghi 4, -192 +; CHECK64: lg 6, 40(5) +; CHECK64: lg 5, 32(5) ; CHECK64: lgr 8, 4 -; TODO Will change to basr with ADA introduction. -; CHECK64: brasl 7, @@ALCAXP -; CHECK64-NEXT: bcr 0, 3 -; CHECK64: lmg 4, 9, 2048(4) +; CHECK64: basr 7, 6 +; CHECK64-NEXT: bcr 0, 0 +; CHECK64: lmg 4, 10, 2048(4) define i64 @func4(i64 %n) { %vla = alloca i64, i64 %n, align 8 %call = call i64 @fun2(i64 %n, ptr nonnull %vla, ptr nonnull %vla) @@ -303,12 +304,11 @@ ; to force use of agfi before stmg. ; CHECK64: lgr 0, 4 ; CHECK64: agfi 4, -1040192 -; CHECK64: stmg 4, 9, 2048(4) +; CHECK64: stmg 4, 10, 2048(4) ; CHECK64: lgr 8, 4 -; TODO Will change to basr with ADA introduction. -; CHECK64: brasl 7, @@ALCAXP -; CHECK64-NEXT: bcr 0, 3 -;; CHECK64: lmg 4, 9, 2048(4) +; CHECK64: basr 7, 6 +; CHECK64-NEXT: bcr 0, 0 +; CHECK64: lmg 4, 10, 2048(4) define i64 @func5(i64 %n) { %vla = alloca i64, i64 %n, align 8 %arr = alloca [130000 x i64], align 8 @@ -369,7 +369,7 @@ ; CHECK64: @BB8_2: ; CHECK64: lgr 3, 0 ; CHECK64: lg 3, 2192(3) -; CHECK64: stmg 4, 11, 2048(4) +; CHECK64: stmg 4, 12, 2048(4) ; CHECK64: lgr 8, 4 define void @large_stack2(i64 %n1, i64 %n2, i64 %n3) { %arr0 = alloca [131072 x i64], align 8