[llvm] 3adc2a0 - [SystemZ/zOS/GOFF] Implement GOFF writer for empty files.

Kai Nacke via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 29 12:27:31 PDT 2023


Author: Kai Nacke
Date: 2023-09-29T19:27:11Z
New Revision: 3adc2a0b84d0fbccead5d77d1b7d7a9b0bec175d

URL: https://github.com/llvm/llvm-project/commit/3adc2a0b84d0fbccead5d77d1b7d7a9b0bec175d
DIFF: https://github.com/llvm/llvm-project/commit/3adc2a0b84d0fbccead5d77d1b7d7a9b0bec175d.diff

LOG: [SystemZ/zOS/GOFF] Implement GOFF writer for empty files.

Set ups the infrastructure to create an empty GOFF file.
Also adds a GOFF writer which writes only HDR/END records.

Reviewed By: jhenderson, kpn

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

Added: 
    llvm/include/llvm/MC/MCGOFFObjectWriter.h
    llvm/include/llvm/MC/MCGOFFStreamer.h
    llvm/lib/MC/GOFFObjectWriter.cpp
    llvm/lib/MC/MCGOFFStreamer.cpp
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZELFObjectWriter.cpp
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp
    llvm/test/MC/GOFF/empty-goff.s

Modified: 
    llvm/include/llvm/BinaryFormat/GOFF.h
    llvm/include/llvm/MC/TargetRegistry.h
    llvm/lib/MC/CMakeLists.txt
    llvm/lib/MC/MCAsmBackend.cpp
    llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h

Removed: 
    llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCObjectWriter.cpp


################################################################################
diff  --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h
index b4ddbabdf1e4e78..f1a30e41b736bda 100644
--- a/llvm/include/llvm/BinaryFormat/GOFF.h
+++ b/llvm/include/llvm/BinaryFormat/GOFF.h
@@ -10,6 +10,8 @@
 // constants for the GOFF file format.
 //
 // GOFF specifics can be found in MVS Program Management: Advanced Facilities.
+// See
+// https://www.ibm.com/docs/en/zos/3.1.0?topic=facilities-generalized-object-file-format-goff
 //
 //===----------------------------------------------------------------------===//
 
@@ -19,13 +21,24 @@
 #include "llvm/Support/DataTypes.h"
 
 namespace llvm {
+
 namespace GOFF {
 
+/// \brief Length of the parts of a physical GOFF record.
 constexpr uint8_t RecordLength = 80;
 constexpr uint8_t RecordPrefixLength = 3;
 constexpr uint8_t PayloadLength = 77;
+constexpr uint8_t RecordContentLength = RecordLength - RecordPrefixLength;
+
+/// \brief Maximum data length before starting a new card for RLD and TXT data.
+///
+/// The maximum number of bytes that can be included in an RLD or TXT record and
+/// their continuations is a SIGNED 16 bit int despite what the spec says. The
+/// number of bytes we allow ourselves to attach to a card is thus arbitrarily
+/// limited to 32K-1 bytes.
+constexpr uint16_t MaxDataLength = 32 * 1024 - 1;
 
-// Prefix byte on every record. This indicates GOFF format.
+/// \brief Prefix byte on every record. This indicates GOFF format.
 constexpr uint8_t PTVPrefix = 0x03;
 
 enum RecordType : uint8_t {

diff  --git a/llvm/include/llvm/MC/MCGOFFObjectWriter.h b/llvm/include/llvm/MC/MCGOFFObjectWriter.h
new file mode 100644
index 000000000000000..85316a6fd5d0e17
--- /dev/null
+++ b/llvm/include/llvm/MC/MCGOFFObjectWriter.h
@@ -0,0 +1,42 @@
+//===- MCGOFFObjectWriter.h - GOFF Object Writer ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_MC_MCGOFFOBJECTWRITER_H
+#define LLVM_MC_MCGOFFOBJECTWRITER_H
+
+#include "llvm/MC/MCObjectWriter.h"
+
+namespace llvm {
+class MCObjectWriter;
+class raw_pwrite_stream;
+
+class MCGOFFObjectTargetWriter : public MCObjectTargetWriter {
+protected:
+  MCGOFFObjectTargetWriter() = default;
+
+public:
+  virtual ~MCGOFFObjectTargetWriter() = default;
+
+  Triple::ObjectFormatType getFormat() const override { return Triple::GOFF; }
+
+  static bool classof(const MCObjectTargetWriter *W) {
+    return W->getFormat() == Triple::GOFF;
+  }
+};
+
+/// \brief Construct a new GOFF writer instance.
+///
+/// \param MOTW - The target-specific GOFF writer subclass.
+/// \param OS - The stream to write to.
+/// \returns The constructed object writer.
+std::unique_ptr<MCObjectWriter>
+createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
+                       raw_pwrite_stream &OS);
+} // namespace llvm
+
+#endif

diff  --git a/llvm/include/llvm/MC/MCGOFFStreamer.h b/llvm/include/llvm/MC/MCGOFFStreamer.h
new file mode 100644
index 000000000000000..2345509b161da5b
--- /dev/null
+++ b/llvm/include/llvm/MC/MCGOFFStreamer.h
@@ -0,0 +1,40 @@
+//===- MCGOFFStreamer.h - MCStreamer GOFF Object File Interface--*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_MC_MCGOFFSTREAMER_H
+#define LLVM_MC_MCGOFFSTREAMER_H
+
+#include "llvm/MC/MCObjectStreamer.h"
+#include "llvm/MC/MCObjectWriter.h"
+
+namespace llvm {
+
+class MCGOFFStreamer : public MCObjectStreamer {
+public:
+  MCGOFFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
+                 std::unique_ptr<MCObjectWriter> OW,
+                 std::unique_ptr<MCCodeEmitter> Emitter)
+      : MCObjectStreamer(Context, std::move(MAB), std::move(OW),
+                         std::move(Emitter)) {}
+
+  ~MCGOFFStreamer() override;
+
+  bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
+    return false;
+  }
+  void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
+                        Align ByteAlignment) 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 {}
+};
+
+} // end namespace llvm
+
+#endif

diff  --git a/llvm/include/llvm/MC/TargetRegistry.h b/llvm/include/llvm/MC/TargetRegistry.h
index bf23313c0f90087..47051447404d00f 100644
--- a/llvm/include/llvm/MC/TargetRegistry.h
+++ b/llvm/include/llvm/MC/TargetRegistry.h
@@ -94,6 +94,11 @@ MCStreamer *createELFStreamer(MCContext &Ctx,
                               std::unique_ptr<MCObjectWriter> &&OW,
                               std::unique_ptr<MCCodeEmitter> &&CE,
                               bool RelaxAll);
+MCStreamer *createGOFFStreamer(MCContext &Ctx,
+                               std::unique_ptr<MCAsmBackend> &&TAB,
+                               std::unique_ptr<MCObjectWriter> &&OW,
+                               std::unique_ptr<MCCodeEmitter> &&CE,
+                               bool RelaxAll);
 MCStreamer *createMachOStreamer(MCContext &Ctx,
                                 std::unique_ptr<MCAsmBackend> &&TAB,
                                 std::unique_ptr<MCObjectWriter> &&OW,
@@ -195,6 +200,10 @@ class Target {
                       std::unique_ptr<MCAsmBackend> &&TAB,
                       std::unique_ptr<MCObjectWriter> &&OW,
                       std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll);
+  using GOFFStreamerCtorTy =
+      MCStreamer *(*)(MCContext &Ctx, std::unique_ptr<MCAsmBackend> &&TAB,
+                      std::unique_ptr<MCObjectWriter> &&OW,
+                      std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll);
   using MachOStreamerCtorTy =
       MCStreamer *(*)(MCContext &Ctx, std::unique_ptr<MCAsmBackend> &&TAB,
                       std::unique_ptr<MCObjectWriter> &&OW,
@@ -327,6 +336,7 @@ class Target {
 
   // Construction functions for the various object formats, if registered.
   COFFStreamerCtorTy COFFStreamerCtorFn = nullptr;
+  GOFFStreamerCtorTy GOFFStreamerCtorFn = nullptr;
   MachOStreamerCtorTy MachOStreamerCtorFn = nullptr;
   ELFStreamerCtorTy ELFStreamerCtorFn = nullptr;
   WasmStreamerCtorTy WasmStreamerCtorFn = nullptr;
@@ -597,7 +607,13 @@ class Target {
                                std::move(Emitter), RelaxAll);
       break;
     case Triple::GOFF:
-      report_fatal_error("GOFF MCObjectStreamer not implemented yet");
+      if (GOFFStreamerCtorFn)
+        S = GOFFStreamerCtorFn(Ctx, std::move(TAB), std::move(OW),
+                               std::move(Emitter), RelaxAll);
+      else
+        S = createGOFFStreamer(Ctx, std::move(TAB), std::move(OW),
+                               std::move(Emitter), RelaxAll);
+      break;
     case Triple::XCOFF:
       if (XCOFFStreamerCtorFn)
         S = XCOFFStreamerCtorFn(T, Ctx, std::move(TAB), std::move(OW),
@@ -1004,6 +1020,10 @@ struct TargetRegistry {
     T.COFFStreamerCtorFn = Fn;
   }
 
+  static void RegisterGOFFStreamer(Target &T, Target::GOFFStreamerCtorTy Fn) {
+    T.GOFFStreamerCtorFn = Fn;
+  }
+
   static void RegisterMachOStreamer(Target &T, Target::MachOStreamerCtorTy Fn) {
     T.MachOStreamerCtorFn = Fn;
   }

diff  --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt
index 8a9515307947389..a089d2bff94f42c 100644
--- a/llvm/lib/MC/CMakeLists.txt
+++ b/llvm/lib/MC/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_component_library(LLVMMC
   ConstantPools.cpp
   DXContainerPSVInfo.cpp
   ELFObjectWriter.cpp
+  GOFFObjectWriter.cpp
   MCAsmBackend.cpp
   MCAsmInfo.cpp
   MCAsmInfoCOFF.cpp
@@ -23,6 +24,7 @@ add_llvm_component_library(LLVMMC
   MCELFStreamer.cpp
   MCExpr.cpp
   MCFragment.cpp
+  MCGOFFStreamer.cpp
   MCInst.cpp
   MCInstPrinter.cpp
   MCInstrAnalysis.cpp

diff  --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
new file mode 100644
index 000000000000000..33244cbf88d919e
--- /dev/null
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -0,0 +1,296 @@
+//===- lib/MC/GOFFObjectWriter.cpp - GOFF File Writer ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements GOFF object file writer information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/MC/MCAsmLayout.h"
+#include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCGOFFObjectWriter.h"
+#include "llvm/MC/MCValue.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "goff-writer"
+
+namespace {
+
+// The standard System/390 convention is to name the high-order (leftmost) bit
+// in a byte as bit zero. The Flags type helps to set bits in a byte according
+// to this numeration order.
+class Flags {
+  uint8_t Val;
+
+  constexpr static uint8_t bits(uint8_t BitIndex, uint8_t Length, uint8_t Value,
+                                uint8_t OldValue) {
+    assert(BitIndex < 8 && "Bit index out of bounds!");
+    assert(Length + BitIndex <= 8 && "Bit length too long!");
+
+    uint8_t Mask = ((1 << Length) - 1) << (8 - BitIndex - Length);
+    Value = Value << (8 - BitIndex - Length);
+    assert((Value & Mask) == Value && "Bits set outside of range!");
+
+    return (OldValue & ~Mask) | Value;
+  }
+
+public:
+  constexpr Flags() : Val(0) {}
+  constexpr Flags(uint8_t BitIndex, uint8_t Length, uint8_t Value)
+      : Val(bits(BitIndex, Length, Value, 0)) {}
+
+  void set(uint8_t BitIndex, uint8_t Length, uint8_t Value) {
+    Val = bits(BitIndex, Length, Value, Val);
+  }
+
+  constexpr operator uint8_t() const { return Val; }
+};
+
+// Common flag values on records.
+
+// Flag: This record is continued.
+constexpr uint8_t RecContinued = Flags(7, 1, 1);
+
+// Flag: This record is a continuation.
+constexpr uint8_t RecContinuation = Flags(6, 1, 1);
+
+// The GOFFOstream is responsible to write the data into the fixed physical
+// records of the format. A user of this class announces the start of a new
+// logical record and the size of its content. While writing the content, the
+// physical records are created for the data. Possible fill bytes at the end of
+// a physical record are written automatically. In principle, the GOFFOstream
+// is agnostic of the endianness of the content. However, it also supports
+// writing data in big endian byte order.
+class GOFFOstream : public raw_ostream {
+  /// The underlying raw_pwrite_stream.
+  raw_pwrite_stream &OS;
+
+  /// The remaining size of this logical record, including fill bytes.
+  size_t RemainingSize;
+
+#ifndef NDEBUG
+  /// The number of bytes needed to fill up the last physical record.
+  size_t Gap = 0;
+#endif
+
+  /// The number of logical records emitted to far.
+  uint32_t LogicalRecords;
+
+  /// The type of the current (logical) record.
+  GOFF::RecordType CurrentType;
+
+  /// Signals start of new record.
+  bool NewLogicalRecord;
+
+  /// Static allocated buffer for the stream, used by the raw_ostream class. The
+  /// buffer is sized to hold the content of a physical record.
+  char Buffer[GOFF::RecordContentLength];
+
+  // Return the number of bytes left to write until next physical record.
+  // Please note that we maintain the total numbers of byte left, not the
+  // written size.
+  size_t bytesToNextPhysicalRecord() {
+    size_t Bytes = RemainingSize % GOFF::RecordContentLength;
+    return Bytes ? Bytes : GOFF::RecordContentLength;
+  }
+
+  /// Write the record prefix of a physical record, using the given record type.
+  static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
+                                size_t RemainingSize,
+                                uint8_t Flags = RecContinuation);
+
+  /// Fill the last physical record of a logical record with zero bytes.
+  void fillRecord();
+
+  /// See raw_ostream::write_impl.
+  void write_impl(const char *Ptr, size_t Size) override;
+
+  /// Return the current position within the stream, not counting the bytes
+  /// currently in the buffer.
+  uint64_t current_pos() const override { return OS.tell(); }
+
+public:
+  explicit GOFFOstream(raw_pwrite_stream &OS)
+      : OS(OS), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) {
+    SetBuffer(Buffer, sizeof(Buffer));
+  }
+
+  ~GOFFOstream() { finalize(); }
+
+  raw_pwrite_stream &getOS() { return OS; }
+
+  void newRecord(GOFF::RecordType Type, size_t Size);
+
+  void finalize() { fillRecord(); }
+
+  uint32_t logicalRecords() { return LogicalRecords; }
+
+  // Support for endian-specific data.
+  template <typename value_type> void writebe(value_type Value) {
+    Value = support::endian::byte_swap<value_type>(Value, support::big);
+    write(reinterpret_cast<const char *>(&Value), sizeof(value_type));
+  }
+};
+
+void GOFFOstream::writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
+                                    size_t RemainingSize, uint8_t Flags) {
+  uint8_t TypeAndFlags = Flags | (Type << 4);
+  if (RemainingSize > GOFF::RecordLength)
+    TypeAndFlags |= RecContinued;
+  OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type
+     << static_cast<unsigned char>(TypeAndFlags)    // Continuation
+     << static_cast<unsigned char>(0);              // Version
+}
+
+void GOFFOstream::newRecord(GOFF::RecordType Type, size_t Size) {
+  fillRecord();
+  CurrentType = Type;
+  RemainingSize = Size;
+#ifdef NDEBUG
+  size_t Gap;
+#endif
+  Gap = (RemainingSize % GOFF::RecordContentLength);
+  if (Gap) {
+    Gap = GOFF::RecordContentLength - Gap;
+    RemainingSize += Gap;
+  }
+  NewLogicalRecord = true;
+  ++LogicalRecords;
+}
+
+void GOFFOstream::fillRecord() {
+  assert((GetNumBytesInBuffer() <= RemainingSize) &&
+         "More bytes in buffer than expected");
+  size_t Remains = RemainingSize - GetNumBytesInBuffer();
+  if (Remains) {
+    assert(Remains == Gap && "Wrong size of fill gap");
+    assert((Remains < GOFF::RecordLength) &&
+           "Attempt to fill more than one physical record");
+    raw_ostream::write_zeros(Remains);
+  }
+  flush();
+  assert(RemainingSize == 0 && "Not fully flushed");
+  assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");
+}
+
+// This function is called from the raw_ostream implementation if:
+// - The internal buffer is full. Size is excactly the size of the buffer.
+// - Data larger than the internal buffer is written. Size is a multiple of the
+//   buffer size.
+// - flush() has been called. Size is at most the buffer size.
+// The GOFFOstream implementation ensures that flush() is called before a new
+// logical record begins. Therefore it is sufficient to check for a new block
+// only once.
+void GOFFOstream::write_impl(const char *Ptr, size_t Size) {
+  assert((RemainingSize >= Size) && "Attempt to write too much data");
+  assert(RemainingSize && "Logical record overflow");
+  if (!(RemainingSize % GOFF::RecordContentLength)) {
+    writeRecordPrefix(OS, CurrentType, RemainingSize,
+                      NewLogicalRecord ? 0 : RecContinuation);
+    NewLogicalRecord = false;
+  }
+  assert(!NewLogicalRecord &&
+         "New logical record not on physical record boundary");
+
+  size_t Idx = 0;
+  while (Size > 0) {
+    size_t BytesToWrite = bytesToNextPhysicalRecord();
+    if (BytesToWrite > Size)
+      BytesToWrite = Size;
+    OS.write(Ptr + Idx, BytesToWrite);
+    Idx += BytesToWrite;
+    Size -= BytesToWrite;
+    RemainingSize -= BytesToWrite;
+    if (Size)
+      writeRecordPrefix(OS, CurrentType, RemainingSize);
+  }
+}
+
+class GOFFObjectWriter : public MCObjectWriter {
+  // The target specific GOFF writer instance.
+  std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter;
+
+  // The stream used to write the GOFF records.
+  GOFFOstream OS;
+
+public:
+  GOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
+                   raw_pwrite_stream &OS)
+      : TargetObjectWriter(std::move(MOTW)), OS(OS) {}
+
+  ~GOFFObjectWriter() override {}
+
+  // Write GOFF records.
+  void writeHeader();
+  void writeEnd();
+
+  // 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 {}
+  uint64_t writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;
+};
+} // end anonymous namespace
+
+void GOFFObjectWriter::writeHeader() {
+  OS.newRecord(GOFF::RT_HDR, /*Size=*/57);
+  OS.write_zeros(1);       // Reserved
+  OS.writebe<uint32_t>(0); // Target Hardware Environment
+  OS.writebe<uint32_t>(0); // Target Operating System Environment
+  OS.write_zeros(2);       // Reserved
+  OS.writebe<uint16_t>(0); // CCSID
+  OS.write_zeros(16);      // Character Set name
+  OS.write_zeros(16);      // Language Product Identifier
+  OS.writebe<uint32_t>(1); // Architecture Level
+  OS.writebe<uint16_t>(0); // Module Properties Length
+  OS.write_zeros(6);       // Reserved
+}
+
+void GOFFObjectWriter::writeEnd() {
+  uint8_t F = GOFF::END_EPR_None;
+  uint8_t AMODE = 0;
+  uint32_t ESDID = 0;
+
+  // TODO Set Flags/AMODE/ESDID for entry point.
+
+  OS.newRecord(GOFF::RT_END, /*Size=*/13);
+  OS.writebe<uint8_t>(Flags(6, 2, F)); // Indicator flags
+  OS.writebe<uint8_t>(AMODE);          // AMODE
+  OS.write_zeros(3);                   // Reserved
+  // The record count is the number of logical records. In principle, this value
+  // is available as OS.logicalRecords(). However, some tools rely on this field
+  // being zero.
+  OS.writebe<uint32_t>(0);     // Record Count
+  OS.writebe<uint32_t>(ESDID); // ESDID (of entry point)
+  OS.finalize();
+}
+
+uint64_t GOFFObjectWriter::writeObject(MCAssembler &Asm,
+                                       const MCAsmLayout &Layout) {
+  uint64_t StartOffset = OS.tell();
+
+  writeHeader();
+  writeEnd();
+
+  LLVM_DEBUG(dbgs() << "Wrote " << OS.logicalRecords() << " logical records.");
+
+  return OS.tell() - StartOffset;
+}
+
+std::unique_ptr<MCObjectWriter>
+llvm::createGOFFObjectWriter(std::unique_ptr<MCGOFFObjectTargetWriter> MOTW,
+                             raw_pwrite_stream &OS) {
+  return std::make_unique<GOFFObjectWriter>(std::move(MOTW), OS);
+}

diff  --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp
index c2e89f4dfa5dba7..4ad0e7a809307e2 100644
--- a/llvm/lib/MC/MCAsmBackend.cpp
+++ b/llvm/lib/MC/MCAsmBackend.cpp
@@ -10,6 +10,7 @@
 #include "llvm/MC/MCDXContainerWriter.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCFixupKindInfo.h"
+#include "llvm/MC/MCGOFFObjectWriter.h"
 #include "llvm/MC/MCMachObjectWriter.h"
 #include "llvm/MC/MCObjectWriter.h"
 #include "llvm/MC/MCSPIRVObjectWriter.h"
@@ -46,6 +47,9 @@ MCAsmBackend::createObjectWriter(raw_pwrite_stream &OS) const {
   case Triple::Wasm:
     return createWasmObjectWriter(cast<MCWasmObjectTargetWriter>(std::move(TW)),
                                   OS);
+  case Triple::GOFF:
+    return createGOFFObjectWriter(cast<MCGOFFObjectTargetWriter>(std::move(TW)),
+                                  OS);
   case Triple::XCOFF:
     return createXCOFFObjectWriter(
         cast<MCXCOFFObjectTargetWriter>(std::move(TW)), OS);

diff  --git a/llvm/lib/MC/MCGOFFStreamer.cpp b/llvm/lib/MC/MCGOFFStreamer.cpp
new file mode 100644
index 000000000000000..58d13c9f3788534
--- /dev/null
+++ b/llvm/lib/MC/MCGOFFStreamer.cpp
@@ -0,0 +1,34 @@
+//===- lib/MC/MCGOFFStreamer.cpp - GOFF Object Output ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file assembles .s files and emits GOFF .o object files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCGOFFStreamer.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/TargetRegistry.h"
+
+using namespace llvm;
+
+MCGOFFStreamer::~MCGOFFStreamer() {}
+
+MCStreamer *llvm::createGOFFStreamer(MCContext &Context,
+                                     std::unique_ptr<MCAsmBackend> &&MAB,
+                                     std::unique_ptr<MCObjectWriter> &&OW,
+                                     std::unique_ptr<MCCodeEmitter> &&CE,
+                                     bool RelaxAll) {
+  MCGOFFStreamer *S =
+      new MCGOFFStreamer(Context, std::move(MAB), std::move(OW), std::move(CE));
+  if (RelaxAll)
+    S->getAssembler().setRelaxAll(true);
+  return S;
+}

diff  --git a/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
index 91aac7dd6603cbb..6700d7936970872 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/CMakeLists.txt
@@ -1,10 +1,11 @@
 add_llvm_component_library(LLVMSystemZDesc
+  SystemZELFObjectWriter.cpp
+  SystemZGOFFObjectWriter.cpp
   SystemZInstPrinter.cpp
   SystemZMCAsmBackend.cpp
   SystemZMCAsmInfo.cpp
   SystemZMCCodeEmitter.cpp
   SystemZMCExpr.cpp
-  SystemZMCObjectWriter.cpp
   SystemZMCTargetDesc.cpp
 
   LINK_COMPONENTS

diff  --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCObjectWriter.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZELFObjectWriter.cpp
similarity index 88%
rename from llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCObjectWriter.cpp
rename to llvm/lib/Target/SystemZ/MCTargetDesc/SystemZELFObjectWriter.cpp
index 9c6a1b6e8af01ca..138ce93ac66531c 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCObjectWriter.cpp
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZELFObjectWriter.cpp
@@ -1,4 +1,4 @@
-//===-- SystemZMCObjectWriter.cpp - SystemZ ELF writer --------------------===//
+//===-- SystemZELFObjectWriter.cpp - SystemZ ELF writer -------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -23,10 +23,10 @@ using namespace llvm;
 
 namespace {
 
-class SystemZObjectWriter : public MCELFObjectTargetWriter {
+class SystemZELFObjectWriter : public MCELFObjectTargetWriter {
 public:
-  SystemZObjectWriter(uint8_t OSABI);
-  ~SystemZObjectWriter() override = default;
+  SystemZELFObjectWriter(uint8_t OSABI);
+  ~SystemZELFObjectWriter() override = default;
 
 protected:
   // Override MCELFObjectTargetWriter.
@@ -36,9 +36,9 @@ class SystemZObjectWriter : public MCELFObjectTargetWriter {
 
 } // end anonymous namespace
 
-SystemZObjectWriter::SystemZObjectWriter(uint8_t OSABI)
-  : MCELFObjectTargetWriter(/*Is64Bit_=*/true, OSABI, ELF::EM_S390,
-                            /*HasRelocationAddend_=*/ true) {}
+SystemZELFObjectWriter::SystemZELFObjectWriter(uint8_t OSABI)
+    : MCELFObjectTargetWriter(/*Is64Bit_=*/true, OSABI, ELF::EM_S390,
+                              /*HasRelocationAddend_=*/true) {}
 
 // Return the relocation type for an absolute value of MCFixupKind Kind.
 static unsigned getAbsoluteReloc(MCContext &Ctx, SMLoc Loc, unsigned Kind) {
@@ -146,10 +146,10 @@ static unsigned getPLTReloc(MCContext &Ctx, SMLoc Loc, unsigned Kind) {
   return 0;
 }
 
-unsigned SystemZObjectWriter::getRelocType(MCContext &Ctx,
-                                           const MCValue &Target,
-                                           const MCFixup &Fixup,
-                                           bool IsPCRel) const {
+unsigned SystemZELFObjectWriter::getRelocType(MCContext &Ctx,
+                                              const MCValue &Target,
+                                              const MCFixup &Fixup,
+                                              bool IsPCRel) const {
   SMLoc Loc = Fixup.getLoc();
   unsigned Kind = Fixup.getKind();
   if (Kind >= FirstLiteralRelocationKind)
@@ -199,6 +199,6 @@ unsigned SystemZObjectWriter::getRelocType(MCContext &Ctx,
 }
 
 std::unique_ptr<MCObjectTargetWriter>
-llvm::createSystemZObjectWriter(uint8_t OSABI) {
-  return std::make_unique<SystemZObjectWriter>(OSABI);
+llvm::createSystemZELFObjectWriter(uint8_t OSABI) {
+  return std::make_unique<SystemZELFObjectWriter>(OSABI);
 }

diff  --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp
new file mode 100644
index 000000000000000..e9989c3b450c7e2
--- /dev/null
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZGOFFObjectWriter.cpp
@@ -0,0 +1,26 @@
+//===- SystemZGOFFObjectWriter.cpp - SystemZ GOFF writer ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/SystemZMCTargetDesc.h"
+#include "llvm/MC/MCGOFFObjectWriter.h"
+
+using namespace llvm;
+
+namespace {
+class SystemZGOFFObjectWriter : public MCGOFFObjectTargetWriter {
+public:
+  SystemZGOFFObjectWriter();
+};
+} // end anonymous namespace
+
+SystemZGOFFObjectWriter::SystemZGOFFObjectWriter()
+    : MCGOFFObjectTargetWriter() {}
+
+std::unique_ptr<MCObjectTargetWriter> llvm::createSystemZGOFFObjectWriter() {
+  return std::make_unique<SystemZGOFFObjectWriter>();
+}

diff  --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp
index 880766a1a23fb0d..20dcf74cb8d92ba 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp
@@ -106,10 +106,9 @@ static uint64_t extractBitsForFixup(MCFixupKind Kind, uint64_t Value,
 
 namespace {
 class SystemZMCAsmBackend : public MCAsmBackend {
-  uint8_t OSABI;
 public:
-  SystemZMCAsmBackend(uint8_t osABI)
-      : MCAsmBackend(support::big), OSABI(osABI) {}
+  SystemZMCAsmBackend()
+      : MCAsmBackend(support::big) {}
 
   // Override MCAsmBackend
   unsigned getNumFixupKinds() const override {
@@ -130,10 +129,6 @@ class SystemZMCAsmBackend : public MCAsmBackend {
   }
   bool writeNopData(raw_ostream &OS, uint64_t Count,
                     const MCSubtargetInfo *STI) const override;
-  std::unique_ptr<MCObjectTargetWriter>
-  createObjectTargetWriter() const override {
-    return createSystemZObjectWriter(OSABI);
-  }
 };
 } // end anonymous namespace
 
@@ -208,11 +203,39 @@ bool SystemZMCAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
   return true;
 }
 
+namespace {
+class ELFSystemZAsmBackend : public SystemZMCAsmBackend {
+  uint8_t OSABI;
+
+public:
+  ELFSystemZAsmBackend(uint8_t OsABI) : SystemZMCAsmBackend(), OSABI(OsABI){};
+
+  std::unique_ptr<MCObjectTargetWriter>
+  createObjectTargetWriter() const override {
+    return createSystemZELFObjectWriter(OSABI);
+  }
+};
+
+class GOFFSystemZAsmBackend : public SystemZMCAsmBackend {
+public:
+  GOFFSystemZAsmBackend() : SystemZMCAsmBackend(){};
+
+  std::unique_ptr<MCObjectTargetWriter>
+  createObjectTargetWriter() const override {
+    return createSystemZGOFFObjectWriter();
+  }
+};
+} // namespace
+
 MCAsmBackend *llvm::createSystemZMCAsmBackend(const Target &T,
                                               const MCSubtargetInfo &STI,
                                               const MCRegisterInfo &MRI,
                                               const MCTargetOptions &Options) {
+  if (STI.getTargetTriple().isOSzOS()) {
+    return new GOFFSystemZAsmBackend();
+  }
+
   uint8_t OSABI =
       MCELFObjectTargetWriter::getOSABI(STI.getTargetTriple().getOS());
-  return new SystemZMCAsmBackend(OSABI);
+  return new ELFSystemZAsmBackend(OSABI);
 }

diff  --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h
index f2bfc9ac48e5dc3..39c1836a137005c 100644
--- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h
+++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCTargetDesc.h
@@ -85,7 +85,9 @@ MCAsmBackend *createSystemZMCAsmBackend(const Target &T,
                                         const MCRegisterInfo &MRI,
                                         const MCTargetOptions &Options);
 
-std::unique_ptr<MCObjectTargetWriter> createSystemZObjectWriter(uint8_t OSABI);
+std::unique_ptr<MCObjectTargetWriter>
+createSystemZELFObjectWriter(uint8_t OSABI);
+std::unique_ptr<MCObjectTargetWriter> createSystemZGOFFObjectWriter();
 } // end namespace llvm
 
 // Defines symbolic names for SystemZ registers.

diff  --git a/llvm/test/MC/GOFF/empty-goff.s b/llvm/test/MC/GOFF/empty-goff.s
new file mode 100644
index 000000000000000..29bc6de71eacc1c
--- /dev/null
+++ b/llvm/test/MC/GOFF/empty-goff.s
@@ -0,0 +1,23 @@
+* RUN: llvm-mc <%s --triple s390x-ibm-zos --filetype=obj -o - | \
+* RUN:   od -Ax -tx1 -v | FileCheck --ignore-case %s
+
+* Header record:
+*  03 is prefix byte
+*  f. is header type
+*  .0 is version
+* The 1 at offset 51 is the architecture level.
+* CHECK: 000000 03 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000030 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+* End record:
+*  03 is prefix byte
+*  4. is header type
+*  .0 is version
+* CHECK: 000050 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+* CHECK: 000090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00


        


More information about the llvm-commits mailing list