[llvm-branch-commits] [llvm] [GOFF] Add writing of section symbols (PR #133799)

Kai Nacke via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Mar 31 14:12:51 PDT 2025


https://github.com/redstar created https://github.com/llvm/llvm-project/pull/133799

The GOFF format uses symbol definitions to represent sections and symbols. Introducing a section can require up to 3 symbol definitions. However, most of these details are not needed by the AsmPrinter. To mapped from a section (a MCSectionGOFF) to the symbol definitions, a new class called MCGOFFSymbolMapper is used. The same information can also be used by the assembly output, which justifies this centralized approach. Writing the mapped symbols is then straight forward.

>From 77c230f82a61769714bee98b2e848820850d9cb5 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Mon, 24 Mar 2025 16:26:19 -0400
Subject: [PATCH] [GOFF] Add writing of section symbols

The GOFF format uses symbol definitions to represent sections and
symbols. Introducing a section can require up to 3 symbol definitions.
However, most of these details are not needed by the AsmPrinter.
To mapped from a section (a MCSectionGOFF) to the symbol definitions,
a new class called MCGOFFSymbolMapper is used. The same information
can also be used by the assembly output, which justifies this
centralized approach. Writing the mapped symbols is then straight
forward.
---
 llvm/include/llvm/BinaryFormat/GOFF.h     |  85 +++++++
 llvm/include/llvm/MC/MCGOFFSymbolMapper.h | 148 +++++++++++
 llvm/lib/MC/CMakeLists.txt                |   1 +
 llvm/lib/MC/GOFFObjectWriter.cpp          | 290 +++++++++++++++++++---
 llvm/lib/MC/MCGOFFSymbolMapper.cpp        | 203 +++++++++++++++
 llvm/lib/MC/MCObjectFileInfo.cpp          |   2 +-
 llvm/test/CodeGen/SystemZ/zos-ppa2.ll     |   2 +-
 llvm/test/MC/GOFF/section.ll              |  73 ++++++
 8 files changed, 765 insertions(+), 39 deletions(-)
 create mode 100644 llvm/include/llvm/MC/MCGOFFSymbolMapper.h
 create mode 100644 llvm/lib/MC/MCGOFFSymbolMapper.cpp
 create mode 100644 llvm/test/MC/GOFF/section.ll

diff --git a/llvm/include/llvm/BinaryFormat/GOFF.h b/llvm/include/llvm/BinaryFormat/GOFF.h
index 443bcfc9479a8..43d80e0c247e9 100644
--- a/llvm/include/llvm/BinaryFormat/GOFF.h
+++ b/llvm/include/llvm/BinaryFormat/GOFF.h
@@ -169,6 +169,91 @@ enum SubsectionKind : uint8_t {
   SK_PPA1 = 2,
   SK_PPA2 = 4,
 };
+
+// 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 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) {
+    uint8_t Pos = 8 - BitIndex - Length;
+    uint8_t Mask = ((1 << Length) - 1) << Pos;
+    Value = Value << Pos;
+    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)) {}
+
+  template <typename T>
+  constexpr void set(uint8_t BitIndex, uint8_t Length, T NewValue) {
+    Val = bits(BitIndex, Length, static_cast<uint8_t>(NewValue), Val);
+  }
+
+  template <typename T>
+  constexpr T get(uint8_t BitIndex, uint8_t Length) const {
+    return static_cast<T>((Val >> (8 - BitIndex - Length)) &
+                          ((1 << Length) - 1));
+  }
+
+  constexpr operator uint8_t() const { return Val; }
+};
+
+// Structure for the flag field of a symbol. See
+// https://www.ibm.com/docs/en/zos/3.1.0?topic=formats-external-symbol-definition-record,
+// offset 41, for the definition.
+struct SymbolFlags {
+  Flags SymFlags;
+
+#define GOFF_SYMBOL_FLAG(NAME, TYPE, BITINDEX, LENGTH)                         \
+  void set##NAME(TYPE Val) { SymFlags.set<TYPE>(BITINDEX, LENGTH, Val); }      \
+  TYPE get##NAME() const { return SymFlags.get<TYPE>(BITINDEX, LENGTH); }
+
+  GOFF_SYMBOL_FLAG(FillBytePresence, bool, 0, 1)
+  GOFF_SYMBOL_FLAG(Mangled, bool, 1, 1)
+  GOFF_SYMBOL_FLAG(Renameable, bool, 2, 1)
+  GOFF_SYMBOL_FLAG(RemovableClass, bool, 3, 1)
+  GOFF_SYMBOL_FLAG(ReservedQwords, ESDReserveQwords, 5, 3)
+
+#undef GOFF_SYMBOL_FLAG
+
+constexpr operator uint8_t() const { return static_cast<uint8_t>(SymFlags); }
+};
+
+// Structure for the behavioral attributes. See
+// https://www.ibm.com/docs/en/zos/3.1.0?topic=record-external-symbol-definition-behavioral-attributes
+// for the definition.
+struct BehavioralAttributes {
+  Flags Attr[10];
+
+#define GOFF_BEHAVIORAL_ATTRIBUTE(NAME, TYPE, ATTRIDX, BITINDEX, LENGTH)       \
+  void set##NAME(TYPE Val) { Attr[ATTRIDX].set<TYPE>(BITINDEX, LENGTH, Val); } \
+  TYPE get##NAME() const { return Attr[ATTRIDX].get<TYPE>(BITINDEX, LENGTH); }
+
+  GOFF_BEHAVIORAL_ATTRIBUTE(Amode, GOFF::ESDAmode, 0, 0, 8)
+  GOFF_BEHAVIORAL_ATTRIBUTE(Rmode, GOFF::ESDRmode, 1, 0, 8)
+  GOFF_BEHAVIORAL_ATTRIBUTE(TextStyle, GOFF::ESDTextStyle, 2, 0, 4)
+  GOFF_BEHAVIORAL_ATTRIBUTE(BindingAlgorithm, GOFF::ESDBindingAlgorithm, 2, 4,
+                            4)
+  GOFF_BEHAVIORAL_ATTRIBUTE(TaskingBehavior, GOFF::ESDTaskingBehavior, 3, 0, 3)
+  GOFF_BEHAVIORAL_ATTRIBUTE(ReadOnly, bool, 3, 4, 1)
+  GOFF_BEHAVIORAL_ATTRIBUTE(Executable, GOFF::ESDExecutable, 3, 5, 3)
+  GOFF_BEHAVIORAL_ATTRIBUTE(DuplicateSymbolSeverity,
+                            GOFF::ESDDuplicateSymbolSeverity, 4, 2, 2)
+  GOFF_BEHAVIORAL_ATTRIBUTE(BindingStrength, GOFF::ESDBindingStrength, 4, 4, 4)
+  GOFF_BEHAVIORAL_ATTRIBUTE(LoadingBehavior, GOFF::ESDLoadingBehavior, 5, 0, 2)
+  GOFF_BEHAVIORAL_ATTRIBUTE(COMMON, bool, 5, 2, 1)
+  GOFF_BEHAVIORAL_ATTRIBUTE(IndirectReference, bool, 5, 3, 1)
+  GOFF_BEHAVIORAL_ATTRIBUTE(BindingScope, GOFF::ESDBindingScope, 5, 4, 4)
+  GOFF_BEHAVIORAL_ATTRIBUTE(LinkageType, GOFF::ESDLinkageType, 6, 2, 1)
+  GOFF_BEHAVIORAL_ATTRIBUTE(Alignment, GOFF::ESDAlignment, 6, 3, 5)
+
+#undef GOFF_BEHAVIORAL_ATTRIBUTE
+};
 } // end namespace GOFF
 
 } // end namespace llvm
diff --git a/llvm/include/llvm/MC/MCGOFFSymbolMapper.h b/llvm/include/llvm/MC/MCGOFFSymbolMapper.h
new file mode 100644
index 0000000000000..dbdc1408dab2f
--- /dev/null
+++ b/llvm/include/llvm/MC/MCGOFFSymbolMapper.h
@@ -0,0 +1,148 @@
+//===- MCGOFFSymbolMapper.h - Maps MC section/symbol to GOFF symbols ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Maps a section or a symbol to the GOFF symbols it is composed of, and their
+// attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_MC_MCGOFFSYMBOLMAPPER_H
+#define LLVM_MC_MCGOFFSYMBOLMAPPER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/Support/Alignment.h"
+#include <string>
+#include <utility>
+
+namespace llvm {
+class MCAssembler;
+class MCContext;
+class MCSectionGOFF;
+
+// An "External Symbol Definition" in the GOFF file has a type, and depending on
+// the type a different subset of the fields is used.
+//
+// Unlike other formats, a 2 dimensional structure is used to define the
+// location of data. For example, the equivalent of the ELF .text section is
+// made up of a Section Definition (SD) and a class (Element Definition; ED).
+// The name of the SD symbol depends on the application, while the class has the
+// predefined name C_CODE64.
+//
+// Data can be placed into this structure in 2 ways. First, the data (in a text
+// record) can be associated with an ED symbol. To refer to data, a Label
+// Definition (LD) is used to give an offset into the data a name. When binding,
+// the whole data is pulled into the resulting executable, and the addresses
+// given by the LD symbols are resolved.
+//
+// The alternative is to use a Part Defiition (PR). In this case, the data (in a
+// text record) is associated with the part. When binding, only the data of
+// referenced PRs is pulled into the resulting binary.
+//
+// Both approaches are used, which means that the equivalent of a section in ELF
+// results in 3 GOFF symbol, either SD/ED/LD or SD/ED/PR. Moreover, certain
+// sections are fine with just defining SD/ED symbols. The SymbolMapper takes
+// care of all those details.
+
+// Attributes for SD symbols.
+struct SDAttr {
+  GOFF::ESDTaskingBehavior TaskingBehavior = GOFF::ESD_TA_Unspecified;
+  GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
+};
+
+// Attributes for ED symbols.
+struct EDAttr {
+  bool IsReadOnly = false;
+  GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
+  GOFF::ESDAmode Amode;
+  GOFF::ESDRmode Rmode;
+  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_NormalName;
+  GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented;
+  GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate;
+  GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial;
+  GOFF::ESDReserveQwords ReservedQwords = GOFF::ESD_RQ_0;
+  GOFF::ESDAlignment Alignment = GOFF::ESD_ALIGN_Doubleword;
+};
+
+// Attributes for LD symbols.
+struct LDAttr {
+  bool IsRenamable = false;
+  GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
+  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_NormalName;
+  GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
+  GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
+  GOFF::ESDAmode Amode;
+  GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
+};
+
+// Attributes for PR symbols.
+struct PRAttr {
+  bool IsRenamable = false;
+  bool IsReadOnly = false; // ???? Not documented.
+  GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
+  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_NormalName;
+  GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
+  GOFF::ESDAmode Amode;
+  GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
+  GOFF::ESDDuplicateSymbolSeverity DuplicateSymbolSeverity =
+      GOFF::ESD_DSS_NoWarning;
+  GOFF::ESDAlignment Alignment = GOFF::ESD_ALIGN_Byte;
+  uint32_t SortKey = 0;
+};
+
+struct GOFFSectionData {
+  // Name and attributes of SD symbol.
+  StringRef SDName;
+  SDAttr SDAttributes;
+
+  // Name and attributes of ED symbol.
+  StringRef EDName;
+  EDAttr EDAttributes;
+
+  // Name and attributes of LD or PR symbol.
+  StringRef LDorPRName;
+  LDAttr LDAttributes;
+  PRAttr PRAttributes;
+
+  // Indicates if there is a LD or PR symbol.
+  enum { None, LD, PR } Tag;
+
+  // Indicates if the SD symbol is to root symbol (aka the Csect Code).
+  bool IsSDRootSD;
+};
+
+class GOFFSymbolMapper {
+  MCContext &Ctx;
+
+  std::string RootSDName;
+  SDAttr RootSDAttributes;
+
+  std::string ADALDName;
+
+  StringRef BaseName;
+
+  bool IsCsectCodeNameEmpty;
+  bool Is64Bit;
+  bool UsesXPLINK;
+
+public:
+  GOFFSymbolMapper(MCContext &Ctx);
+  GOFFSymbolMapper(MCAssembler &Asm);
+
+  // Required order: .text first, then .ada.
+  std::pair<GOFFSectionData, bool> getSection(const MCSectionGOFF &Section);
+
+  void setBaseName();
+  void determineRootSD(StringRef CSectCodeName);
+  llvm::StringRef getRootSDName() const;
+  const SDAttr &getRootSD() const;
+};
+
+} // namespace llvm
+
+#endif
diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt
index f49f14c848b90..967ec73a2be5b 100644
--- a/llvm/lib/MC/CMakeLists.txt
+++ b/llvm/lib/MC/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_component_library(LLVMMC
   MCExpr.cpp
   MCFragment.cpp
   MCGOFFStreamer.cpp
+  MCGOFFSymbolMapper.cpp
   MCInst.cpp
   MCInstPrinter.cpp
   MCInstrAnalysis.cpp
diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp
index efaf5ff006ddc..92603c6fb1002 100644
--- a/llvm/lib/MC/GOFFObjectWriter.cpp
+++ b/llvm/lib/MC/GOFFObjectWriter.cpp
@@ -13,7 +13,11 @@
 #include "llvm/BinaryFormat/GOFF.h"
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCGOFFObjectWriter.h"
+#include "llvm/MC/MCGOFFSymbolMapper.h"
+#include "llvm/MC/MCSectionGOFF.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/raw_ostream.h"
@@ -23,44 +27,13 @@ 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);
+constexpr uint8_t RecContinued = GOFF::Flags(7, 1, 1);
 
 // Flag: This record is a continuation.
-constexpr uint8_t RecContinuation = Flags(6, 1, 1);
+constexpr uint8_t RecContinuation = GOFF::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 begin of a new
@@ -223,13 +196,113 @@ void GOFFOstream::finalizeRecord() {
 }
 
 namespace {
+// A GOFFSymbol holds all the data required for writing an ESD record.
+class GOFFSymbol {
+public:
+  std::string Name;
+  uint32_t EsdId;
+  uint32_t ParentEsdId;
+  uint64_t Offset = 0; // Offset of the symbol into the section. LD only.
+                       // Offset is only 32 bit, the larger type is used to
+                       // enable error checking.
+  GOFF::ESDSymbolType SymbolType;
+  GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_ProgramManagementBinder;
+
+  GOFF::BehavioralAttributes BehavAttrs;
+  GOFF::SymbolFlags SymbolFlags;
+  uint32_t SortKey = 0;
+  uint32_t SectionLength = 0;
+  uint32_t ADAEsdId = 0;
+  uint32_t EASectionEDEsdId = 0;
+  uint32_t EASectionOffset = 0;
+  uint8_t FillByteValue = 0;
+
+  GOFFSymbol() : EsdId(0), ParentEsdId(0) {}
+
+  GOFFSymbol(StringRef Name, uint32_t EsdID, const SDAttr &Attr)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(0),
+        SymbolType(GOFF::ESD_ST_SectionDefinition) {
+    BehavAttrs.setTaskingBehavior(Attr.TaskingBehavior);
+    BehavAttrs.setBindingScope(Attr.BindingScope);
+  }
+
+  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
+             const EDAttr &Attr)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
+        SymbolType(GOFF::ESD_ST_ElementDefinition) {
+    this->NameSpace = Attr.NameSpace;
+    // TODO Do we need/should set the "mangled" flag?
+    SymbolFlags.setFillBytePresence(1);
+    SymbolFlags.setReservedQwords(Attr.ReservedQwords);
+    BehavAttrs.setReadOnly(Attr.IsReadOnly);
+    BehavAttrs.setExecutable(Attr.Executable);
+    BehavAttrs.setAmode(Attr.Amode);
+    BehavAttrs.setRmode(Attr.Rmode);
+    BehavAttrs.setTextStyle(Attr.TextStyle);
+    BehavAttrs.setBindingAlgorithm(Attr.BindAlgorithm);
+    BehavAttrs.setLoadingBehavior(Attr.LoadBehavior);
+    BehavAttrs.setAlignment(Attr.Alignment);
+  }
+
+  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
+             const LDAttr &Attr)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
+        SymbolType(GOFF::ESD_ST_LabelDefinition) {
+    this->NameSpace = Attr.NameSpace;
+    SymbolFlags.setRenameable(Attr.IsRenamable);
+    BehavAttrs.setExecutable(Attr.Executable);
+    BehavAttrs.setBindingStrength(Attr.BindingStrength);
+    BehavAttrs.setLinkageType(Attr.Linkage);
+    BehavAttrs.setAmode(Attr.Amode);
+    BehavAttrs.setBindingScope(Attr.BindingScope);
+  }
+
+  GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID,
+             const PRAttr &Attr)
+      : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID),
+        SymbolType(GOFF::ESD_ST_PartReference) {
+    this->NameSpace = Attr.NameSpace;
+    SymbolFlags.setRenameable(Attr.IsRenamable);
+    BehavAttrs.setExecutable(Attr.Executable);
+    BehavAttrs.setAlignment(Attr.Alignment);
+    BehavAttrs.setAmode(Attr.Amode);
+    BehavAttrs.setLinkageType(Attr.Linkage);
+    BehavAttrs.setBindingScope(Attr.BindingScope);
+    BehavAttrs.setDuplicateSymbolSeverity(Attr.DuplicateSymbolSeverity);
+    BehavAttrs.setReadOnly(Attr.IsReadOnly);
+  }
+};
+
 class GOFFWriter {
   GOFFOstream OS;
   [[maybe_unused]] MCAssembler &Asm;
 
+  /// Mapping from MCSectionGOFF/MCSymbolGOFF to GOFF symbols and attributes.
+  GOFFSymbolMapper SymbolMapper;
+
+  /// Counter for symbol id's.
+  uint32_t EsdIdCounter = 0;
+
+  /// Id's of some special symbols.
+  uint32_t RootSDEsdId = 0;
+  uint32_t ADAEsdId = 0;
+
   void writeHeader();
+  void writeSymbol(const GOFFSymbol &Symbol);
   void writeEnd();
 
+  GOFFSymbol createGOFFSymbol(StringRef Name, const SDAttr &Attr);
+  GOFFSymbol createGOFFSymbol(StringRef Name, const EDAttr &Attr,
+                              uint32_t ParentEsdId);
+  GOFFSymbol createGOFFSymbol(StringRef Name, const LDAttr &Attr,
+                              uint32_t ParentEsdId);
+  GOFFSymbol createGOFFSymbol(StringRef Name, const PRAttr &Attr,
+                              uint32_t ParentEsdId);
+
+  void defineRootSymbol(const MCSectionGOFF *Text);
+  void defineSectionSymbols(const MCSectionGOFF &Section);
+  void defineSymbols();
+
 public:
   GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm);
   uint64_t writeObject();
@@ -237,7 +310,108 @@ class GOFFWriter {
 } // namespace
 
 GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm)
-    : OS(OS), Asm(Asm) {}
+    : OS(OS), Asm(Asm), SymbolMapper(Asm) {}
+
+GOFFSymbol GOFFWriter::createGOFFSymbol(StringRef Name, const SDAttr &Attr) {
+  return GOFFSymbol(Name, ++EsdIdCounter, Attr);
+}
+
+GOFFSymbol GOFFWriter::createGOFFSymbol(StringRef Name, const EDAttr &Attr,
+                                        uint32_t ParentEsdId) {
+  return GOFFSymbol(Name, ++EsdIdCounter, ParentEsdId, Attr);
+}
+
+GOFFSymbol GOFFWriter::createGOFFSymbol(StringRef Name, const LDAttr &Attr,
+                                        uint32_t ParentEsdId) {
+  return GOFFSymbol(Name, ++EsdIdCounter, ParentEsdId, Attr);
+}
+
+GOFFSymbol GOFFWriter::createGOFFSymbol(StringRef Name, const PRAttr &Attr,
+                                        uint32_t ParentEsdId) {
+  return GOFFSymbol(Name, ++EsdIdCounter, ParentEsdId, Attr);
+}
+
+void GOFFWriter::defineRootSymbol(const MCSectionGOFF *Text) {
+  // There is always a text section except for DWARF unit tests.
+  SymbolMapper.determineRootSD("");
+  GOFFSymbol RootSD =
+      createGOFFSymbol(SymbolMapper.getRootSDName(), SymbolMapper.getRootSD());
+  writeSymbol(RootSD);
+  RootSDEsdId = RootSD.EsdId;
+}
+
+void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) {
+  auto [GOFFSectionData, Found] = SymbolMapper.getSection(Section);
+  if (Found) {
+    uint32_t SDEsdId = RootSDEsdId;
+    if (!GOFFSectionData.IsSDRootSD) {
+      GOFFSymbol SD = createGOFFSymbol(GOFFSectionData.SDName,
+                                       GOFFSectionData.SDAttributes);
+      SDEsdId = SD.EsdId;
+      writeSymbol(SD);
+    }
+
+    GOFFSymbol ED = createGOFFSymbol(GOFFSectionData.EDName,
+                                     GOFFSectionData.EDAttributes, SDEsdId);
+    if (GOFFSectionData.Tag == GOFFSectionData::None ||
+        GOFFSectionData.Tag == GOFFSectionData::LD) {
+      ED.SectionLength = Asm.getSectionAddressSize(Section);
+    }
+    writeSymbol(ED);
+
+    if (GOFFSectionData.Tag == GOFFSectionData::LD) {
+      GOFFSymbol LD = createGOFFSymbol(GOFFSectionData.LDorPRName,
+                                       GOFFSectionData.LDAttributes, ED.EsdId);
+      if (Section.isText())
+        LD.ADAEsdId = ADAEsdId;
+      writeSymbol(LD);
+    }
+
+    if (GOFFSectionData.Tag == GOFFSectionData::PR) {
+      GOFFSymbol PR = createGOFFSymbol(GOFFSectionData.LDorPRName,
+                                       GOFFSectionData.PRAttributes, ED.EsdId);
+      PR.SectionLength = Asm.getSectionAddressSize(Section);
+      if (Section.getName() == ".ada") {
+        // We cannot have a zero-length section for data.  If we do,
+        // artificially inflate it. Use 2 bytes to avoid odd alignments. Note:
+        // if this is ever changed, you will need to update the code in
+        // SystemZAsmPrinter::emitCEEMAIN and SystemZAsmPrinter::emitCELQMAIN to
+        // generate -1 if there is no ADA
+        if (!PR.SectionLength)
+          PR.SectionLength = 2;
+        ADAEsdId = PR.EsdId;
+      }
+      writeSymbol(PR);
+    }
+    return;
+  }
+  // TODO It is possible to get here. This will be handled later.
+}
+
+void GOFFWriter::defineSymbols() {
+  // Search for .text and .ada sections. These should be the first sections in
+  // the list, so the loop should be cheap.
+  MCSectionGOFF *Text = nullptr;
+  MCSectionGOFF *ADA = nullptr;
+  for (MCSection &S : Asm) {
+    if (S.getName() == ".text")
+      Text = &cast<MCSectionGOFF>(S);
+    if (S.getName() == ".ada")
+      ADA = &cast<MCSectionGOFF>(S);
+  }
+  defineRootSymbol(Text);
+  if (ADA)
+    defineSectionSymbols(*ADA);
+  if (Text)
+    defineSectionSymbols(*Text);
+
+  // Process the other sections.
+  for (MCSection &S : Asm) {
+    auto &Section = cast<MCSectionGOFF>(S);
+    if (Text != &Section && ADA != &Section)
+      defineSectionSymbols(Section);
+  }
+}
 
 void GOFFWriter::writeHeader() {
   OS.newRecord(GOFF::RT_HDR);
@@ -253,6 +427,45 @@ void GOFFWriter::writeHeader() {
   OS.write_zeros(6);       // Reserved
 }
 
+void GOFFWriter::writeSymbol(const GOFFSymbol &Symbol) {
+  if (Symbol.Offset >= (((uint64_t)1) << 31))
+    report_fatal_error("ESD offset outof range");
+
+  // All symbol names are in EBCDIC.
+  SmallString<256> Name;
+  ConverterEBCDIC::convertToEBCDIC(Symbol.Name, Name);
+
+  // Check length here since this number is technically signed but we need uint
+  // for writing to records.
+  if (Name.size() >= GOFF::MaxDataLength)
+    report_fatal_error("Symbol max name length exceeded");
+  uint16_t NameLength = Name.size();
+
+  OS.newRecord(GOFF::RT_ESD);
+  OS.writebe<uint8_t>(Symbol.SymbolType);   // Symbol Type
+  OS.writebe<uint32_t>(Symbol.EsdId);       // ESDID
+  OS.writebe<uint32_t>(Symbol.ParentEsdId); // Parent or Owning ESDID
+  OS.writebe<uint32_t>(0);                  // Reserved
+  OS.writebe<uint32_t>(
+      static_cast<uint32_t>(Symbol.Offset));     // Offset or Address
+  OS.writebe<uint32_t>(0);                       // Reserved
+  OS.writebe<uint32_t>(Symbol.SectionLength);    // Length
+  OS.writebe<uint32_t>(Symbol.EASectionEDEsdId); // Extended Attribute ESDID
+  OS.writebe<uint32_t>(Symbol.EASectionOffset);  // Extended Attribute Offset
+  OS.writebe<uint32_t>(0);                       // Reserved
+  OS.writebe<uint8_t>(Symbol.NameSpace);         // Name Space ID
+  OS.writebe<uint8_t>(Symbol.SymbolFlags);       // Flags
+  OS.writebe<uint8_t>(Symbol.FillByteValue);     // Fill-Byte Value
+  OS.writebe<uint8_t>(0);                        // Reserved
+  OS.writebe<uint32_t>(Symbol.ADAEsdId);         // ADA ESDID
+  OS.writebe<uint32_t>(Symbol.SortKey);          // Sort Priority
+  OS.writebe<uint64_t>(0);                       // Reserved
+  for (auto F : Symbol.BehavAttrs.Attr)
+    OS.writebe<uint8_t>(F);          // Behavioral Attributes
+  OS.writebe<uint16_t>(NameLength);  // Name Length
+  OS.write(Name.data(), NameLength); // Name
+}
+
 void GOFFWriter::writeEnd() {
   uint8_t F = GOFF::END_EPR_None;
   uint8_t AMODE = 0;
@@ -261,9 +474,9 @@ void GOFFWriter::writeEnd() {
   // TODO Set Flags/AMODE/ESDID for entry point.
 
   OS.newRecord(GOFF::RT_END);
-  OS.writebe<uint8_t>(Flags(6, 2, F)); // Indicator flags
-  OS.writebe<uint8_t>(AMODE);          // AMODE
-  OS.write_zeros(3);                   // Reserved
+  OS.writebe<uint8_t>(GOFF::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.
@@ -273,6 +486,9 @@ void GOFFWriter::writeEnd() {
 
 uint64_t GOFFWriter::writeObject() {
   writeHeader();
+
+  defineSymbols();
+
   writeEnd();
 
   // Make sure all records are written.
diff --git a/llvm/lib/MC/MCGOFFSymbolMapper.cpp b/llvm/lib/MC/MCGOFFSymbolMapper.cpp
new file mode 100644
index 0000000000000..57012c34a94f1
--- /dev/null
+++ b/llvm/lib/MC/MCGOFFSymbolMapper.cpp
@@ -0,0 +1,203 @@
+//===- MCGOFFSymbolMapper.cpp - Maps MC section/symbol to GOFF symbols ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Maps a section or a symbol to the GOFF symbols it is composed of, and their
+// attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCGOFFSymbolMapper.h"
+#include "llvm/BinaryFormat/GOFF.h"
+#include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCSectionGOFF.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+
+namespace {
+const StringLiteral CODE[2]{"C_CODE", "C_CODE64"};
+const StringLiteral WSA[2]{"C_WSA", "C_WSA64"};
+const StringLiteral PPA2[2]{"C_@@PPA2", "C_@@QPPA2"};
+
+const GOFF::ESDAmode AMODE[2]{GOFF::ESD_AMODE_ANY, GOFF::ESD_AMODE_64};
+const GOFF::ESDRmode RMODE[2]{GOFF::ESD_RMODE_31, GOFF::ESD_RMODE_64};
+
+const GOFF::ESDLinkageType LINKAGE[2]{GOFF::ESD_LT_OS, GOFF::ESD_LT_XPLink};
+} // namespace
+
+GOFFSymbolMapper::GOFFSymbolMapper(MCContext &Ctx) : Ctx(Ctx) {
+  IsCsectCodeNameEmpty = true;
+  Is64Bit = true;
+  UsesXPLINK = true;
+}
+
+GOFFSymbolMapper::GOFFSymbolMapper(MCAssembler &Asm)
+    : GOFFSymbolMapper(Asm.getContext()) {
+  if (!Asm.getWriter().getFileNames().empty())
+    BaseName =
+        sys::path::stem((*(Asm.getWriter().getFileNames().begin())).first);
+}
+
+void GOFFSymbolMapper::determineRootSD(StringRef CSectCodeName) {
+  IsCsectCodeNameEmpty = CSectCodeName.empty();
+  if (IsCsectCodeNameEmpty) {
+    RootSDName = BaseName.str().append("#C");
+  } else {
+    RootSDName = CSectCodeName;
+  }
+  RootSDAttributes = {GOFF::ESD_TA_Rent, IsCsectCodeNameEmpty
+                                             ? GOFF::ESD_BSC_Section
+                                             : GOFF::ESD_BSC_Unspecified};
+}
+
+llvm::StringRef GOFFSymbolMapper::getRootSDName() const { return RootSDName; }
+
+const SDAttr &GOFFSymbolMapper::getRootSD() const { return RootSDAttributes; }
+
+std::pair<GOFFSectionData, bool>
+GOFFSymbolMapper::getSection(const MCSectionGOFF &Section) {
+  // Look up GOFFSection from name in MCSectionGOFF.
+  // Customize result, e.g. csect names, 32/64 bit, etc.
+  GOFFSectionData GOFFSec;
+  if (Section.getName() == ".text") {
+    GOFFSec.SDName = RootSDName;
+    GOFFSec.SDAttributes = RootSDAttributes;
+    GOFFSec.IsSDRootSD = true;
+    GOFFSec.EDName = CODE[Is64Bit];
+    // The GOFF alignment is encoded as log_2 value.
+    uint8_t Log = Log2(Section.getAlign());
+    assert(Log <= GOFF::ESD_ALIGN_4Kpage && "Alignment too large");
+    GOFFSec.EDAttributes = {true,
+                            GOFF::ESD_EXE_CODE,
+                            AMODE[Is64Bit],
+                            RMODE[Is64Bit],
+                            GOFF::ESD_NS_NormalName,
+                            GOFF::ESD_TS_ByteOriented,
+                            GOFF::ESD_BA_Concatenate,
+                            GOFF::ESD_LB_Initial,
+                            GOFF::ESD_RQ_0,
+                            static_cast<GOFF::ESDAlignment>(Log)};
+    GOFFSec.LDorPRName = GOFFSec.SDName;
+    GOFFSec.LDAttributes = {false,
+                            GOFF::ESD_EXE_CODE,
+                            GOFF::ESD_NS_NormalName,
+                            GOFF::ESD_BST_Strong,
+                            LINKAGE[UsesXPLINK],
+                            AMODE[Is64Bit],
+                            IsCsectCodeNameEmpty ? GOFF::ESD_BSC_Section
+                                                 : GOFF::ESD_BSC_Library};
+    GOFFSec.Tag = GOFFSectionData::LD;
+  } else if (Section.getName() == ".ada") {
+    assert(!RootSDName.empty() && "RootSD must be defined already");
+    GOFFSec.SDName = RootSDName;
+    GOFFSec.SDAttributes = RootSDAttributes;
+    GOFFSec.IsSDRootSD = true;
+    GOFFSec.EDName = WSA[Is64Bit];
+    GOFFSec.EDAttributes = {false,
+                            GOFF::ESD_EXE_DATA,
+                            AMODE[Is64Bit],
+                            RMODE[Is64Bit],
+                            GOFF::ESD_NS_Parts,
+                            GOFF::ESD_TS_ByteOriented,
+                            GOFF::ESD_BA_Merge,
+                            GOFF::ESD_LB_Deferred,
+                            GOFF::ESD_RQ_1,
+                            Is64Bit ? GOFF::ESD_ALIGN_Quadword
+                                    : GOFF::ESD_ALIGN_Doubleword};
+    ADALDName = BaseName.str().append("#S");
+    GOFFSec.LDorPRName = ADALDName;
+    GOFFSec.PRAttributes = {false,
+                            false,
+                            GOFF::ESD_EXE_DATA,
+                            GOFF::ESD_NS_Parts,
+                            GOFF::ESD_LT_XPLink,
+                            AMODE[Is64Bit],
+                            GOFF::ESD_BSC_Section,
+                            GOFF::ESD_DSS_NoWarning,
+                            Is64Bit ? GOFF::ESD_ALIGN_Quadword
+                                    : GOFF::ESD_ALIGN_Doubleword,
+                            0};
+    GOFFSec.Tag = GOFFSectionData::PR;
+  } else if (Section.getName().starts_with(".gcc_exception_table")) {
+    GOFFSec.SDName = RootSDName;
+    GOFFSec.SDAttributes = RootSDAttributes;
+    GOFFSec.IsSDRootSD = true;
+    GOFFSec.EDName = WSA[Is64Bit];
+    GOFFSec.EDAttributes = {false,
+                            GOFF::ESD_EXE_DATA,
+                            AMODE[Is64Bit],
+                            RMODE[Is64Bit],
+                            GOFF::ESD_NS_Parts,
+                            GOFF::ESD_TS_ByteOriented,
+                            GOFF::ESD_BA_Merge,
+                            UsesXPLINK ? GOFF::ESD_LB_Initial
+                                       : GOFF::ESD_LB_Deferred,
+                            GOFF::ESD_RQ_0,
+                            GOFF::ESD_ALIGN_Doubleword};
+    GOFFSec.LDorPRName = Section.getName();
+    GOFFSec.PRAttributes = {true,
+                            false,
+                            GOFF::ESD_EXE_Unspecified,
+                            GOFF::ESD_NS_Parts,
+                            LINKAGE[UsesXPLINK],
+                            AMODE[Is64Bit],
+                            GOFF::ESD_BSC_Section,
+                            GOFF::ESD_DSS_NoWarning,
+                            GOFF::ESD_ALIGN_Fullword,
+                            0};
+    GOFFSec.Tag = GOFFSectionData::PR;
+  } else if (Section.getName() == ".ppa2list") {
+    GOFFSec.SDName = RootSDName;
+    GOFFSec.SDAttributes = RootSDAttributes;
+    GOFFSec.IsSDRootSD = true;
+    GOFFSec.EDName = PPA2[Is64Bit];
+    GOFFSec.EDAttributes = {true,
+                            GOFF::ESD_EXE_DATA,
+                            AMODE[Is64Bit],
+                            RMODE[Is64Bit],
+                            GOFF::ESD_NS_Parts,
+                            GOFF::ESD_TS_ByteOriented,
+                            GOFF::ESD_BA_Merge,
+                            GOFF::ESD_LB_Initial,
+                            GOFF::ESD_RQ_0,
+                            GOFF::ESD_ALIGN_Doubleword};
+    GOFFSec.LDorPRName = ".&ppa2";
+    GOFFSec.PRAttributes = {true,
+                            false,
+                            GOFF::ESD_EXE_Unspecified,
+                            GOFF::ESD_NS_Parts,
+                            GOFF::ESD_LT_OS,
+                            AMODE[Is64Bit],
+                            GOFF::ESD_BSC_Section,
+                            GOFF::ESD_DSS_NoWarning,
+                            GOFF::ESD_ALIGN_Doubleword,
+                            0};
+    GOFFSec.Tag = GOFFSectionData::PR;
+  } else if (Section.getName() == ".idrl") {
+    GOFFSec.SDName = RootSDName;
+    GOFFSec.SDAttributes = RootSDAttributes;
+    GOFFSec.IsSDRootSD = true;
+    GOFFSec.EDName = "B_IDRL";
+    GOFFSec.EDAttributes = {true,
+                            GOFF::ESD_EXE_Unspecified,
+                            AMODE[Is64Bit],
+                            RMODE[Is64Bit],
+                            GOFF::ESD_NS_NormalName,
+                            GOFF::ESD_TS_Structured,
+                            GOFF::ESD_BA_Concatenate,
+                            GOFF::ESD_LB_NoLoad,
+                            GOFF::ESD_RQ_0,
+                            GOFF::ESD_ALIGN_Doubleword};
+    GOFFSec.Tag = GOFFSectionData::None;
+  } else
+    return std::pair(GOFFSec, false);
+  return std::pair(GOFFSec, true);
+}
diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp
index ab7552ca01061..a514a07ef7d3e 100644
--- a/llvm/lib/MC/MCObjectFileInfo.cpp
+++ b/llvm/lib/MC/MCObjectFileInfo.cpp
@@ -557,7 +557,7 @@ void MCObjectFileInfo::initGOFFMCObjectFileInfo(const Triple &T) {
       Ctx->getGOFFSection(".ppa2list", SectionKind::getData(), nullptr);
 
   ADASection = Ctx->getGOFFSection(".ada", SectionKind::getData(), nullptr);
-  IDRLSection = Ctx->getGOFFSection("B_IDRL", SectionKind::getData(), nullptr);
+  IDRLSection = Ctx->getGOFFSection(".idrl", SectionKind::getData(), nullptr);
 }
 
 void MCObjectFileInfo::initCOFFMCObjectFileInfo(const Triple &T) {
diff --git a/llvm/test/CodeGen/SystemZ/zos-ppa2.ll b/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
index 07025091fb240..82f416ddb8bf9 100644
--- a/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
+++ b/llvm/test/CodeGen/SystemZ/zos-ppa2.ll
@@ -19,7 +19,7 @@
 ; CHECK:    .quad   L#PPA2-CELQSTRT                 * A(PPA2-CELQSTRT)
 ; CHECK: L#PPA1_void_test_0:
 ; CHECK:    .long   L#PPA2-L#PPA1_void_test_0       * Offset to PPA2
-; CHECK:    .section    "B_IDRL"
+; CHECK:    .section    ".idrl"
 ; CHECK:    .byte   0
 ; CHECK:    .byte   3
 ; CHECK:    .short  30
diff --git a/llvm/test/MC/GOFF/section.ll b/llvm/test/MC/GOFF/section.ll
new file mode 100644
index 0000000000000..a0c64f0b83013
--- /dev/null
+++ b/llvm/test/MC/GOFF/section.ll
@@ -0,0 +1,73 @@
+; RUN: llc <%s --mtriple s390x-ibm-zos --filetype=obj -o - | \
+; RUN:   od -Ax -tx1 -v | FileCheck --ignore-case %s
+; REQUIRES: systemz-registered-target
+
+source_filename = "test.ll"
+
+declare void @other(...)
+
+define void @me() {
+entry:
+  tail call void @other()
+  ret void
+}
+
+; Header record:
+;  03 is prefix byte
+;  f. is header type
+;  .0 is flag
+;  00 is version
+; The 1 at offset 0x33 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
+
+; ESD record, type SD.
+;  03 is prefix byte
+;  0. is header type
+;  .0 is flag
+;  00 is version
+;  00 is type = SD
+; The 01 at offset 0x57 is the id of the symbol.
+; The 60 at offset 0x89 is the tasking behavior.
+; The 01 at offset 0x91 is the binding scope.
+; The name begins at offset 0x97, and is test#C.
+; CHECK: 0000050 03 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
+; CHECK: 0000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK: 0000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK: 0000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60
+; CHECK: 0000090 00 01 00 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00
+
+; ESD record, type ED.
+; The name is C_WSA64.
+; CHECK: 00000a0 03 00 00 01 00 00 00 02 00 00 00 01 00 00 00 00
+; CHECK: 00000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK: 00000c0 00 00 00 00 00 00 00 00 03 81 00 00 00 00 00 00
+; CHECK: 00000d0 00 00 00 00 00 00 00 00 00 00 00 00 04 04 01 01
+; CHECK: 00000e0 00 40 04 00 00 00 00 07 c3 6d e6 e2 c1 f6 f4 00
+
+; ESD record, type PR.
+; The name is test#S.
+; CHECK: 00000f0 03 00 00 03 00 00 00 03 00 00 00 02 00 00 00 00
+; CHECK: 0000100 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00
+; CHECK: 0000110 00 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00
+; CHECK: 0000120 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 01
+; CHECK: 0000130 00 01 24 00 00 00 00 06 a3 85 a2 a3 7b e2 00 00
+
+; ESD record, type ED.
+; The name is C_CODE64.
+; CHECK: 0000140 03 00 00 01 00 00 00 04 00 00 00 01 00 00 00 00
+; CHECK: 0000150 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00
+; CHECK: 0000160 00 00 00 00 00 00 00 00 01 80 00 00 00 00 00 00
+; CHECK: 0000170 00 00 00 00 00 00 00 00 00 00 00 00 04 04 00 0a
+; CHECK: 0000180 00 00 04 00 00 00 00 08 c3 6d c3 d6 c4 c5 f6 f4
+
+; ESD record, type LD.
+; The name is test#C.
+; CHECK: 0000190 03 00 00 02 00 00 00 05 00 00 00 04 00 00 00 00
+; CHECK: 00001a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+; CHECK: 00001b0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 03
+; CHECK: 00001c0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02
+; CHECK: 00001d0 00 01 20 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00



More information about the llvm-branch-commits mailing list