[llvm] [GOFF] Emit symbols for functions. (PR #144437)

Kai Nacke via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 19 10:38:07 PST 2025


https://github.com/redstar updated https://github.com/llvm/llvm-project/pull/144437

>From 6261752c43e16feb0ac3b42915c8de943cc96601 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Mon, 16 Jun 2025 17:27:16 -0400
Subject: [PATCH 1/7] [GOFF] Emit symbols for functions.

A function entry is mapped to a LD symbol with an offset to the begin of the section.
---
 llvm/include/llvm/MC/MCGOFFStreamer.h      |  7 +--
 llvm/include/llvm/MC/MCSymbolGOFF.h        | 22 ++++++++-
 llvm/lib/MC/CMakeLists.txt                 |  1 +
 llvm/lib/MC/GOFFObjectWriter.cpp           |  1 +
 llvm/lib/MC/MCGOFFStreamer.cpp             | 57 ++++++++++++++++++++++
 llvm/lib/MC/MCSymbolGOFF.cpp               | 39 +++++++++++++++
 llvm/test/CodeGen/SystemZ/zos-section-1.ll | 34 ++++++++-----
 7 files changed, 143 insertions(+), 18 deletions(-)
 create mode 100644 llvm/lib/MC/MCSymbolGOFF.cpp

diff --git a/llvm/include/llvm/MC/MCGOFFStreamer.h b/llvm/include/llvm/MC/MCGOFFStreamer.h
index 8888d9e7bdbb3..6fa6e6481347f 100644
--- a/llvm/include/llvm/MC/MCGOFFStreamer.h
+++ b/llvm/include/llvm/MC/MCGOFFStreamer.h
@@ -28,9 +28,10 @@ class MCGOFFStreamer : public MCObjectStreamer {
 
   GOFFObjectWriter &getWriter();
 
-  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
-    return false;
-  }
+  void emitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override;
+
+  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override;
+
   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
                         Align ByteAlignment) override {}
 };
diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h
index 11d4df4aea634..b58b520cc5c49 100644
--- a/llvm/include/llvm/MC/MCSymbolGOFF.h
+++ b/llvm/include/llvm/MC/MCSymbolGOFF.h
@@ -28,7 +28,10 @@ class MCSymbolGOFF : public MCSymbol {
   GOFF::LDAttr LDAttributes;
 
   enum SymbolFlags : uint16_t {
-    SF_LD = 0x01, // LD attributes are set.
+    SF_LD = 0x01,     // LD attributes are set.
+                      // Leave place for EX attributes.
+    SF_Hidden = 0x04, // Symbol is hidden, aka not exported.
+    SF_Weak = 0x08,   // Symbol is weak.
   };
 
 public:
@@ -39,7 +42,8 @@ class MCSymbolGOFF : public MCSymbol {
     modifyFlags(SF_LD, SF_LD);
     LDAttributes = Attr;
   }
-  GOFF::LDAttr getLDAttributes() const { return LDAttributes; }
+  const GOFF::LDAttr &getLDAttributes() const { return LDAttributes; }
+  GOFF::LDAttr &getLDAttributes() { return LDAttributes; }
   bool hasLDAttributes() const { return getFlags() & SF_LD; }
 
   void setADA(MCSectionGOFF *AssociatedDataArea) {
@@ -47,6 +51,20 @@ class MCSymbolGOFF : public MCSymbol {
     AssociatedDataArea->RequiresNonZeroLength = true;
   }
   MCSectionGOFF *getADA() const { return ADA; }
+
+  bool isExternal() const { return IsExternal; }
+  void setExternal(bool Value) const { IsExternal = Value; }
+
+  void setHidden(bool Value = true) {
+    modifyFlags(Value ? SF_Hidden : 0, SF_Hidden);
+  }
+  bool isHidden() const { return getFlags() & SF_Hidden; }
+  bool isExported() const { return !isHidden(); }
+
+  void setWeak(bool Value = true) { modifyFlags(Value ? SF_Weak : 0, SF_Weak); }
+  bool isWeak() const { return getFlags() & SF_Weak; }
+
+  void initAttributes();
 };
 } // end namespace llvm
 
diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt
index 70c4577aeec0a..1388f130bb806 100644
--- a/llvm/lib/MC/CMakeLists.txt
+++ b/llvm/lib/MC/CMakeLists.txt
@@ -50,6 +50,7 @@ add_llvm_component_library(LLVMMC
   MCSubtargetInfo.cpp
   MCSymbol.cpp
   MCSymbolELF.cpp
+  MCSymbolGOFF.cpp
   MCSymbolXCOFF.cpp
   MCTargetOptions.cpp
   MCTargetOptionsCommandFlags.cpp
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index a3eaaa743039d..b42ba148ef65b 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -328,6 +328,7 @@ void GOFFWriter::defineLabel(const MCSymbolGOFF &Symbol) {
                 Section.getEDAttributes().NameSpace, Symbol.getLDAttributes());
   if (Symbol.getADA())
     LD.ADAEsdId = Symbol.getADA()->getOrdinal();
+  LD.Offset = Asm.getSymbolOffset(Symbol);
   writeSymbol(LD);
 }
 
diff --git a/llvm/lib/MC/MCGOFFStreamer.cpp b/llvm/lib/MC/MCGOFFStreamer.cpp
index ad6397bce70f0..c76e8cd1dc3c8 100644
--- a/llvm/lib/MC/MCGOFFStreamer.cpp
+++ b/llvm/lib/MC/MCGOFFStreamer.cpp
@@ -15,7 +15,9 @@
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDirectives.h"
 #include "llvm/MC/MCGOFFObjectWriter.h"
+#include "llvm/MC/MCSymbolGOFF.h"
 #include "llvm/MC/TargetRegistry.h"
 
 using namespace llvm;
@@ -37,6 +39,61 @@ void MCGOFFStreamer::changeSection(MCSection *Section, uint32_t Subsection) {
   }
 }
 
+void MCGOFFStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) {
+  MCObjectStreamer::emitLabel(Symbol, Loc);
+  static_cast<MCSymbolGOFF *>(Symbol)->initAttributes();
+}
+
+bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym,
+                                         MCSymbolAttr Attribute) {
+  auto *Symbol = static_cast<MCSymbolGOFF *>(Sym);
+  switch (Attribute) {
+  case MCSA_Invalid:
+  case MCSA_Cold:
+  case MCSA_ELF_TypeFunction:
+  case MCSA_ELF_TypeIndFunction:
+  case MCSA_ELF_TypeObject:
+  case MCSA_ELF_TypeTLS:
+  case MCSA_ELF_TypeCommon:
+  case MCSA_ELF_TypeNoType:
+  case MCSA_ELF_TypeGnuUniqueObject:
+  case MCSA_LGlobal:
+  case MCSA_Extern:
+  case MCSA_Exported:
+  case MCSA_IndirectSymbol:
+  case MCSA_Internal:
+  case MCSA_LazyReference:
+  case MCSA_NoDeadStrip:
+  case MCSA_SymbolResolver:
+  case MCSA_AltEntry:
+  case MCSA_PrivateExtern:
+  case MCSA_Protected:
+  case MCSA_Reference:
+  case MCSA_WeakDefinition:
+  case MCSA_WeakDefAutoPrivate:
+  case MCSA_WeakAntiDep:
+  case MCSA_Memtag:
+    return false;
+
+  case MCSA_Global:
+    Symbol->setExternal(true);
+    break;
+  case MCSA_Local:
+    Symbol->setExternal(false);
+    break;
+  case MCSA_Weak:
+  case MCSA_WeakReference:
+    Symbol->setExternal(true);
+    Symbol->setWeak();
+    break;
+  case MCSA_Hidden:
+    Symbol->setHidden(true);
+    break;
+  }
+
+  return true;
+}
+
 MCStreamer *llvm::createGOFFStreamer(MCContext &Context,
                                      std::unique_ptr<MCAsmBackend> &&MAB,
                                      std::unique_ptr<MCObjectWriter> &&OW,
diff --git a/llvm/lib/MC/MCSymbolGOFF.cpp b/llvm/lib/MC/MCSymbolGOFF.cpp
new file mode 100644
index 0000000000000..86de21b11bc0a
--- /dev/null
+++ b/llvm/lib/MC/MCSymbolGOFF.cpp
@@ -0,0 +1,39 @@
+//===- MCSymbolGOFF.cpp - GOFF Symbol Representation ----------------------===//
+//
+// 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 "llvm/MC/MCSymbolGOFF.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+
+void MCSymbolGOFF::initAttributes() {
+  if (hasLDAttributes())
+    return;
+
+  if (isDefined()) {
+    MCSectionGOFF &Section = static_cast<MCSectionGOFF &>(getSection());
+    GOFF::ESDBindingScope BindingScope =
+        isExternal() ? (isExported() ? GOFF::ESD_BSC_ImportExport
+                                     : GOFF::ESD_BSC_Library)
+                     : GOFF::ESD_BSC_Section;
+    GOFF::ESDBindingStrength BindingStrength =
+        isWeak() ? GOFF::ESDBindingStrength::ESD_BST_Weak
+                 : GOFF::ESDBindingStrength::ESD_BST_Strong;
+    if (Section.isED()) {
+      setLDAttributes(GOFF::LDAttr{false, GOFF::ESD_EXE_CODE, BindingStrength,
+                                   GOFF::ESD_LT_XPLink, GOFF::ESD_AMODE_64,
+                                   BindingScope});
+    } else if (Section.isPR()) {
+      // For data symbols, the attributes are already determind in TLOFI.
+      // TODO Does it make sense to it to here?
+    } else
+      llvm_unreachable("Unexpected section type for label");
+  }
+  // TODO Handle external symbol.
+}
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
index b98584df54d5a..6caa8f4d607de 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
@@ -104,26 +104,34 @@ entry:
 ; CHECK-NEXT: 000300 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
 ; CHECK-NEXT: 000310 00 01 20 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00
 
+; ESD record, type LD.
+; The name is me.
+; CHECK-NEXT: 000320 03 00 00 02 [[ME:00 00 00 09]] [[C_CODE64]] 00 00 00 00
+; CHECK-NEXT: 000330 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000340 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
+; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
+; CHECK-NEXT: 000360 00 04 20 00 00 00 00 02 94 85 00 00 00 00 00 00
+
 ; Text record for the code section C_CODE64.
 ; The regular expression matches the lower byte of the length.
-; CHECK-NEXT: 000320 03 11 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000330 00 00 00 00 00 00 00 {{..}} 00 c3 00 c5 00 c5 00 f1
+; CHECK-NEXT: 000370 03 11 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000380 00 00 00 00 00 00 00 {{..}} 00 c3 00 c5 00 c5 00 f1
 
 ; Text record for the section .&ppa2.
-; CHECK:      0003c0 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0003d0 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      000410 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000420 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; Text record for the ADA section test#S.
-; CHECK:      000410 03 10 00 00 [[TESTS]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000420 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      000460 03 10 00 00 [[TESTS]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000470 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; Text record for the section B_IDRL.
-; CHECK:      000460 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000470 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      0004b0 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0004c0 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; End record.
-; CHECK:      0004b0 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0004c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0004d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0004e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0004f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK:      000500 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000540 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

>From 194ba3a6efd669d3737efa033e767a45bd6f756e Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Wed, 29 Oct 2025 13:19:28 -0400
Subject: [PATCH 2/7] Add HLASM output and external references

Adds HLASM output and tests for it, per reviewer comment.
Also adds external references, because it fits very well
into the implementation.
---
 llvm/include/llvm/MC/MCDirectives.h           |   4 +
 llvm/include/llvm/MC/MCGOFFAttributes.h       |   8 +
 llvm/include/llvm/MC/MCGOFFStreamer.h         |   3 +
 llvm/include/llvm/MC/MCSymbolGOFF.h           |  28 +++-
 llvm/lib/MC/GOFFObjectWriter.cpp              |  21 +++
 llvm/lib/MC/MCAsmStreamer.cpp                 |   3 +
 llvm/lib/MC/MCELFStreamer.cpp                 |   2 +
 llvm/lib/MC/MCGOFFStreamer.cpp                |  19 +++
 llvm/lib/MC/MCMachOStreamer.cpp               |   2 +
 llvm/lib/MC/MCSymbolGOFF.cpp                  |  28 ++--
 .../MCTargetDesc/SystemZHLASMAsmStreamer.cpp  | 158 +++++++++++++++++-
 .../MCTargetDesc/SystemZHLASMAsmStreamer.h    |   7 +-
 .../MCTargetDesc/SystemZTargetStreamer.cpp    |  17 +-
 .../MCTargetDesc/SystemZTargetStreamer.h      |   7 +-
 llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp |  23 +++
 llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll  |   2 +-
 llvm/test/CodeGen/SystemZ/zos-section-1.ll    |  52 ++++--
 llvm/test/CodeGen/SystemZ/zos-section-2.ll    |  34 ++--
 llvm/test/CodeGen/SystemZ/zos-symbol-1.ll     |  37 ++++
 19 files changed, 395 insertions(+), 60 deletions(-)
 create mode 100644 llvm/test/CodeGen/SystemZ/zos-symbol-1.ll

diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h
index f8b7e05b3719b..f69144367c8e7 100644
--- a/llvm/include/llvm/MC/MCDirectives.h
+++ b/llvm/include/llvm/MC/MCDirectives.h
@@ -48,6 +48,10 @@ enum MCSymbolAttr {
   MCSA_WeakDefAutoPrivate,      ///< .weak_def_can_be_hidden (MachO)
   MCSA_WeakAntiDep,             ///< .weak_anti_dep (COFF)
   MCSA_Memtag,                  ///< .memtag (ELF)
+
+  // Attributes specific for HLASM.
+  MCSA_Code,                    ///< symbol is code (GOFF)
+  MCSA_Data,                    ///< symbol is data (GOFF)
 };
 
 enum MCDataRegionType {
diff --git a/llvm/include/llvm/MC/MCGOFFAttributes.h b/llvm/include/llvm/MC/MCGOFFAttributes.h
index c996f0cae2c69..1631d59462935 100644
--- a/llvm/include/llvm/MC/MCGOFFAttributes.h
+++ b/llvm/include/llvm/MC/MCGOFFAttributes.h
@@ -80,6 +80,14 @@ struct PRAttr {
   uint32_t SortKey = 0;
 };
 
+// Attributes for ER symbols.
+struct ERAttr {
+  GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
+  GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
+  GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
+  GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
+};
+
 // Predefined GOFF class names.
 constexpr StringLiteral CLASS_CODE = "C_CODE64";
 constexpr StringLiteral CLASS_WSA = "C_WSA64";
diff --git a/llvm/include/llvm/MC/MCGOFFStreamer.h b/llvm/include/llvm/MC/MCGOFFStreamer.h
index 6fa6e6481347f..3d13ddf216aa3 100644
--- a/llvm/include/llvm/MC/MCGOFFStreamer.h
+++ b/llvm/include/llvm/MC/MCGOFFStreamer.h
@@ -14,6 +14,7 @@
 
 namespace llvm {
 class GOFFObjectWriter;
+class MCSymbolGOFF;
 
 class MCGOFFStreamer : public MCObjectStreamer {
 
@@ -34,6 +35,8 @@ class MCGOFFStreamer : public MCObjectStreamer {
 
   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
                         Align ByteAlignment) override {}
+
+  void emitExterns();
 };
 
 } // end namespace llvm
diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h
index b58b520cc5c49..58305a00ec235 100644
--- a/llvm/include/llvm/MC/MCSymbolGOFF.h
+++ b/llvm/include/llvm/MC/MCSymbolGOFF.h
@@ -23,13 +23,21 @@ namespace llvm {
 
 class MCSymbolGOFF : public MCSymbol {
   // Associated data area of the section. Needs to be emitted first.
-  MCSectionGOFF *ADA;
+  MCSectionGOFF *ADA = nullptr;
+
+  // Owner of the symbol if symbol is an external reference. External references
+  // need a section, too, but adding them to a section would make the symbol
+  // defined.
+  MCSectionGOFF *Owner = nullptr;
 
   GOFF::LDAttr LDAttributes;
+  GOFF::ERAttr ERAttributes;
+
+  GOFF::ESDExecutable CodeData = GOFF::ESDExecutable::ESD_EXE_Unspecified;
 
   enum SymbolFlags : uint16_t {
     SF_LD = 0x01,     // LD attributes are set.
-                      // Leave place for EX attributes.
+    SF_ER = 0x02,     // ER attributes are set.
     SF_Hidden = 0x04, // Symbol is hidden, aka not exported.
     SF_Weak = 0x08,   // Symbol is weak.
   };
@@ -46,12 +54,25 @@ class MCSymbolGOFF : public MCSymbol {
   GOFF::LDAttr &getLDAttributes() { return LDAttributes; }
   bool hasLDAttributes() const { return getFlags() & SF_LD; }
 
+  void setERAttributes(GOFF::ERAttr Attr) {
+    modifyFlags(SF_ER, SF_ER);
+    ERAttributes = Attr;
+  }
+  const GOFF::ERAttr &getERAttributes() const { return ERAttributes; }
+  GOFF::ERAttr &getERAttributes() { return ERAttributes; }
+  bool hasERAttributes() const { return getFlags() & SF_ER; }
+
   void setADA(MCSectionGOFF *AssociatedDataArea) {
     ADA = AssociatedDataArea;
     AssociatedDataArea->RequiresNonZeroLength = true;
   }
   MCSectionGOFF *getADA() const { return ADA; }
 
+  void setOwner(MCSectionGOFF *Owner) {
+    this->Owner = Owner;
+  }
+  MCSectionGOFF *getOwner() const { return Owner; }
+
   bool isExternal() const { return IsExternal; }
   void setExternal(bool Value) const { IsExternal = Value; }
 
@@ -64,6 +85,9 @@ class MCSymbolGOFF : public MCSymbol {
   void setWeak(bool Value = true) { modifyFlags(Value ? SF_Weak : 0, SF_Weak); }
   bool isWeak() const { return getFlags() & SF_Weak; }
 
+  void setCodeData(GOFF::ESDExecutable Value) { CodeData = Value; }
+  GOFF::ESDExecutable getCodeData() const { return CodeData; }
+
   void initAttributes();
 };
 } // end namespace llvm
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index b42ba148ef65b..51f95813eabc8 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -266,6 +266,17 @@ class GOFFSymbol {
     BehavAttrs.setBindingScope(Attr.BindingScope);
     BehavAttrs.setAlignment(EDAttr.Alignment);
   }
+
+  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
+             const GOFF::ERAttr &Attr)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
+        SymbolType(GOFF::ESD_ST_ExternalReference),
+        NameSpace(GOFF::ESD_NS_NormalName) {
+    BehavAttrs.setExecutable(Attr.Executable);
+    BehavAttrs.setBindingStrength(Attr.BindingStrength);
+    BehavAttrs.setLinkageType(Attr.Linkage);
+    BehavAttrs.setBindingScope(Attr.BindingScope);
+  }
 };
 
 class GOFFWriter {
@@ -279,6 +290,7 @@ class GOFFWriter {
 
   void defineSectionSymbols(const MCSectionGOFF &Section);
   void defineLabel(const MCSymbolGOFF &Symbol);
+  void defineExtern(const MCSymbolGOFF &Symbol);
   void defineSymbols();
 
 public:
@@ -332,6 +344,12 @@ void GOFFWriter::defineLabel(const MCSymbolGOFF &Symbol) {
   writeSymbol(LD);
 }
 
+void GOFFWriter::defineExtern(const MCSymbolGOFF &Symbol) {
+  GOFFSymbol ER(Symbol.getName(), Symbol.getIndex(),
+                Symbol.getOwner()->getOrdinal(), Symbol.getERAttributes());
+  writeSymbol(ER);
+}
+
 void GOFFWriter::defineSymbols() {
   unsigned Ordinal = 0;
   // Process all sections.
@@ -349,6 +367,9 @@ void GOFFWriter::defineSymbols() {
     if (Symbol.hasLDAttributes()) {
       Symbol.setIndex(++Ordinal);
       defineLabel(Symbol);
+    } else if (Symbol.hasERAttributes()) {
+      Symbol.setIndex(++Ordinal);
+      defineExtern(Symbol);
     }
   }
 }
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 885fa55b65d50..60e3757f60fed 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -774,6 +774,9 @@ bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol,
     // Assemblers currently do not support a .cold directive.
   case MCSA_Exported:
     // Non-AIX assemblers currently do not support exported visibility.
+  case MCSA_Code:
+  case MCSA_Data:
+    // Only for HLASM.
     return false;
   case MCSA_Memtag:
     OS << "\t.memtag\t";
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index 1bc1b92610871..4d4148c506b4f 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -151,6 +151,8 @@ bool MCELFStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) {
   case MCSA_IndirectSymbol:
   case MCSA_Exported:
   case MCSA_WeakAntiDep:
+  case MCSA_Code:
+  case MCSA_Data:
     return false;
 
   case MCSA_NoDeadStrip:
diff --git a/llvm/lib/MC/MCGOFFStreamer.cpp b/llvm/lib/MC/MCGOFFStreamer.cpp
index c76e8cd1dc3c8..62a9117fe895f 100644
--- a/llvm/lib/MC/MCGOFFStreamer.cpp
+++ b/llvm/lib/MC/MCGOFFStreamer.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/MC/MCGOFFStreamer.h"
+#include "llvm/BinaryFormat/GOFF.h"
 #include "llvm/MC/MCAsmBackend.h"
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCCodeEmitter.h"
@@ -75,6 +76,12 @@ bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Memtag:
     return false;
 
+  case MCSA_Code:
+    Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_CODE);
+    break;
+  case MCSA_Data:
+    Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA);
+    break;
   case MCSA_Global:
     Symbol->setExternal(true);
     break;
@@ -94,6 +101,18 @@ bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym,
   return true;
 }
 
+void MCGOFFStreamer::emitExterns() {
+  for (auto &Symbol : getAssembler().symbols()) {
+    if (Symbol.isTemporary())
+      continue;
+    if (Symbol.isRegistered()) {
+      auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol));
+      Sym.setOwner(static_cast<MCSectionGOFF *>(getCurrentSection().first));
+      Sym.initAttributes();
+    }
+  }
+}
+
 MCStreamer *llvm::createGOFFStreamer(MCContext &Context,
                                      std::unique_ptr<MCAsmBackend> &&MAB,
                                      std::unique_ptr<MCObjectWriter> &&OW,
diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp
index 2b7a248e6d109..da148f81012f7 100644
--- a/llvm/lib/MC/MCMachOStreamer.cpp
+++ b/llvm/lib/MC/MCMachOStreamer.cpp
@@ -299,6 +299,8 @@ bool MCMachOStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Exported:
   case MCSA_Memtag:
   case MCSA_WeakAntiDep:
+  case MCSA_Code:
+  case MCSA_Data:
     return false;
 
   case MCSA_Global:
diff --git a/llvm/lib/MC/MCSymbolGOFF.cpp b/llvm/lib/MC/MCSymbolGOFF.cpp
index 86de21b11bc0a..5e248dea033a4 100644
--- a/llvm/lib/MC/MCSymbolGOFF.cpp
+++ b/llvm/lib/MC/MCSymbolGOFF.cpp
@@ -13,20 +13,26 @@
 using namespace llvm;
 
 void MCSymbolGOFF::initAttributes() {
-  if (hasLDAttributes())
+  // Temporary labels are not emitted into the object file.
+  if (isTemporary())
     return;
 
+  // Do not initialize the attributes multiple times.
+  if (hasLDAttributes() || hasERAttributes())
+    return;
+
+  GOFF::ESDBindingScope BindingScope =
+      isExternal()
+          ? (isExported() ? GOFF::ESD_BSC_ImportExport : GOFF::ESD_BSC_Library)
+          : GOFF::ESD_BSC_Section;
+  GOFF::ESDBindingStrength BindingStrength =
+      isWeak() ? GOFF::ESDBindingStrength::ESD_BST_Weak
+               : GOFF::ESDBindingStrength::ESD_BST_Strong;
+
   if (isDefined()) {
     MCSectionGOFF &Section = static_cast<MCSectionGOFF &>(getSection());
-    GOFF::ESDBindingScope BindingScope =
-        isExternal() ? (isExported() ? GOFF::ESD_BSC_ImportExport
-                                     : GOFF::ESD_BSC_Library)
-                     : GOFF::ESD_BSC_Section;
-    GOFF::ESDBindingStrength BindingStrength =
-        isWeak() ? GOFF::ESDBindingStrength::ESD_BST_Weak
-                 : GOFF::ESDBindingStrength::ESD_BST_Strong;
     if (Section.isED()) {
-      setLDAttributes(GOFF::LDAttr{false, GOFF::ESD_EXE_CODE, BindingStrength,
+      setLDAttributes(GOFF::LDAttr{false, CodeData, BindingStrength,
                                    GOFF::ESD_LT_XPLink, GOFF::ESD_AMODE_64,
                                    BindingScope});
     } else if (Section.isPR()) {
@@ -34,6 +40,8 @@ void MCSymbolGOFF::initAttributes() {
       // TODO Does it make sense to it to here?
     } else
       llvm_unreachable("Unexpected section type for label");
+  } else {
+    setERAttributes(GOFF::ERAttr{CodeData, BindingStrength,
+                                 GOFF::ESD_LT_XPLink, BindingScope});
   }
-  // TODO Handle external symbol.
 }
diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
index 72bb37265259e..8553f8bc14f56 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
@@ -8,12 +8,19 @@
 
 #include "SystemZHLASMAsmStreamer.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/MC/MCGOFFAttributes.h"
+#include "llvm/MC/MCSymbolGOFF.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Signals.h"
 #include <sstream>
 
 using namespace llvm;
 
+void SystemZHLASMAsmStreamer::visitUsedSymbol(const MCSymbol &Sym) {
+  Assembler->registerSymbol(Sym);
+}
+
 void SystemZHLASMAsmStreamer::EmitEOL() {
   // Comments are emitted on a new line before the instruction.
   if (IsVerboseAsm)
@@ -183,17 +190,132 @@ void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst,
   EmitEOL();
 }
 
+static void emitXATTR(raw_ostream &OS, StringRef Name,
+                      GOFF::ESDLinkageType Linkage,
+                      GOFF::ESDExecutable Executable,
+                      GOFF::ESDBindingScope BindingScope) {
+  llvm::ListSeparator Sep(",");
+  OS << Name << " XATTR ";
+  OS << Sep << "LINKAGE(" << (Linkage == GOFF::ESD_LT_OS ? "OS" : "XPLINK")
+     << ")";
+  if (Executable != GOFF::ESD_EXE_Unspecified)
+    OS << Sep << "REFERENCE("
+       << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA") << ")";
+  if (BindingScope != GOFF::ESD_BSC_Unspecified) {
+    OS << Sep << "SCOPE(";
+    switch (BindingScope) {
+    case GOFF::ESD_BSC_Section:
+      OS << "SECTION";
+      break;
+    case GOFF::ESD_BSC_Module:
+      OS << "MODULE";
+      break;
+    case GOFF::ESD_BSC_Library:
+      OS << "LIBRARY";
+      break;
+    case GOFF::ESD_BSC_ImportExport:
+      OS << "EXPORT";
+      break;
+    default:
+      break;
+    }
+    OS << ')';
+  }
+  OS << '\n';
+}
+
+static bool sameNameAsCSECT(MCSymbolGOFF *Sym) {
+  if (Sym->hasLDAttributes() && Sym->isInSection()) {
+    MCSectionGOFF &ED = static_cast<MCSectionGOFF &>(Sym->getSection());
+    return Sym->getName() == ED.getParent()->getName();
+  }
+  return false;
+}
+
 void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) {
+  MCSymbolGOFF *Sym = static_cast<MCSymbolGOFF *>(Symbol);
+
+  MCStreamer::emitLabel(Sym, Loc);
+  Sym->initAttributes();
+
+  // Emit ENTRY statement only if not implied by CSECT.
+  bool EmitEntry = !sameNameAsCSECT(Sym);
+
+  if (!Sym->isTemporary() && Sym->hasLDAttributes()) {
+    GOFF::LDAttr &LD = Sym->getLDAttributes();
+    if (EmitEntry) {
+      OS << " ENTRY " << Sym->getName();
+      EmitEOL();
+    }
 
-  MCStreamer::emitLabel(Symbol, Loc);
+    emitXATTR(OS, Sym->getName(), LD.Linkage, LD.Executable, LD.BindingScope);
+    EmitEOL();
+  }
 
-  Symbol->print(OS, MAI);
   // TODO Need to adjust this based on Label type
-  OS << " DS 0H";
-  // TODO Update LabelSuffix in SystemZMCAsmInfoGOFF once tests have been
-  // moved to HLASM syntax.
-  // OS << MAI->getLabelSuffix();
-  EmitEOL();
+  if (EmitEntry) {
+    OS << Sym->getName() << " DS 0H";
+    // TODO Update LabelSuffix in SystemZMCAsmInfoGOFF once tests have been
+    // moved to HLASM syntax.
+    // OS << MAI->getLabelSuffix();
+    EmitEOL();
+  }
+}
+
+bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
+                                                  MCSymbolAttr Attribute) {
+  auto *Symbol = static_cast<MCSymbolGOFF *>(Sym);
+  switch (Attribute) {
+  case MCSA_Invalid:
+  case MCSA_Cold:
+  case MCSA_ELF_TypeFunction:
+  case MCSA_ELF_TypeIndFunction:
+  case MCSA_ELF_TypeObject:
+  case MCSA_ELF_TypeTLS:
+  case MCSA_ELF_TypeCommon:
+  case MCSA_ELF_TypeNoType:
+  case MCSA_ELF_TypeGnuUniqueObject:
+  case MCSA_LGlobal:
+  case MCSA_Extern:
+  case MCSA_Exported:
+  case MCSA_IndirectSymbol:
+  case MCSA_Internal:
+  case MCSA_LazyReference:
+  case MCSA_NoDeadStrip:
+  case MCSA_SymbolResolver:
+  case MCSA_AltEntry:
+  case MCSA_PrivateExtern:
+  case MCSA_Protected:
+  case MCSA_Reference:
+  case MCSA_WeakDefinition:
+  case MCSA_WeakDefAutoPrivate:
+  case MCSA_WeakAntiDep:
+  case MCSA_Memtag:
+    return false;
+
+  case MCSA_Code:
+    Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_CODE);
+    break;
+  case MCSA_Data:
+    Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA);
+    break;
+  case MCSA_Global:
+    Symbol->setExternal(true);
+    break;
+  case MCSA_Local:
+    Symbol->setExternal(false);
+    break;
+  case MCSA_Weak:
+  case MCSA_WeakReference:
+    Symbol->setExternal(true);
+    Symbol->setWeak();
+    break;
+  case MCSA_Hidden:
+    Symbol->setHidden(true);
+    break;
+  }
+
+  return true;
 }
 
 void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) {
@@ -276,6 +398,7 @@ void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size,
   assert(getCurrentSectionOnly() &&
          "Cannot emit contents before setting section!");
 
+  MCStreamer::emitValueImpl(Value, Size, Loc);
   OS << " DC ";
   emitHLASMValueImpl(Value, Size, true);
   EmitEOL();
@@ -285,3 +408,24 @@ void SystemZHLASMAsmStreamer::emitEnd() {
   OS << " END";
   EmitEOL();
 }
+
+void SystemZHLASMAsmStreamer::emitExterns() {
+  for (auto &Symbol : getAssembler().symbols()) {
+    if (Symbol.isTemporary())
+      continue;
+    if (Symbol.isRegistered()) {
+      auto &Sym = static_cast<MCSymbolGOFF &>(const_cast<MCSymbol &>(Symbol));
+      Sym.setOwner(static_cast<MCSectionGOFF *>(getCurrentSection().first));
+      Sym.initAttributes();
+      GOFF::ERAttr &ER = Sym.getERAttributes();
+      OS << " "
+         << (ER.BindingStrength == GOFF::ESDBindingStrength::ESD_BST_Weak
+                 ? "WXTRN"
+                 : "EXTRN")
+         << " " << Sym.getName();
+      EmitEOL();
+      emitXATTR(OS, Sym.getName(), ER.Linkage, ER.Executable, ER.BindingScope);
+      EmitEOL();
+    }
+  }
+}
diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h
index 93b1ac4d901aa..81cfdad472244 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h
@@ -28,6 +28,7 @@
 #include "llvm/Support/FormattedStream.h"
 
 namespace llvm {
+class MCSymbolGOFF;
 
 class SystemZHLASMAsmStreamer final : public MCStreamer {
   constexpr static size_t InstLimit = 80;
@@ -100,14 +101,13 @@ class SystemZHLASMAsmStreamer final : public MCStreamer {
 
   /// @name MCStreamer Interface
   /// @{
+  void visitUsedSymbol(const MCSymbol &Sym) override;
 
   void changeSection(MCSection *Section, uint32_t Subsection) override;
 
   void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override;
   void emitLabel(MCSymbol *Symbol, SMLoc Loc) override;
-  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
-    return false;
-  }
+  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override;
 
   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
                         Align ByteAlignment) override {}
@@ -123,6 +123,7 @@ class SystemZHLASMAsmStreamer final : public MCStreamer {
   /// @}
 
   void emitEnd();
+  void emitExterns();
 };
 } // namespace llvm
 
diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp
index 1a3e373f25374..d25c77e21d9f8 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp
@@ -15,6 +15,7 @@
 #include "SystemZTargetStreamer.h"
 #include "SystemZHLASMAsmStreamer.h"
 #include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCGOFFStreamer.h"
 #include "llvm/MC/MCObjectFileInfo.h"
 
 using namespace llvm;
@@ -38,12 +39,12 @@ SystemZHLASMAsmStreamer &SystemZTargetHLASMStreamer::getHLASMStreamer() {
   return static_cast<SystemZHLASMAsmStreamer &>(getStreamer());
 }
 
-void SystemZTargetHLASMStreamer::emitExtern(StringRef Sym) {
-  getStreamer().emitRawText(Twine(" EXTRN ") + Twine(Sym));
-}
-
 void SystemZTargetHLASMStreamer::emitEnd() { getHLASMStreamer().emitEnd(); }
 
+void SystemZTargetHLASMStreamer::emitExterns() {
+  getHLASMStreamer().emitExterns();
+}
+
 // HLASM statements can only perform a single operation at a time
 const MCExpr *SystemZTargetHLASMStreamer::createWordDiffExpr(
     MCContext &Ctx, const MCSymbol *Hi, const MCSymbol *Lo) {
@@ -66,3 +67,11 @@ const MCExpr *SystemZTargetGOFFStreamer::createWordDiffExpr(
                               MCSymbolRefExpr::create(Lo, Ctx), Ctx),
       MCConstantExpr::create(1, Ctx), Ctx);
 }
+
+MCGOFFStreamer &SystemZTargetGOFFStreamer::getGOFFStreamer() {
+  return static_cast<MCGOFFStreamer &>(getStreamer());
+}
+
+void SystemZTargetGOFFStreamer::emitExterns() {
+  getGOFFStreamer().emitExterns();
+}
diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h
index 3fc09bc8c683a..31433cc9739c6 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h
@@ -20,6 +20,7 @@
 #include <utility>
 
 namespace llvm {
+class MCGOFFStreamer;
 class SystemZHLASMAsmStreamer;
 
 class SystemZTargetStreamer : public MCTargetStreamer {
@@ -57,8 +58,8 @@ class SystemZTargetStreamer : public MCTargetStreamer {
 
   virtual void emitMachine(StringRef CPUOrCommand) {};
 
-  virtual void emitExtern(StringRef Str) {};
   virtual void emitEnd() {};
+  virtual void emitExterns() {};
 
   virtual const MCExpr *createWordDiffExpr(MCContext &Ctx, const MCSymbol *Hi,
                                            const MCSymbol *Lo) {
@@ -69,6 +70,8 @@ class SystemZTargetStreamer : public MCTargetStreamer {
 class SystemZTargetGOFFStreamer : public SystemZTargetStreamer {
 public:
   SystemZTargetGOFFStreamer(MCStreamer &S) : SystemZTargetStreamer(S) {}
+  MCGOFFStreamer &getGOFFStreamer();
+  void emitExterns() override;
   const MCExpr *createWordDiffExpr(MCContext &Ctx, const MCSymbol *Hi,
                                    const MCSymbol *Lo) override;
 };
@@ -80,8 +83,8 @@ class SystemZTargetHLASMStreamer : public SystemZTargetStreamer {
   SystemZTargetHLASMStreamer(MCStreamer &S, formatted_raw_ostream &OS)
       : SystemZTargetStreamer(S), OS(OS) {}
   SystemZHLASMAsmStreamer &getHLASMStreamer();
-  void emitExtern(StringRef Sym) override;
   void emitEnd() override;
+  void emitExterns() override;
   const MCExpr *createWordDiffExpr(MCContext &Ctx, const MCSymbol *Hi,
                                    const MCSymbol *Lo) override;
 };
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index e31d7c6a86476..87ff2f0353161 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -26,6 +26,7 @@
 #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/IR/Module.h"
+#include "llvm/MC/MCDirectives.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInstBuilder.h"
 #include "llvm/MC/MCSectionELF.h"
@@ -1112,6 +1113,22 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) {
   if (TT.isOSzOS()) {
     emitADASection();
     emitIDRLSection(M);
+    // Emit EXTRN declarations.
+    OutStreamer->pushSection();
+    for (auto &GO : M.global_objects()) {
+      if (GO.isDeclaration()) {
+        MCSymbol *Sym = TM.getSymbol(&GO);
+        OutStreamer->emitSymbolAttribute(
+            Sym, GO.hasExternalWeakLinkage() ? MCSA_WeakReference : MCSA_Global);
+        OutStreamer->emitSymbolAttribute(Sym, isa<Function>(GO) ? MCSA_Code
+                                                                : MCSA_Data);
+      }
+    }
+    OutStreamer->switchSection(
+        static_cast<MCSectionGOFF *>(getObjFileLowering().getTextSection())
+            ->getParent());
+    getTargetStreamer()->emitExterns();
+    OutStreamer->popSection();
   }
   emitAttributes(M);
   // Emit the END instruction in case of HLASM output. This must be the last
@@ -1570,6 +1587,9 @@ void SystemZAsmPrinter::emitPPA2(Module &M) {
   // Make CELQSTRT symbol.
   const char *StartSymbolName = "CELQSTRT";
   MCSymbol *CELQSTRT = OutContext.getOrCreateSymbol(StartSymbolName);
+  OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Code);
+  // TODO Mark as OS linkage.
+  OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Global);
 
   // Create symbol and assign to class field for use in PPA1.
   PPA2Sym = OutContext.createTempSymbol("PPA2", false);
@@ -1734,6 +1754,9 @@ void SystemZAsmPrinter::emitFunctionEntryLabel() {
         OutStreamer->AddComment("  Bit 2: 0 = Does not use alloca");
     }
     OutStreamer->emitInt32(DSAAndFlags);
+
+    // Functions denote CODE.
+    OutStreamer->emitSymbolAttribute(CurrentFnSym, MCSA_Code);
   }
 
   AsmPrinter::emitFunctionEntryLabel();
diff --git a/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll b/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll
index b28f0dd17599a..43ac737b29778 100644
--- a/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll
@@ -1,4 +1,4 @@
-; RUN: llc -mtriple s390x-ibm-zos < %s | FileCheck %s
+; RUN: llc -mtriple s390x-ibm-zos -emit-gnuas-syntax-on-zos=false < %s | FileCheck %s
 
 define signext i32 @_Z9computeitv() personality ptr @__zos_cxx_personality_v2 {
   ret i32 0
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
index 6caa8f4d607de..0e6619157261d 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
@@ -104,34 +104,50 @@ entry:
 ; CHECK-NEXT: 000300 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
 ; CHECK-NEXT: 000310 00 01 20 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00
 
+; ESD record, type ER.
+; The name is CELQSTRT.
+; CHECK-NEXT: 000320 03 00 00 04 [[CELQSTRT:00 00 00 09]] [[ROOTSD]] 00 00 00 00
+; CHECK-NEXT: 000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000340 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
+; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
+; CHECK-NEXT: 000360 00 04 20 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
+
 ; ESD record, type LD.
 ; The name is me.
-; CHECK-NEXT: 000320 03 00 00 02 [[ME:00 00 00 09]] [[C_CODE64]] 00 00 00 00
-; CHECK-NEXT: 000330 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000340 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
-; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
-; CHECK-NEXT: 000360 00 04 20 00 00 00 00 02 94 85 00 00 00 00 00 00
+; CHECK-NEXT: 000370 03 00 00 02 [[ME:00 00 00 0a]] [[C_CODE64]] 00 00 00 00
+; CHECK-NEXT: 000380 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000390 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
+; CHECK-NEXT: 0003a0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
+; CHECK-NEXT: 0003b0 00 04 20 00 00 00 00 02 94 85 00 00 00 00 00 00
+
+; ESD record, type ER.
+; The name is other.
+; CHECK-NEXT: 0003c0 03 00 00 04 [[OTHER:00 00 00 0b]] [[ROOTSD]] 00 00 00 00
+; CHECK-NEXT: 0003d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0003e0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
+; CHECK-NEXT: 0003f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
+; CHECK-NEXT: 000400 00 04 20 00 00 00 00 05 96 a3 88 85 99 00 00 00
 
 ; Text record for the code section C_CODE64.
 ; The regular expression matches the lower byte of the length.
-; CHECK-NEXT: 000370 03 11 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000380 00 00 00 00 00 00 00 {{..}} 00 c3 00 c5 00 c5 00 f1
+; CHECK-NEXT: 000410 03 11 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000420 00 00 00 00 00 00 00 {{..}} 00 c3 00 c5 00 c5 00 f1
 
 ; Text record for the section .&ppa2.
-; CHECK:      000410 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000420 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      0004b0 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0004c0 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; Text record for the ADA section test#S.
-; CHECK:      000460 03 10 00 00 [[TESTS]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000470 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      000500 03 10 00 00 [[TESTS]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000510 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; Text record for the section B_IDRL.
-; CHECK:      0004b0 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0004c0 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      000550 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; End record.
-; CHECK:      000500 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000540 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK:      0005a0 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0005c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0005d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0005e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-2.ll b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
index 0f608c1206b96..bf3fd8234eb33 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-2.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
@@ -147,29 +147,37 @@ source_filename = "test.ll"
 ; CHECK-NEXT: 0004e0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
 ; CHECK-NEXT: 0004f0 00 01 20 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00
 
+; ESD record, type ER.
+; The name is CELQSTRT.
+; CHECK-NEXT: 000500 03 00 00 04 [[CELQSTRT:00 00 00 0f]] [[ROOTSD]] 00 00 00 00
+; CHECK-NEXT: 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000520 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
+; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
+; CHECK-NEXT: 000540 00 04 20 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
+
 ; Text record for the code section C_CODE64.
 ; The regular expression matches the lower byte of the length.
-; CHECK-NEXT: 000500 03 10 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000510 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK-NEXT: 000550 03 10 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; Text record for the section .&ppa2.
-; CHECK:      000550 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      0005a0 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; Text record for the section data.
 ; Length is 4, and the content is 0x2a = 42.
-; CHECK:      0005a0 03 10 00 00 [[DATA_PR]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 04 00 00 00 2a 00 00 00 00
+; CHECK:      0005f0 03 10 00 00 [[DATA_PR]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000600 00 00 00 00 00 00 00 04 00 00 00 2a 00 00 00 00
 
 ; There is no text record for section bss!
 
 ; Text record for the section B_IDRL.
-; CHECK:      0005f0 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000600 00 00 00 00 00 00 00 {{..}} {{.*}}
+; CHECK:      000640 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 000650 00 00 00 00 00 00 00 {{..}} {{.*}}
 
 ; End record.
-; CHECK:      000640 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-; CHECK-NEXT: 000680 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK:      000690 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0006a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0006b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0006c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK-NEXT: 0006d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
diff --git a/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll b/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll
new file mode 100644
index 0000000000000..eaae1b7bbcfd2
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll
@@ -0,0 +1,37 @@
+; RUN: llc <%s --mtriple s390x-ibm-zos -emit-gnuas-syntax-on-zos=false | FileCheck %s
+
+declare extern_weak void @other1(...)
+declare void @other2(...)
+
+define internal void @me1() {
+entry:
+  ret void
+}
+
+define hidden void @me2() {
+entry:
+  tail call void @other1()
+  ret void
+}
+
+define default void @me3() {
+entry:
+  tail call void @other2()
+  ret void
+}
+
+; CHECK:        ENTRY me1
+; CHECK-NEXT: me1 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(SECTION)
+
+; CHECK:       ENTRY me2
+; CHECK-NEXT: me2 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(LIBRARY)
+
+; CHECK:       ENTRY me3
+; CHECK-NEXT: me3 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
+
+; CHECK:       EXTRN CELQSTRT
+; CHECK-NEXT: CELQSTRT XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
+; CHECK-NEXT:  WXTRN other1
+; CHECK-NEXT: other1 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
+; CHECK-NEXT:  EXTRN other2
+; CHECK-NEXT: other2 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
\ No newline at end of file

>From c19957c59feabbcfb36107f019e915acb6787050 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Mon, 3 Nov 2025 17:36:29 -0500
Subject: [PATCH 3/7] Fix some formatting issues.

---
 llvm/include/llvm/MC/MCSymbolGOFF.h           | 4 +---
 llvm/lib/MC/MCSymbolGOFF.cpp                  | 4 ++--
 llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp | 5 +++--
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h
index 58305a00ec235..301be80a8b575 100644
--- a/llvm/include/llvm/MC/MCSymbolGOFF.h
+++ b/llvm/include/llvm/MC/MCSymbolGOFF.h
@@ -68,9 +68,7 @@ class MCSymbolGOFF : public MCSymbol {
   }
   MCSectionGOFF *getADA() const { return ADA; }
 
-  void setOwner(MCSectionGOFF *Owner) {
-    this->Owner = Owner;
-  }
+  void setOwner(MCSectionGOFF *Owner) { this->Owner = Owner; }
   MCSectionGOFF *getOwner() const { return Owner; }
 
   bool isExternal() const { return IsExternal; }
diff --git a/llvm/lib/MC/MCSymbolGOFF.cpp b/llvm/lib/MC/MCSymbolGOFF.cpp
index 5e248dea033a4..33c7d658038bd 100644
--- a/llvm/lib/MC/MCSymbolGOFF.cpp
+++ b/llvm/lib/MC/MCSymbolGOFF.cpp
@@ -41,7 +41,7 @@ void MCSymbolGOFF::initAttributes() {
     } else
       llvm_unreachable("Unexpected section type for label");
   } else {
-    setERAttributes(GOFF::ERAttr{CodeData, BindingStrength,
-                                 GOFF::ESD_LT_XPLink, BindingScope});
+    setERAttributes(GOFF::ERAttr{CodeData, BindingStrength, GOFF::ESD_LT_XPLink,
+                                 BindingScope});
   }
 }
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index 87ff2f0353161..0a02397ab100b 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -1118,8 +1118,9 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) {
     for (auto &GO : M.global_objects()) {
       if (GO.isDeclaration()) {
         MCSymbol *Sym = TM.getSymbol(&GO);
-        OutStreamer->emitSymbolAttribute(
-            Sym, GO.hasExternalWeakLinkage() ? MCSA_WeakReference : MCSA_Global);
+        OutStreamer->emitSymbolAttribute(Sym, GO.hasExternalWeakLinkage()
+                                                  ? MCSA_WeakReference
+                                                  : MCSA_Global);
         OutStreamer->emitSymbolAttribute(Sym, isa<Function>(GO) ? MCSA_Code
                                                                 : MCSA_Data);
       }

>From 2fa73af9d9f9fc87c939e175f90343aa8738d638 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Fri, 7 Nov 2025 15:46:26 -0500
Subject: [PATCH 4/7] ER symbols also need to set AMODE

---
 llvm/include/llvm/MC/MCGOFFAttributes.h    | 1 +
 llvm/lib/MC/GOFFObjectWriter.cpp           | 1 +
 llvm/lib/MC/MCSymbolGOFF.cpp               | 2 +-
 llvm/test/CodeGen/SystemZ/zos-section-1.ll | 4 ++--
 llvm/test/CodeGen/SystemZ/zos-section-2.ll | 2 +-
 5 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/MC/MCGOFFAttributes.h b/llvm/include/llvm/MC/MCGOFFAttributes.h
index 1631d59462935..e771f36f35346 100644
--- a/llvm/include/llvm/MC/MCGOFFAttributes.h
+++ b/llvm/include/llvm/MC/MCGOFFAttributes.h
@@ -85,6 +85,7 @@ struct ERAttr {
   GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
   GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
   GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
+  GOFF::ESDAmode Amode;
   GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
 };
 
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index 51f95813eabc8..07aecf13bce37 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -275,6 +275,7 @@ class GOFFSymbol {
     BehavAttrs.setExecutable(Attr.Executable);
     BehavAttrs.setBindingStrength(Attr.BindingStrength);
     BehavAttrs.setLinkageType(Attr.Linkage);
+    BehavAttrs.setAmode(Attr.Amode);
     BehavAttrs.setBindingScope(Attr.BindingScope);
   }
 };
diff --git a/llvm/lib/MC/MCSymbolGOFF.cpp b/llvm/lib/MC/MCSymbolGOFF.cpp
index 33c7d658038bd..2d067155d45aa 100644
--- a/llvm/lib/MC/MCSymbolGOFF.cpp
+++ b/llvm/lib/MC/MCSymbolGOFF.cpp
@@ -42,6 +42,6 @@ void MCSymbolGOFF::initAttributes() {
       llvm_unreachable("Unexpected section type for label");
   } else {
     setERAttributes(GOFF::ERAttr{CodeData, BindingStrength, GOFF::ESD_LT_XPLink,
-                                 BindingScope});
+                                 GOFF::ESD_AMODE_64, BindingScope});
   }
 }
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
index 0e6619157261d..728686127bacd 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
@@ -109,7 +109,7 @@ entry:
 ; CHECK-NEXT: 000320 03 00 00 04 [[CELQSTRT:00 00 00 09]] [[ROOTSD]] 00 00 00 00
 ; CHECK-NEXT: 000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ; CHECK-NEXT: 000340 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
-; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
+; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
 ; CHECK-NEXT: 000360 00 04 20 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
 
 ; ESD record, type LD.
@@ -125,7 +125,7 @@ entry:
 ; CHECK-NEXT: 0003c0 03 00 00 04 [[OTHER:00 00 00 0b]] [[ROOTSD]] 00 00 00 00
 ; CHECK-NEXT: 0003d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ; CHECK-NEXT: 0003e0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
-; CHECK-NEXT: 0003f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
+; CHECK-NEXT: 0003f0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
 ; CHECK-NEXT: 000400 00 04 20 00 00 00 00 05 96 a3 88 85 99 00 00 00
 
 ; Text record for the code section C_CODE64.
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-2.ll b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
index bf3fd8234eb33..d3a0d75acba88 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-2.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
@@ -152,7 +152,7 @@ source_filename = "test.ll"
 ; CHECK-NEXT: 000500 03 00 00 04 [[CELQSTRT:00 00 00 0f]] [[ROOTSD]] 00 00 00 00
 ; CHECK-NEXT: 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ; CHECK-NEXT: 000520 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
-; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02
+; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
 ; CHECK-NEXT: 000540 00 04 20 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
 
 ; Text record for the code section C_CODE64.

>From 43dcc134f279af6836a635748c8db084f420a92f Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Fri, 7 Nov 2025 17:49:56 -0500
Subject: [PATCH 5/7] Add linkage attribute

---
 llvm/include/llvm/MC/MCDirectives.h           | 4 ++--
 llvm/include/llvm/MC/MCSymbolGOFF.h           | 4 ++++
 llvm/lib/MC/MCELFStreamer.cpp                 | 3 +++
 llvm/lib/MC/MCGOFFStreamer.cpp                | 6 ++++++
 llvm/lib/MC/MCMachOStreamer.cpp               | 2 ++
 llvm/lib/MC/MCSymbolGOFF.cpp                  | 7 +++----
 llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp | 2 +-
 llvm/test/CodeGen/SystemZ/zos-section-1.ll    | 2 +-
 llvm/test/CodeGen/SystemZ/zos-section-2.ll    | 2 +-
 9 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h
index f69144367c8e7..39ecadb9cd029 100644
--- a/llvm/include/llvm/MC/MCDirectives.h
+++ b/llvm/include/llvm/MC/MCDirectives.h
@@ -48,10 +48,10 @@ enum MCSymbolAttr {
   MCSA_WeakDefAutoPrivate,      ///< .weak_def_can_be_hidden (MachO)
   MCSA_WeakAntiDep,             ///< .weak_anti_dep (COFF)
   MCSA_Memtag,                  ///< .memtag (ELF)
-
-  // Attributes specific for HLASM.
   MCSA_Code,                    ///< symbol is code (GOFF)
   MCSA_Data,                    ///< symbol is data (GOFF)
+  MCSA_OSLinkage,               ///< symbol uses OS linkage (GOFF)
+  MCSA_XPLinkage,               ///< symbol uses XP linkage (GOFF)
 };
 
 enum MCDataRegionType {
diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h
index 301be80a8b575..f1397a6b3822c 100644
--- a/llvm/include/llvm/MC/MCSymbolGOFF.h
+++ b/llvm/include/llvm/MC/MCSymbolGOFF.h
@@ -34,6 +34,7 @@ class MCSymbolGOFF : public MCSymbol {
   GOFF::ERAttr ERAttributes;
 
   GOFF::ESDExecutable CodeData = GOFF::ESDExecutable::ESD_EXE_Unspecified;
+  GOFF::ESDLinkageType Linkage = GOFF::ESDLinkageType::ESD_LT_XPLink;
 
   enum SymbolFlags : uint16_t {
     SF_LD = 0x01,     // LD attributes are set.
@@ -86,6 +87,9 @@ class MCSymbolGOFF : public MCSymbol {
   void setCodeData(GOFF::ESDExecutable Value) { CodeData = Value; }
   GOFF::ESDExecutable getCodeData() const { return CodeData; }
 
+  void setLinkage(GOFF::ESDLinkageType Value) { Linkage = Value; }
+  GOFF::ESDLinkageType getLinkage() const { return Linkage; }
+
   void initAttributes();
 };
 } // end namespace llvm
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index 4d4148c506b4f..a296977f7ae98 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -18,6 +18,7 @@
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDirectives.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCFixup.h"
@@ -153,6 +154,8 @@ bool MCELFStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) {
   case MCSA_WeakAntiDep:
   case MCSA_Code:
   case MCSA_Data:
+  case MCSA_OSLinkage:
+  case MCSA_XPLinkage:
     return false;
 
   case MCSA_NoDeadStrip:
diff --git a/llvm/lib/MC/MCGOFFStreamer.cpp b/llvm/lib/MC/MCGOFFStreamer.cpp
index 62a9117fe895f..8450f7cfb5381 100644
--- a/llvm/lib/MC/MCGOFFStreamer.cpp
+++ b/llvm/lib/MC/MCGOFFStreamer.cpp
@@ -82,6 +82,12 @@ bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Data:
     Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA);
     break;
+  case MCSA_OSLinkage:
+    Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_OS);
+    break;
+  case MCSA_XPLinkage:
+    Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_XPLink);
+    break;
   case MCSA_Global:
     Symbol->setExternal(true);
     break;
diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp
index da148f81012f7..3b0acd71e2057 100644
--- a/llvm/lib/MC/MCMachOStreamer.cpp
+++ b/llvm/lib/MC/MCMachOStreamer.cpp
@@ -301,6 +301,8 @@ bool MCMachOStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_WeakAntiDep:
   case MCSA_Code:
   case MCSA_Data:
+  case MCSA_OSLinkage:
+  case MCSA_XPLinkage:
     return false;
 
   case MCSA_Global:
diff --git a/llvm/lib/MC/MCSymbolGOFF.cpp b/llvm/lib/MC/MCSymbolGOFF.cpp
index 2d067155d45aa..345572418ecd8 100644
--- a/llvm/lib/MC/MCSymbolGOFF.cpp
+++ b/llvm/lib/MC/MCSymbolGOFF.cpp
@@ -32,16 +32,15 @@ void MCSymbolGOFF::initAttributes() {
   if (isDefined()) {
     MCSectionGOFF &Section = static_cast<MCSectionGOFF &>(getSection());
     if (Section.isED()) {
-      setLDAttributes(GOFF::LDAttr{false, CodeData, BindingStrength,
-                                   GOFF::ESD_LT_XPLink, GOFF::ESD_AMODE_64,
-                                   BindingScope});
+      setLDAttributes(GOFF::LDAttr{false, CodeData, BindingStrength, Linkage,
+                                   GOFF::ESD_AMODE_64, BindingScope});
     } else if (Section.isPR()) {
       // For data symbols, the attributes are already determind in TLOFI.
       // TODO Does it make sense to it to here?
     } else
       llvm_unreachable("Unexpected section type for label");
   } else {
-    setERAttributes(GOFF::ERAttr{CodeData, BindingStrength, GOFF::ESD_LT_XPLink,
+    setERAttributes(GOFF::ERAttr{CodeData, BindingStrength, Linkage,
                                  GOFF::ESD_AMODE_64, BindingScope});
   }
 }
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index 0a02397ab100b..193e6ef6d1e64 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -1589,7 +1589,7 @@ void SystemZAsmPrinter::emitPPA2(Module &M) {
   const char *StartSymbolName = "CELQSTRT";
   MCSymbol *CELQSTRT = OutContext.getOrCreateSymbol(StartSymbolName);
   OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Code);
-  // TODO Mark as OS linkage.
+  OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_OSLinkage);
   OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Global);
 
   // Create symbol and assign to class field for use in PPA1.
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
index 728686127bacd..cae371b07d0eb 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
@@ -110,7 +110,7 @@ entry:
 ; CHECK-NEXT: 000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ; CHECK-NEXT: 000340 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
 ; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
-; CHECK-NEXT: 000360 00 04 20 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
+; CHECK-NEXT: 000360 00 04 00 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
 
 ; ESD record, type LD.
 ; The name is me.
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-2.ll b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
index d3a0d75acba88..f59bace65ca73 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-2.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
@@ -153,7 +153,7 @@ source_filename = "test.ll"
 ; CHECK-NEXT: 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ; CHECK-NEXT: 000520 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
 ; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
-; CHECK-NEXT: 000540 00 04 20 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
+; CHECK-NEXT: 000540 00 04 00 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3
 
 ; Text record for the code section C_CODE64.
 ; The regular expression matches the lower byte of the length.

>From ee1434b325d46403b98cb1c369b69be04db22a08 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Fri, 7 Nov 2025 18:02:27 -0500
Subject: [PATCH 6/7] Update the asm output for the linkage type

---
 llvm/lib/MC/MCAsmStreamer.cpp                               | 2 ++
 .../Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp | 6 ++++++
 llvm/test/CodeGen/SystemZ/zos-symbol-1.ll                   | 2 +-
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 60e3757f60fed..18db8ef99c2cf 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -776,6 +776,8 @@ bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol,
     // Non-AIX assemblers currently do not support exported visibility.
   case MCSA_Code:
   case MCSA_Data:
+  case MCSA_OSLinkage:
+  case MCSA_XPLinkage:
     // Only for HLASM.
     return false;
   case MCSA_Memtag:
diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
index 8553f8bc14f56..7294d0a12b7ce 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
@@ -299,6 +299,12 @@ bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Data:
     Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA);
     break;
+  case MCSA_OSLinkage:
+    Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_OS);
+    break;
+  case MCSA_XPLinkage:
+    Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_XPLink);
+    break;
   case MCSA_Global:
     Symbol->setExternal(true);
     break;
diff --git a/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll b/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll
index eaae1b7bbcfd2..bd4361aaafdcb 100644
--- a/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll
@@ -30,7 +30,7 @@ entry:
 ; CHECK-NEXT: me3 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
 
 ; CHECK:       EXTRN CELQSTRT
-; CHECK-NEXT: CELQSTRT XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
+; CHECK-NEXT: CELQSTRT XATTR LINKAGE(OS),REFERENCE(CODE),SCOPE(EXPORT)
 ; CHECK-NEXT:  WXTRN other1
 ; CHECK-NEXT: other1 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT)
 ; CHECK-NEXT:  EXTRN other2

>From 214d716d483c6536c56fb45138bbca035b25c846 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Wed, 19 Nov 2025 13:37:36 -0500
Subject: [PATCH 7/7] Replace MCSA_Code/Data

Use MCSA_ELF_TypeFunction/TypeObject instead.
---
 llvm/include/llvm/MC/MCDirectives.h                      | 2 --
 llvm/lib/MC/MCAsmStreamer.cpp                            | 2 --
 llvm/lib/MC/MCELFStreamer.cpp                            | 2 --
 llvm/lib/MC/MCGOFFStreamer.cpp                           | 6 ++----
 llvm/lib/MC/MCMachOStreamer.cpp                          | 2 --
 .../SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp     | 6 ++----
 llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp            | 9 +++++----
 7 files changed, 9 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h
index 39ecadb9cd029..3b9c4670399c9 100644
--- a/llvm/include/llvm/MC/MCDirectives.h
+++ b/llvm/include/llvm/MC/MCDirectives.h
@@ -48,8 +48,6 @@ enum MCSymbolAttr {
   MCSA_WeakDefAutoPrivate,      ///< .weak_def_can_be_hidden (MachO)
   MCSA_WeakAntiDep,             ///< .weak_anti_dep (COFF)
   MCSA_Memtag,                  ///< .memtag (ELF)
-  MCSA_Code,                    ///< symbol is code (GOFF)
-  MCSA_Data,                    ///< symbol is data (GOFF)
   MCSA_OSLinkage,               ///< symbol uses OS linkage (GOFF)
   MCSA_XPLinkage,               ///< symbol uses XP linkage (GOFF)
 };
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 18db8ef99c2cf..7ff570c7df402 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -774,8 +774,6 @@ bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol,
     // Assemblers currently do not support a .cold directive.
   case MCSA_Exported:
     // Non-AIX assemblers currently do not support exported visibility.
-  case MCSA_Code:
-  case MCSA_Data:
   case MCSA_OSLinkage:
   case MCSA_XPLinkage:
     // Only for HLASM.
diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp
index a296977f7ae98..13a7f6d999e61 100644
--- a/llvm/lib/MC/MCELFStreamer.cpp
+++ b/llvm/lib/MC/MCELFStreamer.cpp
@@ -152,8 +152,6 @@ bool MCELFStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) {
   case MCSA_IndirectSymbol:
   case MCSA_Exported:
   case MCSA_WeakAntiDep:
-  case MCSA_Code:
-  case MCSA_Data:
   case MCSA_OSLinkage:
   case MCSA_XPLinkage:
     return false;
diff --git a/llvm/lib/MC/MCGOFFStreamer.cpp b/llvm/lib/MC/MCGOFFStreamer.cpp
index 8450f7cfb5381..81472ba5e4de6 100644
--- a/llvm/lib/MC/MCGOFFStreamer.cpp
+++ b/llvm/lib/MC/MCGOFFStreamer.cpp
@@ -51,9 +51,7 @@ bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym,
   switch (Attribute) {
   case MCSA_Invalid:
   case MCSA_Cold:
-  case MCSA_ELF_TypeFunction:
   case MCSA_ELF_TypeIndFunction:
-  case MCSA_ELF_TypeObject:
   case MCSA_ELF_TypeTLS:
   case MCSA_ELF_TypeCommon:
   case MCSA_ELF_TypeNoType:
@@ -76,10 +74,10 @@ bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Memtag:
     return false;
 
-  case MCSA_Code:
+  case MCSA_ELF_TypeFunction:
     Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_CODE);
     break;
-  case MCSA_Data:
+  case MCSA_ELF_TypeObject:
     Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA);
     break;
   case MCSA_OSLinkage:
diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp
index 3b0acd71e2057..b8e7772f4e16f 100644
--- a/llvm/lib/MC/MCMachOStreamer.cpp
+++ b/llvm/lib/MC/MCMachOStreamer.cpp
@@ -299,8 +299,6 @@ bool MCMachOStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Exported:
   case MCSA_Memtag:
   case MCSA_WeakAntiDep:
-  case MCSA_Code:
-  case MCSA_Data:
   case MCSA_OSLinkage:
   case MCSA_XPLinkage:
     return false;
diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
index 7294d0a12b7ce..ac0dd84fe9941 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp
@@ -268,9 +268,7 @@ bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
   switch (Attribute) {
   case MCSA_Invalid:
   case MCSA_Cold:
-  case MCSA_ELF_TypeFunction:
   case MCSA_ELF_TypeIndFunction:
-  case MCSA_ELF_TypeObject:
   case MCSA_ELF_TypeTLS:
   case MCSA_ELF_TypeCommon:
   case MCSA_ELF_TypeNoType:
@@ -293,10 +291,10 @@ bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym,
   case MCSA_Memtag:
     return false;
 
-  case MCSA_Code:
+  case MCSA_ELF_TypeFunction:
     Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_CODE);
     break;
-  case MCSA_Data:
+  case MCSA_ELF_TypeObject:
     Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA);
     break;
   case MCSA_OSLinkage:
diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
index 193e6ef6d1e64..b895ae9546e5a 100644
--- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp
@@ -1121,8 +1121,9 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) {
         OutStreamer->emitSymbolAttribute(Sym, GO.hasExternalWeakLinkage()
                                                   ? MCSA_WeakReference
                                                   : MCSA_Global);
-        OutStreamer->emitSymbolAttribute(Sym, isa<Function>(GO) ? MCSA_Code
-                                                                : MCSA_Data);
+        OutStreamer->emitSymbolAttribute(Sym, isa<Function>(GO)
+                                                  ? MCSA_ELF_TypeFunction
+                                                  : MCSA_ELF_TypeObject);
       }
     }
     OutStreamer->switchSection(
@@ -1588,7 +1589,7 @@ void SystemZAsmPrinter::emitPPA2(Module &M) {
   // Make CELQSTRT symbol.
   const char *StartSymbolName = "CELQSTRT";
   MCSymbol *CELQSTRT = OutContext.getOrCreateSymbol(StartSymbolName);
-  OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Code);
+  OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_ELF_TypeFunction);
   OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_OSLinkage);
   OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Global);
 
@@ -1757,7 +1758,7 @@ void SystemZAsmPrinter::emitFunctionEntryLabel() {
     OutStreamer->emitInt32(DSAAndFlags);
 
     // Functions denote CODE.
-    OutStreamer->emitSymbolAttribute(CurrentFnSym, MCSA_Code);
+    OutStreamer->emitSymbolAttribute(CurrentFnSym, MCSA_ELF_TypeFunction);
   }
 
   AsmPrinter::emitFunctionEntryLabel();



More information about the llvm-commits mailing list