Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[z/OS][GOFF] Implement support for writing ESD + TXT records by the GOFFObjectWriter #85851

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

Everybody0523
Copy link
Contributor

This PR implements generation of ESD/TXT Records in GOFF Writer.

A few notes on GOFF and the overall design:

GOFF Structure

GOFF (GOFF Object File Format) is is the object file format used on z/OS. GOFF files are built out of 80-byte records.

There are 5 types of records. They are:

  1. HDR (Header)
  2. ESD (External Symbol)
  3. TXT (Text)
  4. RLD (Relocation Data)
  5. END (End).

The HDR and END records are present in every file, and are implemented already by this commit.

ESD and TXT records have a notion of "ownership", which means that an ESD symbol can be "owned" another ESD symbol. TXT records are always owned by an ESD symbol.

GOFF doesn't truly have "sections" or "symbols" in the way other object file formats do, although it does have something vaguely analogous, which will be discussed below.

MCSymbol handling

An MCSymbol mostly maps to one (or more) ESD Record. Some types of symbols may cause more than one symbol to be generated. In addition, every compilation unit has a few special symbols that are used by the binder.

MCSection handling

GOFF "sections" are defined by a triple of ESD Symbols. Specifically, each section is defined by an SD symbol, an ED symbol, and a PR or LD symbol. The PR/LD symbol's parent should be the ED symbol and the ED symbol's parent should be the SD symbol. Furthermore, the PR/LD symbol should own a TXT record. The contents of the section reside in that TXT record. The SD symbol need not be unique to the section; in fact it is quite common that multiple GOFF sections are are "rooted" and the initial SD node (ie. the first ESD entry).

Some examples of "sections" are:

  • Global Variables. Each global variable with external linkage has its own section.
  • The "code section". The code section is a code csect with the default name <filename>#C The bodies of all functions reside in this section.
  • The ADA section. This is a section that contains a pointer to the body of each function as well as metadata relevant to that function.

@llvmbot
Copy link
Collaborator

llvmbot commented Mar 19, 2024

@llvm/pr-subscribers-backend-systemz

@llvm/pr-subscribers-llvm-binary-utilities

Author: Neumann Hon (Everybody0523)

Changes

This PR implements generation of ESD/TXT Records in GOFF Writer.

A few notes on GOFF and the overall design:

GOFF Structure

GOFF (GOFF Object File Format) is is the object file format used on z/OS. GOFF files are built out of 80-byte records.

There are 5 types of records. They are:

  1. HDR (Header)
  2. ESD (External Symbol)
  3. TXT (Text)
  4. RLD (Relocation Data)
  5. END (End).

The HDR and END records are present in every file, and are implemented already by this commit.

ESD and TXT records have a notion of "ownership", which means that an ESD symbol can be "owned" another ESD symbol. TXT records are always owned by an ESD symbol.

GOFF doesn't truly have "sections" or "symbols" in the way other object file formats do, although it does have something vaguely analogous, which will be discussed below.

MCSymbol handling

An MCSymbol mostly maps to one (or more) ESD Record. Some types of symbols may cause more than one symbol to be generated. In addition, every compilation unit has a few special symbols that are used by the binder.

MCSection handling

GOFF "sections" are defined by a triple of ESD Symbols. Specifically, each section is defined by an SD symbol, an ED symbol, and a PR or LD symbol. The PR/LD symbol's parent should be the ED symbol and the ED symbol's parent should be the SD symbol. Furthermore, the PR/LD symbol should own a TXT record. The contents of the section reside in that TXT record. The SD symbol need not be unique to the section; in fact it is quite common that multiple GOFF sections are are "rooted" and the initial SD node (ie. the first ESD entry).

Some examples of "sections" are:

  • Global Variables. Each global variable with external linkage has its own section.
  • The "code section". The code section is a code csect with the default name &lt;filename&gt;#C The bodies of all functions reside in this section.
  • The ADA section. This is a section that contains a pointer to the body of each function as well as metadata relevant to that function.

Patch is 67.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/85851.diff

16 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/GOFF.h (+10-6)
  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+4-1)
  • (modified) llvm/include/llvm/MC/MCAssembler.h (+7)
  • (modified) llvm/include/llvm/MC/MCContext.h (+2-1)
  • (modified) llvm/include/llvm/MC/MCGOFFStreamer.h (+7-4)
  • (modified) llvm/include/llvm/MC/MCSymbolGOFF.h (+54-1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+6-2)
  • (modified) llvm/lib/MC/GOFFObjectWriter.cpp (+806-1)
  • (modified) llvm/lib/MC/MCGOFFStreamer.cpp (+76)
  • (modified) llvm/lib/MC/MCObjectFileInfo.cpp (+3)
  • (modified) llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp (+4)
  • (modified) llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h (+2)
  • (modified) llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp (+94-5)
  • (modified) llvm/lib/Target/SystemZ/SystemZAsmPrinter.h (+4)
  • (added) llvm/test/MC/GOFF/basic-goff-64.ll (+252)
  • (modified) llvm/test/MC/GOFF/empty-goff.s (+20-19)
diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h
index 443bcfc9479a8b..b0cd2484536ea2 100644
--- a/llvm/include/llvm/BinaryFormat/GOFF.h
+++ b/llvm/include/llvm/BinaryFormat/GOFF.h
@@ -29,6 +29,8 @@ constexpr uint8_t RecordLength = 80;
 constexpr uint8_t RecordPrefixLength = 3;
 constexpr uint8_t PayloadLength = 77;
 constexpr uint8_t RecordContentLength = RecordLength - RecordPrefixLength;
+constexpr uint8_t ESDMetadataLength = 69;
+constexpr uint8_t TXTMetadataLength = 21;
 
 /// \brief Maximum data length before starting a new card for RLD and TXT data.
 ///
@@ -65,12 +67,7 @@ enum ESDNameSpaceId : uint8_t {
   ESD_NS_Parts = 3
 };
 
-enum ESDReserveQwords : uint8_t {
-  ESD_RQ_0 = 0,
-  ESD_RQ_1 = 1,
-  ESD_RQ_2 = 2,
-  ESD_RQ_3 = 3
-};
+enum ESDReserveQwords : uint8_t { ESD_RQ_0 = 0, ESD_RQ_1 = 1 };
 
 enum ESDAmode : uint8_t {
   ESD_AMODE_None = 0,
@@ -157,6 +154,12 @@ enum ESDAlignment : uint8_t {
   ESD_ALIGN_4Kpage = 12,
 };
 
+enum TXTRecordStyle : uint8_t {
+  TXT_RS_Byte = 0,
+  TXT_RS_Structured = 1,
+  TXT_RS_Unstructured = 2,
+};
+
 enum ENDEntryPointRequest : uint8_t {
   END_EPR_None = 0,
   END_EPR_EsdidOffset = 1,
@@ -166,6 +169,7 @@ enum ENDEntryPointRequest : uint8_t {
 
 // \brief Subsections of the primary C_CODE section in the object file.
 enum SubsectionKind : uint8_t {
+  SK_ReadOnly = 1,
   SK_PPA1 = 2,
   SK_PPA2 = 4,
 };
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index a7fbf4aeb74494..0fb696bbb68b13 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -897,10 +897,13 @@ class AsmPrinter : public MachineFunctionPass {
   virtual void emitModuleCommandLines(Module &M);
 
   GCMetadataPrinter *getOrCreateGCPrinter(GCStrategy &S);
+
+protected:
   virtual void emitGlobalAlias(const Module &M, const GlobalAlias &GA);
-  void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
 
 private:
+  void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
+
   /// This method decides whether the specified basic block requires a label.
   bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const;
 
diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h
index 3fa5f2fe655e86..7c1b6bdb3ffb60 100644
--- a/llvm/include/llvm/MC/MCAssembler.h
+++ b/llvm/include/llvm/MC/MCAssembler.h
@@ -136,6 +136,9 @@ class MCAssembler {
   // Optional compiler version.
   std::string CompilerVersion;
 
+  /// GOFF Csect names.
+  std::pair<std::string, std::string> CsectNames;
+
   MCDwarfLineTableParams LTParams;
 
   /// The set of function symbols for which a .thumb_func directive has
@@ -488,6 +491,10 @@ class MCAssembler {
     FileNames.emplace_back(std::string(FileName), Symbols.size());
   }
 
+  std::pair<std::string, std::string> const &getCsectNames() {
+    return CsectNames;
+  }
+
   void setCompilerVersion(std::string CompilerVers) {
     if (CompilerVersion.empty())
       CompilerVersion = std::move(CompilerVers);
diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h
index 3f585d4d2efaf5..e9c7c96298d683 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -623,7 +623,8 @@ class MCContext {
                                                    unsigned EntrySize);
 
   MCSectionGOFF *getGOFFSection(StringRef Section, SectionKind Kind,
-                                MCSection *Parent, const MCExpr *SubsectionId);
+                                MCSection *Parent = nullptr,
+                                const MCExpr *SubsectionId = nullptr);
 
   MCSectionCOFF *getCOFFSection(StringRef Section, unsigned Characteristics,
                                 SectionKind Kind, StringRef COMDATSymName,
diff --git a/llvm/include/llvm/MC/MCGOFFStreamer.h b/llvm/include/llvm/MC/MCGOFFStreamer.h
index 2345509b161da5..52cde0632483a0 100644
--- a/llvm/include/llvm/MC/MCGOFFStreamer.h
+++ b/llvm/include/llvm/MC/MCGOFFStreamer.h
@@ -24,12 +24,15 @@ class MCGOFFStreamer : public MCObjectStreamer {
 
   ~MCGOFFStreamer() override;
 
-  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
-    return false;
-  }
+  // state management
+  void initSections(bool NoExecStack, const MCSubtargetInfo &STI) override;
+
+  void switchSection(MCSection *Section,
+                     const MCExpr *Subsection = nullptr) override;
+  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override;
   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
                         Align ByteAlignment) override {}
-  void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override {}
+  void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override;
   void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
                     uint64_t Size = 0, Align ByteAlignment = Align(1),
                     SMLoc Loc = SMLoc()) override {}
diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h
index cc4e2bbe246e2d..bb3c3b07947aa3 100644
--- a/llvm/include/llvm/MC/MCSymbolGOFF.h
+++ b/llvm/include/llvm/MC/MCSymbolGOFF.h
@@ -18,10 +18,63 @@
 namespace llvm {
 
 class MCSymbolGOFF : public MCSymbol {
+  Align Alignment;
+
+  mutable StringRef AliasName; // ADA indirect
+
+  enum SymbolFlags : uint16_t {
+    SF_NoRent = 0x01,    // Symbol is no-reentrant.
+    SF_Alias = 0x02,     // Symbol is alias.
+    SF_Hidden = 0x04,    // Symbol is hidden, aka not exported.
+    SF_Weak = 0x08,      // Symbol is weak.
+    SF_OSLinkage = 0x10, // Symbol uses OS linkage.
+  };
+
+  // Shift value for GOFF::ESDExecutable. 3 possible values. 2 bits.
+  static constexpr uint8_t GOFF_Executable_Shift = 6;
+  static constexpr uint8_t GOFF_Executable_Bitmask = 0x03;
+
 public:
   MCSymbolGOFF(const StringMapEntry<bool> *Name, bool IsTemporary)
-      : MCSymbol(SymbolKindGOFF, Name, IsTemporary) {}
+      : MCSymbol(SymbolKindGOFF, Name, IsTemporary), AliasName() {}
   static bool classof(const MCSymbol *S) { return S->isGOFF(); }
+
+  bool hasAliasName() const { return !AliasName.empty(); }
+  void setAliasName(StringRef Name) { AliasName = Name; }
+  StringRef getAliasName() const { return AliasName; }
+
+  void setTemporary(bool Value) { IsTemporary = Value; }
+
+  void setOSLinkage(bool Value = true) const {
+    modifyFlags(Value ? SF_OSLinkage : 0, SF_OSLinkage);
+  }
+  bool isOSLinkage() const { return getFlags() & SF_OSLinkage; }
+
+  void setAlignment(Align Value) { Alignment = Value; }
+  Align getAlignment() const { return Alignment; }
+
+  void setExecutable(GOFF::ESDExecutable Value) const {
+    modifyFlags(Value << GOFF_Executable_Shift,
+                GOFF_Executable_Bitmask << GOFF_Executable_Shift);
+  }
+  GOFF::ESDExecutable getExecutable() const {
+    return static_cast<GOFF::ESDExecutable>(
+        (getFlags() >> GOFF_Executable_Shift) & GOFF_Executable_Bitmask);
+  }
+
+  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 setAlias(bool Value = true) {
+    modifyFlags(Value ? SF_Alias : 0, SF_Alias);
+  }
+  bool isAlias() const { return getFlags() & SF_Alias; }
 };
 } // end namespace llvm
 
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 15b59421a0f44a..3b4f7faa99d590 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2744,9 +2744,13 @@ MCSection *TargetLoweringObjectFileGOFF::getSectionForLSDA(
 MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
   auto *Symbol = TM.getSymbol(GO);
+  if (Kind.isData())
+    return getContext().getGOFFSection(Symbol->getName(),
+                                       SectionKind::getData());
+
   if (Kind.isBSS())
-    return getContext().getGOFFSection(Symbol->getName(), SectionKind::getBSS(),
-                                       nullptr, nullptr);
+    return getContext().getGOFFSection(Symbol->getName(),
+                                       SectionKind::getBSS());
 
   return getContext().getObjectFileInfo()->getTextSection();
 }
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index addeb6db959693..60ab410e9765e2 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -13,8 +13,13 @@
 #include "llvm/BinaryFormat/GOFF.h"
 #include "llvm/MC/MCAsmLayout.h"
 #include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCGOFFObjectWriter.h"
+#include "llvm/MC/MCSectionGOFF.h"
+#include "llvm/MC/MCSymbolGOFF.h"
 #include "llvm/MC/MCValue.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ConvertEBCDIC.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/Path.h"
@@ -217,13 +222,112 @@ void GOFFOstream::write_impl(const char *Ptr, size_t Size) {
   }
 }
 
+/// \brief Wrapper class for symbols used exclusively for the symbol table in a
+/// GOFF file.
+class GOFFSymbol {
+public:
+  std::string Name;
+  uint32_t EsdId;
+  uint32_t ParentEsdId;
+  const MCSymbolGOFF *MCSym;
+  GOFF::ESDSymbolType SymbolType;
+
+  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_NormalName;
+  GOFF::ESDAmode Amode = GOFF::ESD_AMODE_64;
+  GOFF::ESDRmode Rmode = GOFF::ESD_RMODE_64;
+  GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
+  GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
+  GOFF::ESDAlignment Alignment = GOFF::ESD_ALIGN_Byte;
+  GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented;
+  GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate;
+  GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial;
+  GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
+  GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
+  GOFF::ESDReserveQwords ReservedQwords = GOFF::ESD_RQ_0;
+
+  uint32_t SortKey = 0;
+  uint32_t SectionLength = 0;
+  uint32_t ADAEsdId = 0;
+  bool Indirect = false;
+  bool ForceRent = false;
+  bool Renamable = false;
+  bool ReadOnly = false;
+  uint32_t EASectionEsdId = 0;
+  uint32_t EASectionOffset = 0;
+
+  GOFFSymbol(StringRef Name, GOFF::ESDSymbolType Type, uint32_t EsdID,
+             uint32_t ParentEsdID)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
+        MCSym(nullptr), SymbolType(Type) {}
+  GOFFSymbol() {}
+
+  bool isForceRent() const { return ForceRent; }
+  bool isReadOnly() const { return ReadOnly; }
+  bool isRemovable() const { return false; }
+  bool isExecutable() const { return Executable == GOFF::ESD_EXE_CODE; }
+  bool isExecUnspecified() const {
+    return Executable == GOFF::ESD_EXE_Unspecified;
+  }
+  bool isWeakRef() const { return BindingStrength == GOFF::ESD_BST_Weak; }
+  bool isExternal() const {
+    return (BindingScope == GOFF::ESD_BSC_Library) ||
+           (BindingScope == GOFF::ESD_BSC_ImportExport);
+  }
+
+  void setAlignment(Align A) {
+    // The GOFF alignment is encoded as log_2 value.
+    uint8_t Log = Log2(A);
+    if (Log <= GOFF::ESD_ALIGN_4Kpage)
+      Alignment = static_cast<GOFF::ESDAlignment>(Log);
+    else
+      llvm_unreachable("Unsupported alignment");
+  }
+};
+
+/// \brief Wrapper class for sections used exclusively for representing sections
+/// of the GOFF output that have actual bytes.  This could be a ED or a PR.
+/// Relocations will always have a P-pointer to the ESDID of one of these.
+struct GOFFSection {
+  uint32_t PEsdId;
+  uint32_t REsdId;
+  uint32_t SDEsdId;
+  const MCSectionGOFF *MC;
+
+  GOFFSection(uint32_t PEsdId, uint32_t REsdId, uint32_t SDEsdId)
+      : PEsdId(PEsdId), REsdId(REsdId), SDEsdId(SDEsdId), MC(nullptr) {}
+  GOFFSection(uint32_t PEsdId, uint32_t REsdId, uint32_t SDEsdId,
+              const MCSectionGOFF *MC)
+      : PEsdId(PEsdId), REsdId(REsdId), SDEsdId(SDEsdId), MC(MC) {}
+};
+
+// An MCSymbol may map to up to two GOFF Symbols. The first is the
+// "true" underlying symbol, the second one represents any indirect
+// references to that symbol.
 class GOFFObjectWriter : public MCObjectWriter {
+  typedef DenseMap<MCSymbol const *, uint32_t> SymbolMapType;
+  typedef DenseMap<MCSection const *, GOFFSection> SectionMapType;
+
   // The target specific GOFF writer instance.
   std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter;
 
+  /// Lookup table for MCSymbols to GOFFSymbols.  Needed to determine EsdIds
+  /// of symbols in Relocations.
+  SymbolMapType SymbolMap;
+
+  /// Lookup table for MCSections to GOFFSections.  Needed to determine
+  /// SymbolType on GOFFSymbols that reside in GOFFSections.
+  SectionMapType SectionMap;
+
   // The stream used to write the GOFF records.
   GOFFOstream OS;
 
+  uint32_t EsdCounter = 1;
+
+  // The "root" SD node. This should have ESDID = 1
+  GOFFSymbol RootSD;
+  uint32_t EntryEDEsdId;
+  uint32_t CodeLDEsdId;
+
 public:
   GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
                    raw_pwrite_stream &OS)
@@ -231,20 +335,290 @@ class GOFFObjectWriter : public MCObjectWriter {
 
   ~GOFFObjectWriter() override {}
 
+private:
   // Write GOFF records.
   void writeHeader();
+
+  void writeSymbol(const GOFFSymbol &Symbol, const MCAsmLayout &Layout);
+
+  void writeADAandCodeSectionSymbols(MCAssembler &Asm,
+                                     const MCAsmLayout &Layout);
+  // Write out all ESD Symbols that own a section. GOFF doesn't have
+  // "sections" in the way other object file formats like ELF do; instead
+  // a GOFF section is defined as a triple of ESD Symbols (SD, ED, PR/LD).
+  // The PR/LD symbol should own a TXT record that contains the actual
+  // data of the section.
+  void writeSectionSymbols(MCAssembler &Asm, const MCAsmLayout &Layout);
+
+  void writeText(const MCSectionGOFF *MCSec, uint32_t EsdId, bool IsStructured,
+                 MCAssembler &Asm, const MCAsmLayout &Layout);
+
   void writeEnd();
 
+public:
   // Implementation of the MCObjectWriter interface.
   void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                         const MCFragment *Fragment, const MCFixup &Fixup,
                         MCValue Target, uint64_t &FixedValue) override {}
   void executePostLayoutBinding(MCAssembler &Asm,
-                                const MCAsmLayout &Layout) override {}
+                                const MCAsmLayout &Layout) override;
   uint64_t writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;
+
+private:
+  GOFFSection *createGOFFSection(GOFFSymbol *Pptr, GOFFSymbol *Rptr,
+                                 GOFFSymbol *SD, const MCSectionGOFF *MC);
+  GOFFSymbol createGOFFSymbol(StringRef Name, GOFF::ESDSymbolType Type,
+                              uint32_t ParentEsdId);
+  GOFFSymbol createSDSymbol(StringRef Name);
+  GOFFSymbol createEDSymbol(StringRef Name, uint32_t ParentEsdId);
+  GOFFSymbol createLDSymbol(StringRef Name, uint32_t ParentEsdId);
+  GOFFSymbol createERSymbol(StringRef Name, uint32_t ParentEsdId,
+                            const MCSymbolGOFF *Source = nullptr);
+  GOFFSymbol createPRSymbol(StringRef Name, uint32_t ParentEsdId);
+
+  GOFFSymbol createWSASymbol(uint32_t ParentEsdId, bool ReserveQWords = false);
+
+  // Define the root SD node as well as the symbols that make up the
+  // ADA section.
+  void defineRootSD(MCAssembler &Asm, const MCAsmLayout &Layout);
+
+  void defineSectionSymbols(const MCAssembler &Asm,
+                            const MCSectionGOFF &Section,
+                            const MCAsmLayout &Layout);
+  void writeSymbolDefinedInModule(const MCSymbolGOFF &Symbol, MCAssembler &Asm,
+                                  const MCAsmLayout &Layout);
+  void writeSymbolDeclaredInModule(const MCSymbolGOFF &Symbol, MCAssembler &Asm,
+                                   const MCAsmLayout &Layout);
+
+  // In XPLINK, the ESD Record owning the ADA is always the 4th record, behind
+  // the HDR record, the Root SD, and the ED symbol for the ADA, meaning it
+  // always has EsdId = 3.
+  static constexpr uint32_t ADAEsdId = 3u;
 };
 } // end anonymous namespace
 
+GOFFSymbol GOFFObjectWriter::createGOFFSymbol(StringRef Name,
+                                              GOFF::ESDSymbolType Type,
+                                              uint32_t ParentEsdId) {
+  return GOFFSymbol(Name, Type, EsdCounter++, ParentEsdId);
+}
+
+GOFFSymbol GOFFObjectWriter::createSDSymbol(StringRef Name) {
+  return createGOFFSymbol(Name, GOFF::ESD_ST_SectionDefinition, 0);
+}
+
+GOFFSymbol GOFFObjectWriter::createEDSymbol(StringRef Name,
+                                            uint32_t ParentEsdId) {
+  GOFFSymbol ED =
+      createGOFFSymbol(Name, GOFF::ESD_ST_ElementDefinition, ParentEsdId);
+
+  ED.Alignment = GOFF::ESD_ALIGN_Doubleword;
+  return ED;
+}
+
+GOFFSymbol GOFFObjectWriter::createLDSymbol(StringRef Name,
+                                            uint32_t ParentEsdId) {
+  return createGOFFSymbol(Name, GOFF::ESD_ST_LabelDefinition, ParentEsdId);
+}
+
+GOFFSymbol GOFFObjectWriter::createERSymbol(StringRef Name,
+                                            uint32_t ParentEsdId,
+                                            const MCSymbolGOFF *Source) {
+  GOFFSymbol ER =
+      createGOFFSymbol(Name, GOFF::ESD_ST_ExternalReference, ParentEsdId);
+
+  if (Source) {
+    ER.Linkage = Source->isOSLinkage() ? GOFF::ESDLinkageType::ESD_LT_OS
+                                       : GOFF::ESDLinkageType::ESD_LT_XPLink;
+    ER.Executable = Source->getExecutable();
+    ER.BindingScope = Source->isExternal()
+                          ? GOFF::ESDBindingScope::ESD_BSC_Library
+                          : GOFF::ESDBindingScope::ESD_BSC_Section;
+    ER.BindingStrength = Source->isWeak()
+                             ? GOFF::ESDBindingStrength::ESD_BST_Weak
+                             : GOFF::ESDBindingStrength::ESD_BST_Strong;
+  }
+
+  return ER;
+}
+
+GOFFSymbol GOFFObjectWriter::createPRSymbol(StringRef Name,
+                                            uint32_t ParentEsdId) {
+  return createGOFFSymbol(Name, GOFF::ESD_ST_PartReference, ParentEsdId);
+}
+
+GOFFSymbol GOFFObjectWriter::createWSASymbol(uint32_t ParentEsdId,
+                                             bool ReserveQwords) {
+  const char *WSAClassName = "C_WSA64";
+  GOFFSymbol WSA = createEDSymbol(WSAClassName, ParentEsdId);
+
+  WSA.Executable = GOFF::ESD_EXE_DATA;
+  WSA.TextStyle = GOFF::ESD_TS_ByteOriented;
+  WSA.BindAlgorithm = GOFF::ESD_BA_Merge;
+  WSA.Alignment = GOFF::ESD_ALIGN_Quadword;
+  WSA.LoadBehavior = GOFF::ESD_LB_Deferred;
+  WSA.NameSpace = GOFF::ESD_NS_Parts;
+  WSA.SectionLength = 0;
+  WSA.ReservedQwords = ReserveQwords ? GOFF::ESD_RQ_1 : GOFF::ESD_RQ_0;
+
+  return WSA;
+}
+
+static uint32_t getADASectionLength(MCAssembler &Asm,
+                                    const MCAsmLayout &Layout) {
+  for (MCSection &S : Asm)
+    if (S.getName().equals(".ada"))
+      return Layout.getSectionAddressSize(&S);
+  return 0;
+}
+
+void GOFFObjectWriter::defineRootSD(MCAssembler &Asm,
+                                    const MCAsmLayout &Layout) {
+  StringRef FileName = "";
+  if (!Asm.getFileNames().empty())
+    FileName = sys::path::stem((*(Asm.getFileNames().begin())).first);
+  std::pair<std::string, std::string> CsectNames = Asm.getCsectNames();
+  if (CsectNames.first.empty()) {
+    RootSD = createSDSymbol(FileName.str().append("#C"));
+    RootSD.BindingScope = GOFF::ESD_BSC_Section;
+  } else {
+    RootSD = createSDSymbol(CsectNames.first);
+  }
+  RootSD.Executable = GOFF::ESD_EXE_CODE;
+}
+
+void GOFFObjectWriter::writeSymbolDefinedInModule(const MCSymbolGOFF &Symbol,
+                                                  MCAssembler &Asm,
+                                                  const MCAsmLayout &Layout) {
+  MCSection &Section = Symbol.getSection();
+  SectionKind Kind = Section.getKind();
+  auto &Sec = cast<MCSectionGOFF>(Section);
+
+  StringRef SymbolName = Symbol.getName();...
[truncated]

@llvmbot
Copy link
Collaborator

llvmbot commented Mar 19, 2024

@llvm/pr-subscribers-mc

Author: Neumann Hon (Everybody0523)

Changes

This PR implements generation of ESD/TXT Records in GOFF Writer.

A few notes on GOFF and the overall design:

GOFF Structure

GOFF (GOFF Object File Format) is is the object file format used on z/OS. GOFF files are built out of 80-byte records.

There are 5 types of records. They are:

  1. HDR (Header)
  2. ESD (External Symbol)
  3. TXT (Text)
  4. RLD (Relocation Data)
  5. END (End).

The HDR and END records are present in every file, and are implemented already by this commit.

ESD and TXT records have a notion of "ownership", which means that an ESD symbol can be "owned" another ESD symbol. TXT records are always owned by an ESD symbol.

GOFF doesn't truly have "sections" or "symbols" in the way other object file formats do, although it does have something vaguely analogous, which will be discussed below.

MCSymbol handling

An MCSymbol mostly maps to one (or more) ESD Record. Some types of symbols may cause more than one symbol to be generated. In addition, every compilation unit has a few special symbols that are used by the binder.

MCSection handling

GOFF "sections" are defined by a triple of ESD Symbols. Specifically, each section is defined by an SD symbol, an ED symbol, and a PR or LD symbol. The PR/LD symbol's parent should be the ED symbol and the ED symbol's parent should be the SD symbol. Furthermore, the PR/LD symbol should own a TXT record. The contents of the section reside in that TXT record. The SD symbol need not be unique to the section; in fact it is quite common that multiple GOFF sections are are "rooted" and the initial SD node (ie. the first ESD entry).

Some examples of "sections" are:

  • Global Variables. Each global variable with external linkage has its own section.
  • The "code section". The code section is a code csect with the default name &lt;filename&gt;#C The bodies of all functions reside in this section.
  • The ADA section. This is a section that contains a pointer to the body of each function as well as metadata relevant to that function.

Patch is 67.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/85851.diff

16 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/GOFF.h (+10-6)
  • (modified) llvm/include/llvm/CodeGen/AsmPrinter.h (+4-1)
  • (modified) llvm/include/llvm/MC/MCAssembler.h (+7)
  • (modified) llvm/include/llvm/MC/MCContext.h (+2-1)
  • (modified) llvm/include/llvm/MC/MCGOFFStreamer.h (+7-4)
  • (modified) llvm/include/llvm/MC/MCSymbolGOFF.h (+54-1)
  • (modified) llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (+6-2)
  • (modified) llvm/lib/MC/GOFFObjectWriter.cpp (+806-1)
  • (modified) llvm/lib/MC/MCGOFFStreamer.cpp (+76)
  • (modified) llvm/lib/MC/MCObjectFileInfo.cpp (+3)
  • (modified) llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp (+4)
  • (modified) llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h (+2)
  • (modified) llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp (+94-5)
  • (modified) llvm/lib/Target/SystemZ/SystemZAsmPrinter.h (+4)
  • (added) llvm/test/MC/GOFF/basic-goff-64.ll (+252)
  • (modified) llvm/test/MC/GOFF/empty-goff.s (+20-19)
diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h
index 443bcfc9479a8b..b0cd2484536ea2 100644
--- a/llvm/include/llvm/BinaryFormat/GOFF.h
+++ b/llvm/include/llvm/BinaryFormat/GOFF.h
@@ -29,6 +29,8 @@ constexpr uint8_t RecordLength = 80;
 constexpr uint8_t RecordPrefixLength = 3;
 constexpr uint8_t PayloadLength = 77;
 constexpr uint8_t RecordContentLength = RecordLength - RecordPrefixLength;
+constexpr uint8_t ESDMetadataLength = 69;
+constexpr uint8_t TXTMetadataLength = 21;
 
 /// \brief Maximum data length before starting a new card for RLD and TXT data.
 ///
@@ -65,12 +67,7 @@ enum ESDNameSpaceId : uint8_t {
   ESD_NS_Parts = 3
 };
 
-enum ESDReserveQwords : uint8_t {
-  ESD_RQ_0 = 0,
-  ESD_RQ_1 = 1,
-  ESD_RQ_2 = 2,
-  ESD_RQ_3 = 3
-};
+enum ESDReserveQwords : uint8_t { ESD_RQ_0 = 0, ESD_RQ_1 = 1 };
 
 enum ESDAmode : uint8_t {
   ESD_AMODE_None = 0,
@@ -157,6 +154,12 @@ enum ESDAlignment : uint8_t {
   ESD_ALIGN_4Kpage = 12,
 };
 
+enum TXTRecordStyle : uint8_t {
+  TXT_RS_Byte = 0,
+  TXT_RS_Structured = 1,
+  TXT_RS_Unstructured = 2,
+};
+
 enum ENDEntryPointRequest : uint8_t {
   END_EPR_None = 0,
   END_EPR_EsdidOffset = 1,
@@ -166,6 +169,7 @@ enum ENDEntryPointRequest : uint8_t {
 
 // \brief Subsections of the primary C_CODE section in the object file.
 enum SubsectionKind : uint8_t {
+  SK_ReadOnly = 1,
   SK_PPA1 = 2,
   SK_PPA2 = 4,
 };
diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h
index a7fbf4aeb74494..0fb696bbb68b13 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -897,10 +897,13 @@ class AsmPrinter : public MachineFunctionPass {
   virtual void emitModuleCommandLines(Module &M);
 
   GCMetadataPrinter *getOrCreateGCPrinter(GCStrategy &S);
+
+protected:
   virtual void emitGlobalAlias(const Module &M, const GlobalAlias &GA);
-  void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
 
 private:
+  void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
+
   /// This method decides whether the specified basic block requires a label.
   bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const;
 
diff --git a/llvm/include/llvm/MC/MCAssembler.h b/llvm/include/llvm/MC/MCAssembler.h
index 3fa5f2fe655e86..7c1b6bdb3ffb60 100644
--- a/llvm/include/llvm/MC/MCAssembler.h
+++ b/llvm/include/llvm/MC/MCAssembler.h
@@ -136,6 +136,9 @@ class MCAssembler {
   // Optional compiler version.
   std::string CompilerVersion;
 
+  /// GOFF Csect names.
+  std::pair<std::string, std::string> CsectNames;
+
   MCDwarfLineTableParams LTParams;
 
   /// The set of function symbols for which a .thumb_func directive has
@@ -488,6 +491,10 @@ class MCAssembler {
     FileNames.emplace_back(std::string(FileName), Symbols.size());
   }
 
+  std::pair<std::string, std::string> const &getCsectNames() {
+    return CsectNames;
+  }
+
   void setCompilerVersion(std::string CompilerVers) {
     if (CompilerVersion.empty())
       CompilerVersion = std::move(CompilerVers);
diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h
index 3f585d4d2efaf5..e9c7c96298d683 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -623,7 +623,8 @@ class MCContext {
                                                    unsigned EntrySize);
 
   MCSectionGOFF *getGOFFSection(StringRef Section, SectionKind Kind,
-                                MCSection *Parent, const MCExpr *SubsectionId);
+                                MCSection *Parent = nullptr,
+                                const MCExpr *SubsectionId = nullptr);
 
   MCSectionCOFF *getCOFFSection(StringRef Section, unsigned Characteristics,
                                 SectionKind Kind, StringRef COMDATSymName,
diff --git a/llvm/include/llvm/MC/MCGOFFStreamer.h b/llvm/include/llvm/MC/MCGOFFStreamer.h
index 2345509b161da5..52cde0632483a0 100644
--- a/llvm/include/llvm/MC/MCGOFFStreamer.h
+++ b/llvm/include/llvm/MC/MCGOFFStreamer.h
@@ -24,12 +24,15 @@ class MCGOFFStreamer : public MCObjectStreamer {
 
   ~MCGOFFStreamer() override;
 
-  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
-    return false;
-  }
+  // state management
+  void initSections(bool NoExecStack, const MCSubtargetInfo &STI) override;
+
+  void switchSection(MCSection *Section,
+                     const MCExpr *Subsection = nullptr) override;
+  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override;
   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
                         Align ByteAlignment) override {}
-  void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override {}
+  void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override;
   void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
                     uint64_t Size = 0, Align ByteAlignment = Align(1),
                     SMLoc Loc = SMLoc()) override {}
diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h
index cc4e2bbe246e2d..bb3c3b07947aa3 100644
--- a/llvm/include/llvm/MC/MCSymbolGOFF.h
+++ b/llvm/include/llvm/MC/MCSymbolGOFF.h
@@ -18,10 +18,63 @@
 namespace llvm {
 
 class MCSymbolGOFF : public MCSymbol {
+  Align Alignment;
+
+  mutable StringRef AliasName; // ADA indirect
+
+  enum SymbolFlags : uint16_t {
+    SF_NoRent = 0x01,    // Symbol is no-reentrant.
+    SF_Alias = 0x02,     // Symbol is alias.
+    SF_Hidden = 0x04,    // Symbol is hidden, aka not exported.
+    SF_Weak = 0x08,      // Symbol is weak.
+    SF_OSLinkage = 0x10, // Symbol uses OS linkage.
+  };
+
+  // Shift value for GOFF::ESDExecutable. 3 possible values. 2 bits.
+  static constexpr uint8_t GOFF_Executable_Shift = 6;
+  static constexpr uint8_t GOFF_Executable_Bitmask = 0x03;
+
 public:
   MCSymbolGOFF(const StringMapEntry<bool> *Name, bool IsTemporary)
-      : MCSymbol(SymbolKindGOFF, Name, IsTemporary) {}
+      : MCSymbol(SymbolKindGOFF, Name, IsTemporary), AliasName() {}
   static bool classof(const MCSymbol *S) { return S->isGOFF(); }
+
+  bool hasAliasName() const { return !AliasName.empty(); }
+  void setAliasName(StringRef Name) { AliasName = Name; }
+  StringRef getAliasName() const { return AliasName; }
+
+  void setTemporary(bool Value) { IsTemporary = Value; }
+
+  void setOSLinkage(bool Value = true) const {
+    modifyFlags(Value ? SF_OSLinkage : 0, SF_OSLinkage);
+  }
+  bool isOSLinkage() const { return getFlags() & SF_OSLinkage; }
+
+  void setAlignment(Align Value) { Alignment = Value; }
+  Align getAlignment() const { return Alignment; }
+
+  void setExecutable(GOFF::ESDExecutable Value) const {
+    modifyFlags(Value << GOFF_Executable_Shift,
+                GOFF_Executable_Bitmask << GOFF_Executable_Shift);
+  }
+  GOFF::ESDExecutable getExecutable() const {
+    return static_cast<GOFF::ESDExecutable>(
+        (getFlags() >> GOFF_Executable_Shift) & GOFF_Executable_Bitmask);
+  }
+
+  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 setAlias(bool Value = true) {
+    modifyFlags(Value ? SF_Alias : 0, SF_Alias);
+  }
+  bool isAlias() const { return getFlags() & SF_Alias; }
 };
 } // end namespace llvm
 
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index 15b59421a0f44a..3b4f7faa99d590 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2744,9 +2744,13 @@ MCSection *TargetLoweringObjectFileGOFF::getSectionForLSDA(
 MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
     const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const {
   auto *Symbol = TM.getSymbol(GO);
+  if (Kind.isData())
+    return getContext().getGOFFSection(Symbol->getName(),
+                                       SectionKind::getData());
+
   if (Kind.isBSS())
-    return getContext().getGOFFSection(Symbol->getName(), SectionKind::getBSS(),
-                                       nullptr, nullptr);
+    return getContext().getGOFFSection(Symbol->getName(),
+                                       SectionKind::getBSS());
 
   return getContext().getObjectFileInfo()->getTextSection();
 }
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index addeb6db959693..60ab410e9765e2 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -13,8 +13,13 @@
 #include "llvm/BinaryFormat/GOFF.h"
 #include "llvm/MC/MCAsmLayout.h"
 #include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCGOFFObjectWriter.h"
+#include "llvm/MC/MCSectionGOFF.h"
+#include "llvm/MC/MCSymbolGOFF.h"
 #include "llvm/MC/MCValue.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ConvertEBCDIC.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/Path.h"
@@ -217,13 +222,112 @@ void GOFFOstream::write_impl(const char *Ptr, size_t Size) {
   }
 }
 
+/// \brief Wrapper class for symbols used exclusively for the symbol table in a
+/// GOFF file.
+class GOFFSymbol {
+public:
+  std::string Name;
+  uint32_t EsdId;
+  uint32_t ParentEsdId;
+  const MCSymbolGOFF *MCSym;
+  GOFF::ESDSymbolType SymbolType;
+
+  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_NormalName;
+  GOFF::ESDAmode Amode = GOFF::ESD_AMODE_64;
+  GOFF::ESDRmode Rmode = GOFF::ESD_RMODE_64;
+  GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
+  GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
+  GOFF::ESDAlignment Alignment = GOFF::ESD_ALIGN_Byte;
+  GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented;
+  GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate;
+  GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial;
+  GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
+  GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
+  GOFF::ESDReserveQwords ReservedQwords = GOFF::ESD_RQ_0;
+
+  uint32_t SortKey = 0;
+  uint32_t SectionLength = 0;
+  uint32_t ADAEsdId = 0;
+  bool Indirect = false;
+  bool ForceRent = false;
+  bool Renamable = false;
+  bool ReadOnly = false;
+  uint32_t EASectionEsdId = 0;
+  uint32_t EASectionOffset = 0;
+
+  GOFFSymbol(StringRef Name, GOFF::ESDSymbolType Type, uint32_t EsdID,
+             uint32_t ParentEsdID)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
+        MCSym(nullptr), SymbolType(Type) {}
+  GOFFSymbol() {}
+
+  bool isForceRent() const { return ForceRent; }
+  bool isReadOnly() const { return ReadOnly; }
+  bool isRemovable() const { return false; }
+  bool isExecutable() const { return Executable == GOFF::ESD_EXE_CODE; }
+  bool isExecUnspecified() const {
+    return Executable == GOFF::ESD_EXE_Unspecified;
+  }
+  bool isWeakRef() const { return BindingStrength == GOFF::ESD_BST_Weak; }
+  bool isExternal() const {
+    return (BindingScope == GOFF::ESD_BSC_Library) ||
+           (BindingScope == GOFF::ESD_BSC_ImportExport);
+  }
+
+  void setAlignment(Align A) {
+    // The GOFF alignment is encoded as log_2 value.
+    uint8_t Log = Log2(A);
+    if (Log <= GOFF::ESD_ALIGN_4Kpage)
+      Alignment = static_cast<GOFF::ESDAlignment>(Log);
+    else
+      llvm_unreachable("Unsupported alignment");
+  }
+};
+
+/// \brief Wrapper class for sections used exclusively for representing sections
+/// of the GOFF output that have actual bytes.  This could be a ED or a PR.
+/// Relocations will always have a P-pointer to the ESDID of one of these.
+struct GOFFSection {
+  uint32_t PEsdId;
+  uint32_t REsdId;
+  uint32_t SDEsdId;
+  const MCSectionGOFF *MC;
+
+  GOFFSection(uint32_t PEsdId, uint32_t REsdId, uint32_t SDEsdId)
+      : PEsdId(PEsdId), REsdId(REsdId), SDEsdId(SDEsdId), MC(nullptr) {}
+  GOFFSection(uint32_t PEsdId, uint32_t REsdId, uint32_t SDEsdId,
+              const MCSectionGOFF *MC)
+      : PEsdId(PEsdId), REsdId(REsdId), SDEsdId(SDEsdId), MC(MC) {}
+};
+
+// An MCSymbol may map to up to two GOFF Symbols. The first is the
+// "true" underlying symbol, the second one represents any indirect
+// references to that symbol.
 class GOFFObjectWriter : public MCObjectWriter {
+  typedef DenseMap<MCSymbol const *, uint32_t> SymbolMapType;
+  typedef DenseMap<MCSection const *, GOFFSection> SectionMapType;
+
   // The target specific GOFF writer instance.
   std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter;
 
+  /// Lookup table for MCSymbols to GOFFSymbols.  Needed to determine EsdIds
+  /// of symbols in Relocations.
+  SymbolMapType SymbolMap;
+
+  /// Lookup table for MCSections to GOFFSections.  Needed to determine
+  /// SymbolType on GOFFSymbols that reside in GOFFSections.
+  SectionMapType SectionMap;
+
   // The stream used to write the GOFF records.
   GOFFOstream OS;
 
+  uint32_t EsdCounter = 1;
+
+  // The "root" SD node. This should have ESDID = 1
+  GOFFSymbol RootSD;
+  uint32_t EntryEDEsdId;
+  uint32_t CodeLDEsdId;
+
 public:
   GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
                    raw_pwrite_stream &OS)
@@ -231,20 +335,290 @@ class GOFFObjectWriter : public MCObjectWriter {
 
   ~GOFFObjectWriter() override {}
 
+private:
   // Write GOFF records.
   void writeHeader();
+
+  void writeSymbol(const GOFFSymbol &Symbol, const MCAsmLayout &Layout);
+
+  void writeADAandCodeSectionSymbols(MCAssembler &Asm,
+                                     const MCAsmLayout &Layout);
+  // Write out all ESD Symbols that own a section. GOFF doesn't have
+  // "sections" in the way other object file formats like ELF do; instead
+  // a GOFF section is defined as a triple of ESD Symbols (SD, ED, PR/LD).
+  // The PR/LD symbol should own a TXT record that contains the actual
+  // data of the section.
+  void writeSectionSymbols(MCAssembler &Asm, const MCAsmLayout &Layout);
+
+  void writeText(const MCSectionGOFF *MCSec, uint32_t EsdId, bool IsStructured,
+                 MCAssembler &Asm, const MCAsmLayout &Layout);
+
   void writeEnd();
 
+public:
   // Implementation of the MCObjectWriter interface.
   void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                         const MCFragment *Fragment, const MCFixup &Fixup,
                         MCValue Target, uint64_t &FixedValue) override {}
   void executePostLayoutBinding(MCAssembler &Asm,
-                                const MCAsmLayout &Layout) override {}
+                                const MCAsmLayout &Layout) override;
   uint64_t writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;
+
+private:
+  GOFFSection *createGOFFSection(GOFFSymbol *Pptr, GOFFSymbol *Rptr,
+                                 GOFFSymbol *SD, const MCSectionGOFF *MC);
+  GOFFSymbol createGOFFSymbol(StringRef Name, GOFF::ESDSymbolType Type,
+                              uint32_t ParentEsdId);
+  GOFFSymbol createSDSymbol(StringRef Name);
+  GOFFSymbol createEDSymbol(StringRef Name, uint32_t ParentEsdId);
+  GOFFSymbol createLDSymbol(StringRef Name, uint32_t ParentEsdId);
+  GOFFSymbol createERSymbol(StringRef Name, uint32_t ParentEsdId,
+                            const MCSymbolGOFF *Source = nullptr);
+  GOFFSymbol createPRSymbol(StringRef Name, uint32_t ParentEsdId);
+
+  GOFFSymbol createWSASymbol(uint32_t ParentEsdId, bool ReserveQWords = false);
+
+  // Define the root SD node as well as the symbols that make up the
+  // ADA section.
+  void defineRootSD(MCAssembler &Asm, const MCAsmLayout &Layout);
+
+  void defineSectionSymbols(const MCAssembler &Asm,
+                            const MCSectionGOFF &Section,
+                            const MCAsmLayout &Layout);
+  void writeSymbolDefinedInModule(const MCSymbolGOFF &Symbol, MCAssembler &Asm,
+                                  const MCAsmLayout &Layout);
+  void writeSymbolDeclaredInModule(const MCSymbolGOFF &Symbol, MCAssembler &Asm,
+                                   const MCAsmLayout &Layout);
+
+  // In XPLINK, the ESD Record owning the ADA is always the 4th record, behind
+  // the HDR record, the Root SD, and the ED symbol for the ADA, meaning it
+  // always has EsdId = 3.
+  static constexpr uint32_t ADAEsdId = 3u;
 };
 } // end anonymous namespace
 
+GOFFSymbol GOFFObjectWriter::createGOFFSymbol(StringRef Name,
+                                              GOFF::ESDSymbolType Type,
+                                              uint32_t ParentEsdId) {
+  return GOFFSymbol(Name, Type, EsdCounter++, ParentEsdId);
+}
+
+GOFFSymbol GOFFObjectWriter::createSDSymbol(StringRef Name) {
+  return createGOFFSymbol(Name, GOFF::ESD_ST_SectionDefinition, 0);
+}
+
+GOFFSymbol GOFFObjectWriter::createEDSymbol(StringRef Name,
+                                            uint32_t ParentEsdId) {
+  GOFFSymbol ED =
+      createGOFFSymbol(Name, GOFF::ESD_ST_ElementDefinition, ParentEsdId);
+
+  ED.Alignment = GOFF::ESD_ALIGN_Doubleword;
+  return ED;
+}
+
+GOFFSymbol GOFFObjectWriter::createLDSymbol(StringRef Name,
+                                            uint32_t ParentEsdId) {
+  return createGOFFSymbol(Name, GOFF::ESD_ST_LabelDefinition, ParentEsdId);
+}
+
+GOFFSymbol GOFFObjectWriter::createERSymbol(StringRef Name,
+                                            uint32_t ParentEsdId,
+                                            const MCSymbolGOFF *Source) {
+  GOFFSymbol ER =
+      createGOFFSymbol(Name, GOFF::ESD_ST_ExternalReference, ParentEsdId);
+
+  if (Source) {
+    ER.Linkage = Source->isOSLinkage() ? GOFF::ESDLinkageType::ESD_LT_OS
+                                       : GOFF::ESDLinkageType::ESD_LT_XPLink;
+    ER.Executable = Source->getExecutable();
+    ER.BindingScope = Source->isExternal()
+                          ? GOFF::ESDBindingScope::ESD_BSC_Library
+                          : GOFF::ESDBindingScope::ESD_BSC_Section;
+    ER.BindingStrength = Source->isWeak()
+                             ? GOFF::ESDBindingStrength::ESD_BST_Weak
+                             : GOFF::ESDBindingStrength::ESD_BST_Strong;
+  }
+
+  return ER;
+}
+
+GOFFSymbol GOFFObjectWriter::createPRSymbol(StringRef Name,
+                                            uint32_t ParentEsdId) {
+  return createGOFFSymbol(Name, GOFF::ESD_ST_PartReference, ParentEsdId);
+}
+
+GOFFSymbol GOFFObjectWriter::createWSASymbol(uint32_t ParentEsdId,
+                                             bool ReserveQwords) {
+  const char *WSAClassName = "C_WSA64";
+  GOFFSymbol WSA = createEDSymbol(WSAClassName, ParentEsdId);
+
+  WSA.Executable = GOFF::ESD_EXE_DATA;
+  WSA.TextStyle = GOFF::ESD_TS_ByteOriented;
+  WSA.BindAlgorithm = GOFF::ESD_BA_Merge;
+  WSA.Alignment = GOFF::ESD_ALIGN_Quadword;
+  WSA.LoadBehavior = GOFF::ESD_LB_Deferred;
+  WSA.NameSpace = GOFF::ESD_NS_Parts;
+  WSA.SectionLength = 0;
+  WSA.ReservedQwords = ReserveQwords ? GOFF::ESD_RQ_1 : GOFF::ESD_RQ_0;
+
+  return WSA;
+}
+
+static uint32_t getADASectionLength(MCAssembler &Asm,
+                                    const MCAsmLayout &Layout) {
+  for (MCSection &S : Asm)
+    if (S.getName().equals(".ada"))
+      return Layout.getSectionAddressSize(&S);
+  return 0;
+}
+
+void GOFFObjectWriter::defineRootSD(MCAssembler &Asm,
+                                    const MCAsmLayout &Layout) {
+  StringRef FileName = "";
+  if (!Asm.getFileNames().empty())
+    FileName = sys::path::stem((*(Asm.getFileNames().begin())).first);
+  std::pair<std::string, std::string> CsectNames = Asm.getCsectNames();
+  if (CsectNames.first.empty()) {
+    RootSD = createSDSymbol(FileName.str().append("#C"));
+    RootSD.BindingScope = GOFF::ESD_BSC_Section;
+  } else {
+    RootSD = createSDSymbol(CsectNames.first);
+  }
+  RootSD.Executable = GOFF::ESD_EXE_CODE;
+}
+
+void GOFFObjectWriter::writeSymbolDefinedInModule(const MCSymbolGOFF &Symbol,
+                                                  MCAssembler &Asm,
+                                                  const MCAsmLayout &Layout) {
+  MCSection &Section = Symbol.getSection();
+  SectionKind Kind = Section.getKind();
+  auto &Sec = cast<MCSectionGOFF>(Section);
+
+  StringRef SymbolName = Symbol.getName();...
[truncated]

Copy link
Member

@uweigand uweigand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go through in more detail later, but a couple of general comments first:

  • Some of the information provided in the commit message here should really go as comments into the source as well.
  • This PR seems to have "policy" and "implementation" aspects very closely intertwined. I think from a general maintainability perspective it would be much preferable to implement a clearer separation of concerns. For example, I don't think the section writer should ever look at the section name to decide what to do - instead, whoever creates the section should use generic mechanism like attributes to describe the requirements this particular section has, and then the section writer would just follow those instructions generically.
  • There are a number of GOFF-specific symbol attributes implemented by this patch where I'm wondering if we couldn't use existing generic attributes instead. In particular, the "setExecutable" attribute seems to track the same type of information tracked by the "MCSA_ELF_Type*" symbol attributes. If we could hook into this, we'd benefit from common code already setting these attributes as needed.
  • The patch seems incomplete/partial in certain aspects. E.g. there are various flags that the code attempts to implement, but there is no place where they are ever set. I think it would be better to commit an initial version of this patch without those flags, and then add them back in follow-on patches, together with the places where they're actually used and test cases.
  • The whole GlobalAlias handling seems to duplicate/conflict with this PR [SystemZ][zOS] Override emitGlobalAlias for ADA  #84829 - in either case, I'm still not convinced this is the best approach. Again, it would probably best to leave all that out of this PR and add it later separately.

@@ -136,6 +136,9 @@ class MCAssembler {
// Optional compiler version.
std::string CompilerVersion;

/// GOFF Csect names.
std::pair<std::string, std::string> CsectNames;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be never set to anything.

@@ -18,10 +18,63 @@
namespace llvm {

class MCSymbolGOFF : public MCSymbol {
Align Alignment;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we use alignment information already in the generic MCSymbol?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I've noticed that the MCSymbol only retains alignment information for "common" symbols. Would it be appropriate for everything that ultimately becomes a ED or PR GOFFSymbol to be marked as common? I can't seem to find any information on what is meant by the MC API by a "common" symbol, unless that is a target-defined term in which case that should work fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A "common" symbol is an uninitialized global variable, typically placed in .bss or the equivalent. They're different from other symbols in that the .bss is not laid out by the assembler/compiler (it's all uninitialized, so there would be no point). Instead, only the linker actually assigns offsets to symbols in .bss - that's why the linker needs to know alignment requirements for those.

For other symbols, they're assigned fixed offsets into their section already by the assembler/compiler, which is the point where any alignment requirements are fulfilled. From there on, only the section itself carries any further alignment information that needs to be respected by the linker.

I think it should be straightforward to follow the same approach in GOFF: for ED symbols generated from text sections, use the section alignment; similarly for PR symbols generated from (initialized) data sections. If there are common symbols in .bss, you can generate a PR symbol for those using the common symbol alignment - however, if you already forced all of these into sections of their own, you can instead just use the section alignment as well.

@@ -18,10 +18,63 @@
namespace llvm {

class MCSymbolGOFF : public MCSymbol {
Align Alignment;

mutable StringRef AliasName; // ADA indirect
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments on Alias handling in general. I think this would be better postponed to a separate PR.

static bool classof(const MCSymbol *S) { return S->isGOFF(); }

bool hasAliasName() const { return !AliasName.empty(); }
void setAliasName(StringRef Name) { AliasName = Name; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing ever calls this.

void setAliasName(StringRef Name) { AliasName = Name; }
StringRef getAliasName() const { return AliasName; }

void setTemporary(bool Value) { IsTemporary = Value; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need this - why is the "temporary" flag not set correctly to begin with?

@@ -1014,6 +1016,8 @@ void SystemZAsmPrinter::emitADASection() {
MCSymbolRefExpr::create(Sym, OutContext),
OutContext),
PointerSize);
Sym->setExecutable(GOFF::ESD_EXE_CODE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above about setExecutable ... it would be preferable if we didn't have to do that all over the place, but could rely on common code setting the symbol types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it does look like GOFF::ESD_EXE_CODE basically tracks the same information as MCSA_ELF_TypeFunction (and ESD_EXE_DATA essentially being analogous to that bit not being set).

Would you prefer just using that field, or creating a new MCSymbolAttribute? It feels kind of odd to have to use an attribute with ELF in its name for a GOFF-specific thing, but I'd also rather not add a new field to the MCSymbolAttribute if only one target will use it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be OK to use MCSA_ELF_Type. The Wasm format also uses those, even though it is not exactly ELF ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, can we take a step back?

Is if the problem is us setting the Executable flag all over the place, or if the problem is that it seems somewhat similar to MCSA_ELF_TypeFunction and MCSA_ELF_TypeObject.

If it's the former, I can see how it is problematic. I did a quick test and it seems that most places actually set it redundantly, so that definitely needs to be cleaned up. My first thought is to override doFinalization in the SystemZAsmPrinter, and (for zOS targets only), set the appropriate flag as needed there (and nowhere else).

If it's the latter, then we can piggyback off of that flag, but I don't think we can exactly rely on common code, at least without modifying it somewhat. That flag gets set in the following places (by common code): emitFunctionHeader, emitFunctionEntryLabel, and emitGlobalAlias.

The problem is that we also need to set that flag for functions that are declared but not defined in the current compilation unit, and none of those functions mentioned above are called for function declarations, so we wouldn't be able to entirely piggyback off common code anyways, and if we attempted to do so we would still need to set it in multiple places.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess in the end the interface to the GOFF writer should closely follow the HLASM language. Eventually, we want to able to create both a valid GOFF object file and a valid HLASM assembler output file from the same AsmWriter calls.

So the question becomes, how would setting the Executable flag for external symbols be implemented in HLASM source? Would this be inferred from the symbol's use inside a VCon or RCon expression? Or would we have to use an explicit HLASM directive like XATTR REF(CODE) ? This should guide the design of the interface.

If an explicit directive would be needed, this should be implemented via a corresponding AsmWriter call (just like the MCSA_ELF_Type* attributes correspond to the .type directive in ELF assemblers). In any case, I'd prefer the decision to be made close to the place of usage, rather than in a completely separate pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be the latter (ie. an explicit directive).

The executable flag would look something like SYMBOLNAME XATTR REFERENCE(CODE)

The data flag would be SYMBOLNAME XATTR REFERENCE(DATA)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this a bit more, I actually like the current approach of setting this flag in doFinalization once for every external symbol. (I think you should probably exclude internal symbols there, however.)

Given that this behavior is actually quite a bit different from the ELF .type handling (which applies only to defined symbols), however, I now think it would indeed be better to define some new MCSA_GOFF_ attributes rather than reusing the ELF_Type attributes. We'll need other GOFF-specific symbol attributes anyway.

@@ -1014,6 +1016,8 @@ void SystemZAsmPrinter::emitADASection() {
MCSymbolRefExpr::create(Sym, OutContext),
OutContext),
PointerSize);
Sym->setExecutable(GOFF::ESD_EXE_CODE);
Sym->setTemporary(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When can this ever be necessary? It's a function symbol, this should never have been marked as temporary in the first place ...

}

void SystemZAsmPrinter::emitGlobalAlias(const Module &M,
const GlobalAlias &GA) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See general comment about GlobalAlias.

Sym = cast<MCSymbolGOFF>(getSymbol(GValue));
// TODO: Is it necessary to assert if CV is of type GlobalIFunc?

if (IsFunc) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow the logic, but can we ever get here with a NULL Sym?

@@ -0,0 +1,252 @@
; RUN: llc %s -mtriple s390x-ibm-zos -filetype=obj -o - | od -v -Ax -tx1 | FileCheck --ignore-case %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use obj2yaml for this type of tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not currently (obj2yaml doesn't support TXT records yet), but once that PR is in the intention is to augment the existing tests instead of relying on od.

Copy link

github-actions bot commented Mar 25, 2024

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff ba5dad35fbefec456eb9da0f7c5a0ad983a268b2 c1bfe45a205da932e83cb6be3db8ca9d836632cc -- llvm/include/llvm/BinaryFormat/GOFF.h llvm/include/llvm/CodeGen/AsmPrinter.h llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h llvm/include/llvm/MC/MCContext.h llvm/include/llvm/MC/MCDirectives.h llvm/include/llvm/MC/MCGOFFStreamer.h llvm/include/llvm/MC/MCSectionGOFF.h llvm/include/llvm/MC/MCSymbolGOFF.h llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp llvm/lib/MC/GOFFObjectWriter.cpp llvm/lib/MC/MCAsmStreamer.cpp llvm/lib/MC/MCContext.cpp llvm/lib/MC/MCELFStreamer.cpp llvm/lib/MC/MCGOFFStreamer.cpp llvm/lib/MC/MCMachOStreamer.cpp llvm/lib/MC/MCObjectFileInfo.cpp llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.cpp llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCExpr.h llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp llvm/lib/Target/SystemZ/SystemZAsmPrinter.h
View the diff from clang-format here.
diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h
index 46144b722e..94cf931283 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -312,8 +312,8 @@ private:
           HasParentSection(HasParentSection) {}
 
     bool operator<(const GOFFSectionKey &Other) const {
-      if (SectionType == GOFF::GOFFSectionType::Other
-          && SectionType == GOFF::GOFFSectionType::Other) {
+      if (SectionType == GOFF::GOFFSectionType::Other &&
+          SectionType == GOFF::GOFFSectionType::Other) {
         return SectionName < Other.SectionName;
       }
       return SectionType < Other.SectionType;
@@ -654,24 +654,23 @@ public:
                                                    unsigned Flags,
                                                    unsigned EntrySize);
 
-  MCSectionGOFF *getGOFFSection(StringRef Section, SectionKind Kind,
-                                GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented,
-                                GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate,
-                                GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial,
-                                GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified,
-                                bool isRooted = false,
-                                const MCSymbolGOFF *TextOwner = nullptr);
-
-  MCSectionGOFF *
-  getGOFFSection(StringRef Section, SectionKind Kind,
-                 MCSection *Parent = nullptr,
-                 const MCExpr *SubsectionId = nullptr,
-                 GOFF::GOFFSectionType SectionType = GOFF::Other,
-                 GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented,
-                 GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate,
-                 GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial,
-                 GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified,
-                 bool isRooted = false, const MCSymbolGOFF * TextOwner = nullptr);
+  MCSectionGOFF *getGOFFSection(
+      StringRef Section, SectionKind Kind,
+      GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented,
+      GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate,
+      GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial,
+      GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified,
+      bool isRooted = false, const MCSymbolGOFF *TextOwner = nullptr);
+
+  MCSectionGOFF *getGOFFSection(
+      StringRef Section, SectionKind Kind, MCSection *Parent = nullptr,
+      const MCExpr *SubsectionId = nullptr,
+      GOFF::GOFFSectionType SectionType = GOFF::Other,
+      GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented,
+      GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate,
+      GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial,
+      GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified,
+      bool isRooted = false, const MCSymbolGOFF *TextOwner = nullptr);
 
   MCSectionCOFF *getCOFFSection(StringRef Section, unsigned Characteristics,
                                 SectionKind Kind, StringRef COMDATSymName,
diff --git a/llvm/include/llvm/MC/MCSectionGOFF.h b/llvm/include/llvm/MC/MCSectionGOFF.h
index 5871b61fe6..0f534096db 100644
--- a/llvm/include/llvm/MC/MCSectionGOFF.h
+++ b/llvm/include/llvm/MC/MCSectionGOFF.h
@@ -49,20 +49,25 @@ class MCSectionGOFF final : public MCSection {
   /// a LD or PR Symbol.
   bool TextOwnedByED = false;
 
-  /// TextOwner - Valid if owned the text record containing the body of this section
-  /// is not owned by an ED Symbol. The MCSymbol that represents the part or label that
-  /// actually owns the TXT Record.
+  /// TextOwner - Valid if owned the text record containing the body of this
+  /// section is not owned by an ED Symbol. The MCSymbol that represents the
+  /// part or label that actually owns the TXT Record.
   const MCSymbolGOFF *TextOwner = nullptr;
 
   friend class MCContext;
   MCSectionGOFF(StringRef Name, SectionKind K, MCSection *P, const MCExpr *Sub)
       : MCSection(SV_GOFF, Name, K, nullptr), Parent(P), SubsectionId(Sub) {}
-  
+
   MCSectionGOFF(StringRef Name, SectionKind K, MCSection *P, const MCExpr *Sub,
-                GOFF::ESDTextStyle TextStyle, GOFF::ESDBindingAlgorithm BindAlgorithm,
-                GOFF::ESDLoadingBehavior LoadBehavior, GOFF::ESDBindingScope BindingScope, bool IsRooted, const MCSymbolGOFF *TextOwner)
+                GOFF::ESDTextStyle TextStyle,
+                GOFF::ESDBindingAlgorithm BindAlgorithm,
+                GOFF::ESDLoadingBehavior LoadBehavior,
+                GOFF::ESDBindingScope BindingScope, bool IsRooted,
+                const MCSymbolGOFF *TextOwner)
       : MCSection(SV_GOFF, Name, K, nullptr), Parent(P), SubsectionId(Sub),
-        TextStyle(TextStyle), BindAlgorithm(BindAlgorithm), LoadBehavior(LoadBehavior), BindingScope(BindingScope), IsRooted(IsRooted), TextOwner(TextOwner) {}
+        TextStyle(TextStyle), BindAlgorithm(BindAlgorithm),
+        LoadBehavior(LoadBehavior), BindingScope(BindingScope),
+        IsRooted(IsRooted), TextOwner(TextOwner) {}
 
   MCSectionGOFF(StringRef Name, SectionKind K, MCSection *P, const MCExpr *Sub,
                 GOFF::GOFFSectionType Type)
@@ -103,7 +108,9 @@ public:
   bool isB_IDRL() const { return Type == GOFF::B_IDRL; }
 
   GOFF::ESDTextStyle getTextStyle() const { return TextStyle; }
-  GOFF::ESDBindingAlgorithm getBindingAlgorithm() const { return BindAlgorithm; }
+  GOFF::ESDBindingAlgorithm getBindingAlgorithm() const {
+    return BindAlgorithm;
+  }
   GOFF::ESDLoadingBehavior getLoadBehavior() const { return LoadBehavior; }
   GOFF::ESDBindingScope getBindingScope() const { return BindingScope; }
   bool getRooted() const { return IsRooted; }
@@ -117,18 +124,17 @@ public:
   // Return the name of the External Definition (ED) used to represent this
   // MCSectionGOFF in the object file.
   std::string getExternalDefinitionName() const {
-    switch (Type)
-    {
-      case GOFF::GOFFSectionType::Code:
-        return "C_CODE64";
-      case GOFF::GOFFSectionType::Static:
-        return "C_WSA64";
-      case GOFF::GOFFSectionType::PPA2Offset:
-        return "C_@@QPPA2";
-      case GOFF::GOFFSectionType::B_IDRL:
-        return "B_IDRL";
-      case GOFF::GOFFSectionType::Other:
-        return "C_WSA64";
+    switch (Type) {
+    case GOFF::GOFFSectionType::Code:
+      return "C_CODE64";
+    case GOFF::GOFFSectionType::Static:
+      return "C_WSA64";
+    case GOFF::GOFFSectionType::PPA2Offset:
+      return "C_@@QPPA2";
+    case GOFF::GOFFSectionType::B_IDRL:
+      return "B_IDRL";
+    case GOFF::GOFFSectionType::Other:
+      return "C_WSA64";
     }
     return "";
   }
@@ -155,9 +161,7 @@ public:
     return getName().str();
   }
 
-  bool isReadOnly() const {
-    return isCode() || isPPA2Offset() || isB_IDRL();
-  }
+  bool isReadOnly() const { return isCode() || isPPA2Offset() || isB_IDRL(); }
 
   GOFF::ESDNameSpaceId getNameSpace() const {
     return isB_IDRL() ? GOFF::ESD_NS_NormalName : GOFF::ESD_NS_Parts;
diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
index c0618ca36e..7fa831a273 100644
--- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -2740,11 +2740,9 @@ MCSection *TargetLoweringObjectFileGOFF::getSectionForLSDA(
   std::string Name = ".gcc_exception_table." + F.getName().str();
   const auto *Symbol = cast<MCSymbolGOFF>(&FnSym);
   return getContext().getGOFFSection(Name, SectionKind::getData(),
-                                         GOFF::ESD_TS_ByteOriented,
-                                         GOFF::ESD_BA_Merge,
-                                         GOFF::ESD_LB_Deferred,
-                                         GOFF::ESD_BSC_Section,
-                                         true, Symbol);
+                                     GOFF::ESD_TS_ByteOriented,
+                                     GOFF::ESD_BA_Merge, GOFF::ESD_LB_Deferred,
+                                     GOFF::ESD_BSC_Section, true, Symbol);
 }
 
 MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
@@ -2752,15 +2750,12 @@ MCSection *TargetLoweringObjectFileGOFF::SelectSectionForGlobal(
   auto *Symbol = cast<MCSymbolGOFF>(TM.getSymbol(GO));
   if (Kind.isData() || Kind.isBSS()) {
     auto BindingScope = Symbol->isExternal()
-                          ? (Symbol->isExported() ? GOFF::ESD_BSC_ImportExport
-                                                 : GOFF::ESD_BSC_Library)
-                          : GOFF::ESD_BSC_Section;
-    return getContext().getGOFFSection(Symbol->getName(), Kind,
-                                       GOFF::ESD_TS_ByteOriented,
-                                       GOFF::ESD_BA_Merge,
-                                       GOFF::ESD_LB_Deferred,
-                                       BindingScope,
-                                       false, Symbol);
+                            ? (Symbol->isExported() ? GOFF::ESD_BSC_ImportExport
+                                                    : GOFF::ESD_BSC_Library)
+                            : GOFF::ESD_BSC_Section;
+    return getContext().getGOFFSection(
+        Symbol->getName(), Kind, GOFF::ESD_TS_ByteOriented, GOFF::ESD_BA_Merge,
+        GOFF::ESD_LB_Deferred, BindingScope, false, Symbol);
   }
   return getContext().getObjectFileInfo()->getTextSection();
 }
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index 123dbca206..31f7b1f40e 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -361,14 +361,17 @@ private:
     return nullptr;
   }
 
-  std::pair<GOFFSymbol, GOFFSymbol> getSDEDSymbolsForSection(const MCSectionGOFF& GSection, const MCAsmLayout Layout);
+  std::pair<GOFFSymbol, GOFFSymbol>
+  getSDEDSymbolsForSection(const MCSectionGOFF &GSection,
+                           const MCAsmLayout Layout);
 
   // Write out all ESD Symbols that make up a section. GOFF doesn't have
   // "sections" in the way other object file formats like ELF do; instead
   // a GOFF section is defined as a triple of ESD Symbols (SD, ED, PR/LD).
   // The ED or PR symbol should own a TXT Record that contains the contents
   // of the section itself
-  void writeSectionSymbols(const MCSectionGOFF &GSection, MCAssembler &Asm, const MCAsmLayout &Layout);
+  void writeSectionSymbols(const MCSectionGOFF &GSection, MCAssembler &Asm,
+                           const MCAsmLayout &Layout);
 
   void writeText(const MCSectionGOFF *MCSec, uint32_t EsdId,
                  GOFF::TXTRecordStyle RecordStyle, MCAssembler &Asm,
@@ -865,18 +868,19 @@ void GOFFObjectWriter::writeSymbol(const GOFFSymbol &Symbol,
   OS.write(Name.data(), NameLength); // Name
 }
 
-std::pair<GOFFSymbol, GOFFSymbol> GOFFObjectWriter::getSDEDSymbolsForSection(const MCSectionGOFF& GSection, const MCAsmLayout Layout) {
-  GOFFSymbol SD = GSection.getRooted() ? RootSD : createSDSymbol(GSection.getName());
-
-  GOFFSymbol ED = createEDSymbol(GSection.getExternalDefinitionName(), SD.EsdId, Layout.getSectionAddressSize(&GSection),
-                                 GSection.isCode() ? GOFF::ESD_EXE_CODE : GOFF::ESD_EXE_DATA,
-                                 GSection.getBindingScope(),
-                                 GSection.getNameSpace(),
-                                 GSection.getBindingAlgorithm(),
-                                 GSection.isReadOnly(),
-                                 GSection.getTextStyle(),
-                                 GSection.getLoadBehavior()
-                                 );
+std::pair<GOFFSymbol, GOFFSymbol>
+GOFFObjectWriter::getSDEDSymbolsForSection(const MCSectionGOFF &GSection,
+                                           const MCAsmLayout Layout) {
+  GOFFSymbol SD =
+      GSection.getRooted() ? RootSD : createSDSymbol(GSection.getName());
+
+  GOFFSymbol ED = createEDSymbol(
+      GSection.getExternalDefinitionName(), SD.EsdId,
+      Layout.getSectionAddressSize(&GSection),
+      GSection.isCode() ? GOFF::ESD_EXE_CODE : GOFF::ESD_EXE_DATA,
+      GSection.getBindingScope(), GSection.getNameSpace(),
+      GSection.getBindingAlgorithm(), GSection.isReadOnly(),
+      GSection.getTextStyle(), GSection.getLoadBehavior());
   return std::make_pair(SD, ED);
 }
 
@@ -895,7 +899,8 @@ void GOFFObjectWriter::writeSectionSymbols(const MCSectionGOFF &GSection,
     LD.BindingScope = GOFF::ESD_BSC_Section;
     EntryEDEsdId = SDEDSymbols.second.EsdId;
     CodeLDEsdId = LD.EsdId;
-    GOFFSection GoffSec = GOFFSection(LD.EsdId, SDEDSymbols.second.EsdId, SDEDSymbols.first.EsdId);
+    GOFFSection GoffSec = GOFFSection(LD.EsdId, SDEDSymbols.second.EsdId,
+                                      SDEDSymbols.first.EsdId);
     writeSymbol(LD, Layout);
   } else if (!GSection.isTextOwnedByED()) {
     auto GSym = GSection.getTextOwner();
@@ -906,21 +911,21 @@ void GOFFObjectWriter::writeSectionSymbols(const MCSectionGOFF &GSection,
       IsOSLinkage = GSym.value()->isOSLinkage();
     }
     std::string ADAName = FileName + "#S";
-    GOFFSymbol PR = createPRSymbol(GSection.isStatic() ? ADAName : GSection.getTextOwnerName(),
-                                   SDEDSymbols.second.EsdId,
-                                   GOFF::ESD_NS_Parts,
-                                   Executability,
-                                   GOFFSymbol::setGOFFAlignment(GSection.getAlign()),
-                                   GOFF::ESD_BSC_Section,
-                                   Layout.getSectionAddressSize(&GSection),
-                                   GSection.getLoadBehavior(),
-                                   IsOSLinkage ? GOFF::ESD_LT_OS : GOFF::ESD_LT_XPLink);
-    ADAPREsdId = PR.EsdId; 
+    GOFFSymbol PR = createPRSymbol(
+        GSection.isStatic() ? ADAName : GSection.getTextOwnerName(),
+        SDEDSymbols.second.EsdId, GOFF::ESD_NS_Parts, Executability,
+        GOFFSymbol::setGOFFAlignment(GSection.getAlign()),
+        GOFF::ESD_BSC_Section, Layout.getSectionAddressSize(&GSection),
+        GSection.getLoadBehavior(),
+        IsOSLinkage ? GOFF::ESD_LT_OS : GOFF::ESD_LT_XPLink);
+    ADAPREsdId = PR.EsdId;
     writeSymbol(PR, Layout);
-    GOFFSection GoffSec = GOFFSection(PR.EsdId, SDEDSymbols.second.EsdId, SDEDSymbols.first.EsdId);
+    GOFFSection GoffSec = GOFFSection(PR.EsdId, SDEDSymbols.second.EsdId,
+                                      SDEDSymbols.first.EsdId);
     SectionMap.insert(std::make_pair(&GSection, GoffSec));
   } else {
-    GOFFSection GoffSec = GOFFSection(SDEDSymbols.second.EsdId, 0, SDEDSymbols.first.EsdId);
+    GOFFSection GoffSec =
+        GOFFSection(SDEDSymbols.second.EsdId, 0, SDEDSymbols.first.EsdId);
     SectionMap.insert(std::make_pair(&GSection, GoffSec));
   }
 }
@@ -1023,8 +1028,9 @@ uint64_t GOFFObjectWriter::writeObject(MCAssembler &Asm,
   writeHeader();
   writeSymbol(RootSD, Layout);
 
-  // The static and code sections must be written first because the symbols that make up the aforementioned sections
-  // are referenced by several other symbols/sections.
+  // The static and code sections must be written first because the symbols that
+  // make up the aforementioned sections are referenced by several other
+  // symbols/sections.
   writeSectionSymbols(*getStaticSection(Asm), Asm, Layout);
   writeSectionSymbols(*getCodeSection(Asm), Asm, Layout);
 
diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp
index deb3ad40bd..8232d3f76d 100644
--- a/llvm/lib/MC/MCContext.cpp
+++ b/llvm/lib/MC/MCContext.cpp
@@ -651,25 +651,22 @@ MCContext::getELFUniqueIDForEntsize(StringRef SectionName, unsigned Flags,
                                       : std::nullopt;
 }
 
-MCSectionGOFF *MCContext::getGOFFSection(StringRef Section, SectionKind Kind,
-                                         GOFF::ESDTextStyle TextStyle,
-                                         GOFF::ESDBindingAlgorithm BindAlgorithm,
-                                         GOFF::ESDLoadingBehavior LoadBehavior,
-                                         GOFF::ESDBindingScope BindingScope,
-                                         bool isRooted, const MCSymbolGOFF *TextOwner) {
-  return getGOFFSection(Section, Kind, nullptr, nullptr, GOFF::GOFFSectionType::Other,
-  TextStyle, BindAlgorithm, LoadBehavior, BindingScope, isRooted, TextOwner);
-}
-
-MCSectionGOFF *MCContext::getGOFFSection(StringRef Section, SectionKind Kind,
-                                         MCSection *Parent,
-                                         const MCExpr *SubsectionId,
-                                         GOFF::GOFFSectionType SectionType,
-                                         GOFF::ESDTextStyle TextStyle,
-                                         GOFF::ESDBindingAlgorithm BindAlgorithm,
-                                         GOFF::ESDLoadingBehavior LoadBehavior,
-                                         GOFF::ESDBindingScope BindingScope,
-                                         bool isRooted, const MCSymbolGOFF *TextOwner) {
+MCSectionGOFF *MCContext::getGOFFSection(
+    StringRef Section, SectionKind Kind, GOFF::ESDTextStyle TextStyle,
+    GOFF::ESDBindingAlgorithm BindAlgorithm,
+    GOFF::ESDLoadingBehavior LoadBehavior, GOFF::ESDBindingScope BindingScope,
+    bool isRooted, const MCSymbolGOFF *TextOwner) {
+  return getGOFFSection(Section, Kind, nullptr, nullptr,
+                        GOFF::GOFFSectionType::Other, TextStyle, BindAlgorithm,
+                        LoadBehavior, BindingScope, isRooted, TextOwner);
+}
+
+MCSectionGOFF *MCContext::getGOFFSection(
+    StringRef Section, SectionKind Kind, MCSection *Parent,
+    const MCExpr *SubsectionId, GOFF::GOFFSectionType SectionType,
+    GOFF::ESDTextStyle TextStyle, GOFF::ESDBindingAlgorithm BindAlgorithm,
+    GOFF::ESDLoadingBehavior LoadBehavior, GOFF::ESDBindingScope BindingScope,
+    bool isRooted, const MCSymbolGOFF *TextOwner) {
   // Do the lookup. If we don't have a hit, return a new section.
   auto IterBool = GOFFUniquingMap.insert(std::make_pair(
       GOFFSectionKey{Section.str(), SectionType, (Parent != nullptr)},
@@ -684,8 +681,9 @@ MCSectionGOFF *MCContext::getGOFFSection(StringRef Section, SectionKind Kind,
     GOFFSection = new (GOFFAllocator.Allocate())
         MCSectionGOFF(CachedName, Kind, Parent, SubsectionId, SectionType);
   else
-    GOFFSection = new (GOFFAllocator.Allocate())
-        MCSectionGOFF(CachedName, Kind, Parent, SubsectionId, TextStyle, BindAlgorithm, LoadBehavior, BindingScope, isRooted, TextOwner);
+    GOFFSection = new (GOFFAllocator.Allocate()) MCSectionGOFF(
+        CachedName, Kind, Parent, SubsectionId, TextStyle, BindAlgorithm,
+        LoadBehavior, BindingScope, isRooted, TextOwner);
   Iter->second = GOFFSection;
 
   return GOFFSection;

Copy link

✅ With the latest revision this PR passed the Python code formatter.

* Got rid of "alignment" field on MCSymbolGOFF
* No more checking for 'main'
@redstar
Copy link
Member

redstar commented Apr 15, 2024

Due to some changes, I have to take over this work from Neumann. However, GitHub does not allow to transfer the ownership of a PR. I am currently going through the comments, and try to understand how to continue.

@redstar
Copy link
Member

redstar commented May 2, 2024

I decided to tackle the testing problem first. See #90871 for the first part of obj2yaml.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants