[llvm] 9df0f66 - [SystemZ][z/OS] This patch adds support for the ADA (associated data area), doing the following:

Yusra Syeda via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 28 07:13:59 PDT 2023


Author: Yusra Syeda
Date: 2023-06-28T10:13:10-04:00
New Revision: 9df0f66af5462e23216eae31aedbd4d2f459cc3d

URL: https://github.com/llvm/llvm-project/commit/9df0f66af5462e23216eae31aedbd4d2f459cc3d
DIFF: https://github.com/llvm/llvm-project/commit/9df0f66af5462e23216eae31aedbd4d2f459cc3d.diff

LOG: [SystemZ][z/OS] This patch adds support for the ADA (associated data area), doing the following:
- Creates the ADA table to handle displacements
- Emits the ADA section in the SystemZAsmPrinter
- Lowers the ADA_ENTRY node into the appropriate load instruction

Differential Revision: https://reviews.llvm.org/D153788

Added: 
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h
    llvm/test/CodeGen/SystemZ/zos-ada-relocations.ll
    llvm/test/CodeGen/SystemZ/zos-ada.ll

Modified: 
    llvm/include/llvm/MC/MCObjectFileInfo.h
    llvm/lib/MC/MCObjectFileInfo.cpp
    llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
    llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
    llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
    llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
    llvm/lib/Target/SystemZ/SystemZISelLowering.h
    llvm/lib/Target/SystemZ/SystemZInstrInfo.h
    llvm/lib/Target/SystemZ/SystemZInstrInfo.td
    llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h
    llvm/lib/Target/SystemZ/SystemZOperators.td
    llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
    llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
    llvm/lib/Target/SystemZ/SystemZSubtarget.h
    llvm/test/CodeGen/SystemZ/call-zos-vararg.ll
    llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h
index 43ab85fbc3274..54f696cb795fb 100644
--- a/llvm/include/llvm/MC/MCObjectFileInfo.h
+++ b/llvm/include/llvm/MC/MCObjectFileInfo.h
@@ -227,6 +227,7 @@ class MCObjectFileInfo {
 
   // GOFF specific sections.
   MCSection *PPA1Section = nullptr;
+  MCSection *ADASection = nullptr;
 
   // XCOFF specific sections
   MCSection *TOCBaseSection = nullptr;
@@ -430,6 +431,7 @@ class MCObjectFileInfo {
 
   // 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
index 9fceaa20ca68c..df2085e28cbc2 100644
--- a/llvm/lib/MC/MCObjectFileInfo.cpp
+++ b/llvm/lib/MC/MCObjectFileInfo.cpp
@@ -542,6 +542,8 @@ void MCObjectFileInfo::initGOFFMCObjectFileInfo(const Triple &T) {
   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
index 055482ee31b05..91aac7dd6603c 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
@@ -3,6 +3,7 @@ add_llvm_component_library(LLVMSystemZDesc
   SystemZMCAsmBackend.cpp
   SystemZMCAsmInfo.cpp
   SystemZMCCodeEmitter.cpp
+  SystemZMCExpr.cpp
   SystemZMCObjectWriter.cpp
   SystemZMCTargetDesc.cpp
 

diff  --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp
new file mode 100644
index 0000000000000..647cf765c6a3e
--- /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<uint32_t>(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/MCTargetDesc/SystemZMCExpr.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h
new file mode 100644
index 0000000000000..f548b34baa424
--- /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/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index f84af452a0159..19e253898aa2e 100644
--- 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 @@ void SystemZAsmPrinter::emitCallInformation(CallType CT) {
                      .addReg(SystemZMC::GR64Regs[static_cast<unsigned>(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 @@ void SystemZAsmPrinter::emitInstruction(const MachineInstr *MI) {
     emitCallInformation(CallType::BASR33);
     return;
 
+  case SystemZ::ADA_ENTRY_VALUE:
+  case SystemZ::ADA_ENTRY: {
+    const SystemZSubtarget &Subtarget = MF->getSubtarget<SystemZSubtarget>();
+    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 @@ bool SystemZAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
 }
 
 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/SystemZAsmPrinter.h b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
index c99fcda6dcc52..c9dbbfd0b4c43 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
@@ -46,12 +46,56 @@ class LLVM_LIBRARY_VISIBILITY SystemZAsmPrinter : public AsmPrinter {
     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<std::pair<const MCSymbol *, unsigned>, 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<MCStreamer> 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/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index c50e9398a8b16..785a08a763eb7 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -1642,8 +1642,15 @@ SDValue SystemZTargetLowering::LowerFormalArguments(
     }
   }
 
-  // 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<SystemZXPLINK64Registers *>(
+        Subtarget.getSpecialRegisters());
+    MRI.addLiveIn(Regs->getADARegister(), ADAvReg);
+    FuncInfo->setADAVirtualRegister(ADAvReg);
+  }
   return Chain;
 }
 
@@ -1668,6 +1675,94 @@ static bool canUseSiblingCall(const CCState &ArgCCInfo,
   return true;
 }
 
+static SDValue getADAEntry(SelectionDAG &DAG, SDValue Val, SDLoc DL,
+                           unsigned Offset, bool LoadAdr = false) {
+  MachineFunction &MF = DAG.getMachineFunction();
+  SystemZMachineFunctionInfo *MFI = MF.getInfo<SystemZMachineFunctionInfo>();
+  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<GlobalAlias>(GV);
+  bool IsFunction =
+      (isa<Function>(GV)) || (GA && isa<Function>(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<GlobalAddressSDNode>(Callee)) {
+    bool IsInternal = (G->getGlobal()->hasInternalLinkage() ||
+                       G->getGlobal()->hasPrivateLinkage());
+    if (IsInternal) {
+      SystemZMachineFunctionInfo *MFI =
+          MF.getInfo<SystemZMachineFunctionInfo>();
+      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<ExternalSymbolSDNode>(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<SDValue> &InVals) const {
@@ -1814,17 +1909,31 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI,
   // 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<GlobalAddressSDNode>(Callee)) {
-    Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVT);
-    Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee);
-  } else if (auto *E = dyn_cast<ExternalSymbolSDNode>(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<SystemZXPLINK64Registers *>(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<SystemZXPLINK64Registers *>(Regs)->getADARegister(), ADA));
+  } else {
+    if (auto *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
+      Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, PtrVT);
+      Callee = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Callee);
+    } else if (auto *E = dyn_cast<ExternalSymbolSDNode>(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 @@ SDValue SystemZTargetLowering::lowerGlobalAddress(GlobalAddressSDNode *Node,
       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 @@ const char *SystemZTargetLowering::getTargetNodeName(unsigned Opcode) const {
     OPCODE(VLER);
     OPCODE(VSTER);
     OPCODE(PREFETCH);
+    OPCODE(ADA_ENTRY);
   }
   return nullptr;
 #undef OPCODE

diff  --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index 7f6e25ccf6f7a..47fa1831c3ee7 100644
--- 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 @@ enum NodeType : unsigned {
   // 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/SystemZInstrInfo.h b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h
index 9ce75db6c177c..bb883ea464d37 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.h
@@ -74,6 +74,17 @@ enum {
   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
index 9d5ad81abf5e3..6a82c36931a84 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -293,6 +293,19 @@ let Predicates = [IsTargetXPLINK64] in {
   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 ADDR64:$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 ADDR64:$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
index 7a0802abb2765..be7012a37a3d3 100644
--- a/llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZMachineFunctionInfo.h
@@ -38,6 +38,8 @@ class SystemZMachineFunctionInfo : public MachineFunctionInfo {
   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 @@ class SystemZMachineFunctionInfo : public MachineFunctionInfo {
   // 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
index b16f8f2a7ff35..6713cac2a7807 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -127,6 +127,11 @@ def SDT_ZTBegin             : SDTypeProfile<1, 2,
                                             [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_tbegin_nofloat    : SDNode<"SystemZISD::TBEGIN_NOFLOAT", SDT_ZTBegin,
 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
index 78abeb720b43b..cbc02c73f1ac7 100644
--- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.h
@@ -89,6 +89,8 @@ class SystemZXPLINK64Registers : public SystemZCallingConventionRegisters {
 
   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.cpp b/llvm/lib/Target/SystemZ/SystemZSubtarget.cpp
index 25b013ba1876b..bbcb1fd92d6e2 100644
--- 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,12 +78,47 @@ bool SystemZSubtarget::enableSubRegLiveness() const {
   return UseSubRegLiveness;
 }
 
+bool SystemZSubtarget::isAddressedViaADA(const GlobalValue *GV) const {
+  if (const auto *GO = dyn_cast<GlobalObject>(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 {
   // PC32DBL accesses require the low bit to be clear.
   //
   // FIXME: Explicitly check for functions: the datalayout is currently
   // missing information about function pointers.
+
+  if (isTargetzOS())
+    return !isAddressedViaADA(GV);
+
   const DataLayout &DL = GV->getParent()->getDataLayout();
   if (GV->getPointerAlignment(DL) == 1 && !GV->getValueType()->isFunctionTy())
     return false;

diff  --git a/llvm/lib/Target/SystemZ/SystemZSubtarget.h b/llvm/lib/Target/SystemZ/SystemZSubtarget.h
index 8f766a4d87838..5fa7c8f194ebf 100644
--- a/llvm/lib/Target/SystemZ/SystemZSubtarget.h
+++ b/llvm/lib/Target/SystemZ/SystemZSubtarget.h
@@ -106,6 +106,8 @@ class SystemZSubtarget : public SystemZGenSubtargetInfo {
   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/test/CodeGen/SystemZ/call-zos-vararg.ll b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll
index ff52e50373e1e..ac157e5fa67f6 100644
--- a/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll
+++ b/llvm/test/CodeGen/SystemZ/call-zos-vararg.ll
@@ -66,7 +66,7 @@ define i64 @call_vararg_both0(i64 %arg0, double %arg1) {
 ; 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
index 0000000000000..e25246917ec09
--- /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)
+ at i = external global i32, align 4
+ at 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 at indirect0, DoFunc
+; CHECK:      .indirect_symbol   @@DoFunc at indirect0
+; CHECK:  .quad V(@@DoFunc at 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
index 0000000000000..18ee9a2255d68
--- /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 at 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
index 6b8c3bf53122d..510b6188270ab 100644
--- a/llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-prologue-epilog.ll
@@ -286,13 +286,14 @@ define void @func3() {
 
 ; 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 @@ define i64 @func4(i64 %n) {
 ; 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 @@ define void @large_stack1(i64 %n1, i64 %n2, i64 %n3) {
 ; 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


        


More information about the llvm-commits mailing list