[llvm] [GOFF] Add writing of text records (PR #137235)

Kai Nacke via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 26 10:49:19 PDT 2025


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

>From 78356cd5b19b1767b849168cee28e1b4b958cc69 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Wed, 9 Apr 2025 15:08:52 -0400
Subject: [PATCH] [GOFF] Add writing of text records

Sections which are not allowed to carry data are marked as virtual.
Only complication when writing out the text is that it must be
written in chunks of 32k-1 bytes, which is done by having a wrapper
stream writing those records.
Data of BSS sections is not written, since the contents is known to
be zero. Instead, the fill byte value is used.
---
 llvm/include/llvm/MC/MCContext.h           |  3 +-
 llvm/include/llvm/MC/MCSectionGOFF.h       | 47 ++++++++-----
 llvm/lib/MC/GOFFObjectWriter.cpp           | 78 ++++++++++++++++++++++
 llvm/lib/MC/MCContext.cpp                  | 18 +++--
 llvm/test/CodeGen/SystemZ/zos-section-1.ll | 27 ++++++--
 llvm/test/CodeGen/SystemZ/zos-section-2.ll | 30 +++++++--
 6 files changed, 171 insertions(+), 32 deletions(-)

diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h
index 50085c436c63c..d7b81af4a785a 100644
--- a/llvm/include/llvm/MC/MCContext.h
+++ b/llvm/include/llvm/MC/MCContext.h
@@ -359,7 +359,8 @@ class MCContext {
 
   template <typename TAttr>
   MCSectionGOFF *getGOFFSection(SectionKind Kind, StringRef Name,
-                                TAttr SDAttributes, MCSection *Parent);
+                                TAttr SDAttributes, MCSection *Parent,
+                                bool IsVirtual);
 
   /// Map of currently defined macros.
   StringMap<MCAsmMacro> MacroMap;
diff --git a/llvm/include/llvm/MC/MCSectionGOFF.h b/llvm/include/llvm/MC/MCSectionGOFF.h
index b8b8cf112a34d..b2ca74c3ba78a 100644
--- a/llvm/include/llvm/MC/MCSectionGOFF.h
+++ b/llvm/include/llvm/MC/MCSectionGOFF.h
@@ -39,6 +39,9 @@ class MCSectionGOFF final : public MCSection {
   // The type of this section.
   GOFF::ESDSymbolType SymbolType;
 
+  // This section is a BSS section.
+  unsigned IsBSS : 1;
+
   // Indicates that the PR symbol needs to set the length of the section to a
   // non-zero value. This is only a problem with the ADA PR - the binder will
   // generate an error in this case.
@@ -50,26 +53,26 @@ class MCSectionGOFF final : public MCSection {
   friend class MCContext;
   friend class MCSymbolGOFF;
 
-  MCSectionGOFF(StringRef Name, SectionKind K, GOFF::SDAttr SDAttributes,
-                MCSectionGOFF *Parent)
-      : MCSection(SV_GOFF, Name, K.isText(), /*IsVirtual=*/false, nullptr),
+  MCSectionGOFF(StringRef Name, SectionKind K, bool IsVirtual,
+                GOFF::SDAttr SDAttributes, MCSectionGOFF *Parent)
+      : MCSection(SV_GOFF, Name, K.isText(), IsVirtual, nullptr),
         Parent(Parent), SDAttributes(SDAttributes),
-        SymbolType(GOFF::ESD_ST_SectionDefinition), RequiresNonZeroLength(0),
-        Emitted(0) {}
+        SymbolType(GOFF::ESD_ST_SectionDefinition), IsBSS(K.isBSS()),
+        RequiresNonZeroLength(0), Emitted(0) {}
 
-  MCSectionGOFF(StringRef Name, SectionKind K, GOFF::EDAttr EDAttributes,
-                MCSectionGOFF *Parent)
-      : MCSection(SV_GOFF, Name, K.isText(), /*IsVirtual=*/false, nullptr),
+  MCSectionGOFF(StringRef Name, SectionKind K, bool IsVirtual,
+                GOFF::EDAttr EDAttributes, MCSectionGOFF *Parent)
+      : MCSection(SV_GOFF, Name, K.isText(), IsVirtual, nullptr),
         Parent(Parent), EDAttributes(EDAttributes),
-        SymbolType(GOFF::ESD_ST_ElementDefinition), RequiresNonZeroLength(0),
-        Emitted(0) {}
+        SymbolType(GOFF::ESD_ST_ElementDefinition), IsBSS(K.isBSS()),
+        RequiresNonZeroLength(0), Emitted(0) {}
 
-  MCSectionGOFF(StringRef Name, SectionKind K, GOFF::PRAttr PRAttributes,
-                MCSectionGOFF *Parent)
-      : MCSection(SV_GOFF, Name, K.isText(), /*IsVirtual=*/false, nullptr),
+  MCSectionGOFF(StringRef Name, SectionKind K, bool IsVirtual,
+                GOFF::PRAttr PRAttributes, MCSectionGOFF *Parent)
+      : MCSection(SV_GOFF, Name, K.isText(), IsVirtual, nullptr),
         Parent(Parent), PRAttributes(PRAttributes),
-        SymbolType(GOFF::ESD_ST_PartReference), RequiresNonZeroLength(0),
-        Emitted(0) {}
+        SymbolType(GOFF::ESD_ST_PartReference), IsBSS(K.isBSS()),
+        RequiresNonZeroLength(0), Emitted(0) {}
 
 public:
   void printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
@@ -81,6 +84,9 @@ class MCSectionGOFF final : public MCSection {
   // Return the parent section.
   MCSectionGOFF *getParent() const { return Parent; }
 
+  // Returns true if this is a BSS section.
+  bool isBSS() const { return IsBSS; }
+
   // Returns the type of this section.
   GOFF::ESDSymbolType getSymbolType() const { return SymbolType; }
 
@@ -102,6 +108,17 @@ class MCSectionGOFF final : public MCSection {
     return PRAttributes;
   }
 
+  // Returns the text style for a section. Only defined for ED and PR sections.
+  GOFF::ESDTextStyle getTextStyle() const {
+    assert(isED() || isPR() || isVirtualSection() && "Expect ED or PR section");
+    if (isED())
+      return EDAttributes.TextStyle;
+    if (isPR())
+      return getParent()->getEDAttributes().TextStyle;
+    // Virtual sections have no data, so byte orientation is fine.
+    return GOFF::ESD_TS_ByteOriented;
+  }
+
   bool requiresNonZeroLength() const { return RequiresNonZeroLength; }
 
   void setName(StringRef SectionName) { Name = SectionName; }
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index 1f3c131289b49..1871f5fe507e2 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -275,6 +275,7 @@ class GOFFWriter {
 
   void writeHeader();
   void writeSymbol(const GOFFSymbol &Symbol);
+  void writeText(const MCSectionGOFF *MC);
   void writeEnd();
 
   void defineSectionSymbols(const MCSectionGOFF &Section);
@@ -405,6 +406,80 @@ void GOFFWriter::writeSymbol(const GOFFSymbol &Symbol) {
   OS.write(Name.data(), NameLength); // Name
 }
 
+namespace {
+/// Adapter stream to write a text section.
+class TextStream : public raw_ostream {
+  /// The underlying GOFFOstream.
+  GOFFOstream &OS;
+
+  /// The buffer size is the maximum number of bytes in a TXT section.
+  static constexpr size_t BufferSize = GOFF::MaxDataLength;
+
+  /// Static allocated buffer for the stream, used by the raw_ostream class. The
+  /// buffer is sized to hold the payload of a logical TXT record.
+  char Buffer[BufferSize];
+
+  /// The offset for the next TXT record. This is equal to the number of bytes
+  /// written.
+  size_t Offset;
+
+  /// The Esdid of the GOFF section.
+  const uint32_t EsdId;
+
+  /// The record style.
+  const GOFF::ESDTextStyle RecordStyle;
+
+  /// See raw_ostream::write_impl.
+  void write_impl(const char *Ptr, size_t Size) override;
+
+  uint64_t current_pos() const override { return Offset; }
+
+public:
+  explicit TextStream(GOFFOstream &OS, uint32_t EsdId,
+                      GOFF::ESDTextStyle RecordStyle)
+      : OS(OS), Offset(0), EsdId(EsdId), RecordStyle(RecordStyle) {
+    SetBuffer(Buffer, sizeof(Buffer));
+  }
+
+  ~TextStream() { flush(); }
+};
+} // namespace
+
+void TextStream::write_impl(const char *Ptr, size_t Size) {
+  size_t WrittenLength = 0;
+
+  // We only have signed 32bits of offset.
+  if (Offset + Size > std::numeric_limits<int32_t>::max())
+    report_fatal_error("TXT section too large");
+
+  while (WrittenLength < Size) {
+    size_t ToWriteLength =
+        std::min(Size - WrittenLength, size_t(GOFF::MaxDataLength));
+
+    OS.newRecord(GOFF::RT_TXT);
+    OS.writebe<uint8_t>(GOFF::Flags(4, 4, RecordStyle)); // Text Record Style
+    OS.writebe<uint32_t>(EsdId);                         // Element ESDID
+    OS.writebe<uint32_t>(0);                             // Reserved
+    OS.writebe<uint32_t>(static_cast<uint32_t>(Offset)); // Offset
+    OS.writebe<uint32_t>(0);                      // Text Field True Length
+    OS.writebe<uint16_t>(0);                      // Text Encoding
+    OS.writebe<uint16_t>(ToWriteLength);          // Data Length
+    OS.write(Ptr + WrittenLength, ToWriteLength); // Data
+
+    WrittenLength += ToWriteLength;
+    Offset += ToWriteLength;
+  }
+}
+
+void GOFFWriter::writeText(const MCSectionGOFF *Section) {
+  // A BSS section contains only zeros, no need to write this.
+  if (Section->isBSS())
+    return;
+
+  TextStream S(OS, Section->getOrdinal(), Section->getTextStyle());
+  Asm.writeSectionData(S, Section);
+}
+
 void GOFFWriter::writeEnd() {
   uint8_t F = GOFF::END_EPR_None;
   uint8_t AMODE = 0;
@@ -428,6 +503,9 @@ uint64_t GOFFWriter::writeObject() {
 
   defineSymbols();
 
+  for (const MCSection &Section : Asm)
+    writeText(static_cast<const MCSectionGOFF *>(&Section));
+
   writeEnd();
 
   // Make sure all records are written.
diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp
index f3bb5aa88e8fb..3f2f9a756d33b 100644
--- a/llvm/lib/MC/MCContext.cpp
+++ b/llvm/lib/MC/MCContext.cpp
@@ -723,7 +723,8 @@ MCContext::getELFUniqueIDForEntsize(StringRef SectionName, unsigned Flags,
 
 template <typename TAttr>
 MCSectionGOFF *MCContext::getGOFFSection(SectionKind Kind, StringRef Name,
-                                         TAttr Attributes, MCSection *Parent) {
+                                         TAttr Attributes, MCSection *Parent,
+                                         bool IsVirtual) {
   std::string UniqueName(Name);
   if (Parent) {
     UniqueName.append("/").append(Parent->getName());
@@ -737,8 +738,9 @@ MCSectionGOFF *MCContext::getGOFFSection(SectionKind Kind, StringRef Name,
     return Iter->second;
 
   StringRef CachedName = StringRef(Iter->first.c_str(), Name.size());
-  MCSectionGOFF *GOFFSection = new (GOFFAllocator.Allocate()) MCSectionGOFF(
-      CachedName, Kind, Attributes, static_cast<MCSectionGOFF *>(Parent));
+  MCSectionGOFF *GOFFSection = new (GOFFAllocator.Allocate())
+      MCSectionGOFF(CachedName, Kind, IsVirtual, Attributes,
+                    static_cast<MCSectionGOFF *>(Parent));
   Iter->second = GOFFSection;
   allocInitialFragment(*GOFFSection);
   return GOFFSection;
@@ -746,19 +748,23 @@ MCSectionGOFF *MCContext::getGOFFSection(SectionKind Kind, StringRef Name,
 
 MCSectionGOFF *MCContext::getGOFFSection(SectionKind Kind, StringRef Name,
                                          GOFF::SDAttr SDAttributes) {
-  return getGOFFSection<GOFF::SDAttr>(Kind, Name, SDAttributes, nullptr);
+  return getGOFFSection<GOFF::SDAttr>(Kind, Name, SDAttributes, nullptr,
+                                      /*IsVirtual=*/true);
 }
 
 MCSectionGOFF *MCContext::getGOFFSection(SectionKind Kind, StringRef Name,
                                          GOFF::EDAttr EDAttributes,
                                          MCSection *Parent) {
-  return getGOFFSection<GOFF::EDAttr>(Kind, Name, EDAttributes, Parent);
+  return getGOFFSection<GOFF::EDAttr>(
+      Kind, Name, EDAttributes, Parent,
+      /*IsVirtual=*/EDAttributes.BindAlgorithm == GOFF::ESD_BA_Merge);
 }
 
 MCSectionGOFF *MCContext::getGOFFSection(SectionKind Kind, StringRef Name,
                                          GOFF::PRAttr PRAttributes,
                                          MCSection *Parent) {
-  return getGOFFSection<GOFF::PRAttr>(Kind, Name, PRAttributes, Parent);
+  return getGOFFSection<GOFF::PRAttr>(Kind, Name, PRAttributes, Parent,
+                                      /*IsVirtual=*/false);
 }
 
 MCSectionCOFF *MCContext::getCOFFSection(StringRef Section,
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
index ea9bc4ce95174..b98584df54d5a 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll
@@ -104,9 +104,26 @@ 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
 
+; 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
+
+; 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 {{..}} {{.*}}
+
+; 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 {{..}} {{.*}}
+
+; 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 {{..}} {{.*}}
+
 ; End record.
-; CHECK-NEXT: 000320 03 40 00 00 00 00 00 00 00 00 00 00 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 00 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 00
-; CHECK-NEXT: 000360 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; 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
diff --git a/llvm/test/CodeGen/SystemZ/zos-section-2.ll b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
index 472517acc4a45..0f608c1206b96 100644
--- a/llvm/test/CodeGen/SystemZ/zos-section-2.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-section-2.ll
@@ -147,9 +147,29 @@ 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
 
+; 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 {{..}} {{.*}}
+
+; 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 {{..}} {{.*}}
+
+; 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
+
+; 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 {{..}} {{.*}}
+
 ; End record.
-; CHECK-NEXT: 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:      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



More information about the llvm-commits mailing list