diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h index f8b7e05b3719b..3b9c4670399c9 100644 --- a/llvm/include/llvm/MC/MCDirectives.h +++ b/llvm/include/llvm/MC/MCDirectives.h @@ -48,6 +48,8 @@ enum MCSymbolAttr { MCSA_WeakDefAutoPrivate, ///< .weak_def_can_be_hidden (MachO) MCSA_WeakAntiDep, ///< .weak_anti_dep (COFF) MCSA_Memtag, ///< .memtag (ELF) + MCSA_OSLinkage, ///< symbol uses OS linkage (GOFF) + MCSA_XPLinkage, ///< symbol uses XP linkage (GOFF) }; enum MCDataRegionType { diff --git a/llvm/include/llvm/MC/MCGOFFAttributes.h b/llvm/include/llvm/MC/MCGOFFAttributes.h index c996f0cae2c69..e771f36f35346 100644 --- a/llvm/include/llvm/MC/MCGOFFAttributes.h +++ b/llvm/include/llvm/MC/MCGOFFAttributes.h @@ -80,6 +80,15 @@ struct PRAttr { uint32_t SortKey = 0; }; +// Attributes for ER symbols. +struct ERAttr { + GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified; + GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong; + GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink; + GOFF::ESDAmode Amode; + GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified; +}; + // Predefined GOFF class names. constexpr StringLiteral CLASS_CODE = "C_CODE64"; constexpr StringLiteral CLASS_WSA = "C_WSA64"; diff --git a/llvm/include/llvm/MC/MCGOFFObjectWriter.h b/llvm/include/llvm/MC/MCGOFFObjectWriter.h index ec07637dd2847..90887b296d46f 100644 --- a/llvm/include/llvm/MC/MCGOFFObjectWriter.h +++ b/llvm/include/llvm/MC/MCGOFFObjectWriter.h @@ -14,6 +14,7 @@ namespace llvm { class MCObjectWriter; +class MCSectionGOFF; class raw_pwrite_stream; class MCGOFFObjectTargetWriter : public MCObjectTargetWriter { @@ -37,11 +38,16 @@ class GOFFObjectWriter : public MCObjectWriter { // The stream used to write the GOFF records. raw_pwrite_stream &OS; + // The RootSD section. + MCSectionGOFF *RootSD = nullptr; + public: GOFFObjectWriter(std::unique_ptr MOTW, raw_pwrite_stream &OS); ~GOFFObjectWriter() override; + void setRootSD(MCSectionGOFF *RootSD) { this->RootSD = RootSD; } + // Implementation of the MCObjectWriter interface. void recordRelocation(const MCFragment &F, const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) override {} diff --git a/llvm/include/llvm/MC/MCGOFFStreamer.h b/llvm/include/llvm/MC/MCGOFFStreamer.h index 8888d9e7bdbb3..622fca0c5e70f 100644 --- a/llvm/include/llvm/MC/MCGOFFStreamer.h +++ b/llvm/include/llvm/MC/MCGOFFStreamer.h @@ -14,6 +14,7 @@ namespace llvm { class GOFFObjectWriter; +class MCSymbolGOFF; class MCGOFFStreamer : public MCObjectStreamer { @@ -24,15 +25,20 @@ class MCGOFFStreamer : public MCObjectStreamer { ~MCGOFFStreamer() override; + void finishImpl() override; + void changeSection(MCSection *Section, uint32_t Subsection = 0) override; GOFFObjectWriter &getWriter(); - bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { - return false; - } + void emitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; + + bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; + void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) override {} + + void emitExterns(); }; } // end namespace llvm diff --git a/llvm/include/llvm/MC/MCSymbolGOFF.h b/llvm/include/llvm/MC/MCSymbolGOFF.h index 11d4df4aea634..61a5db3d83540 100644 --- a/llvm/include/llvm/MC/MCSymbolGOFF.h +++ b/llvm/include/llvm/MC/MCSymbolGOFF.h @@ -23,12 +23,19 @@ namespace llvm { class MCSymbolGOFF : public MCSymbol { // Associated data area of the section. Needs to be emitted first. - MCSectionGOFF *ADA; + MCSectionGOFF *ADA = nullptr; GOFF::LDAttr LDAttributes; + GOFF::ERAttr ERAttributes; + + GOFF::ESDExecutable CodeData = GOFF::ESDExecutable::ESD_EXE_Unspecified; + GOFF::ESDLinkageType Linkage = GOFF::ESDLinkageType::ESD_LT_XPLink; enum SymbolFlags : uint16_t { - SF_LD = 0x01, // LD attributes are set. + SF_LD = 0x01, // LD attributes are set. + SF_ER = 0x02, // ER attributes are set. + SF_Hidden = 0x04, // Symbol is hidden, aka not exported. + SF_Weak = 0x08, // Symbol is weak. }; public: @@ -39,14 +46,43 @@ class MCSymbolGOFF : public MCSymbol { modifyFlags(SF_LD, SF_LD); LDAttributes = Attr; } - GOFF::LDAttr getLDAttributes() const { return LDAttributes; } + const GOFF::LDAttr &getLDAttributes() const { return LDAttributes; } + GOFF::LDAttr &getLDAttributes() { return LDAttributes; } bool hasLDAttributes() const { return getFlags() & SF_LD; } + void setERAttributes(GOFF::ERAttr Attr) { + modifyFlags(SF_ER, SF_ER); + ERAttributes = Attr; + } + const GOFF::ERAttr &getERAttributes() const { return ERAttributes; } + GOFF::ERAttr &getERAttributes() { return ERAttributes; } + bool hasERAttributes() const { return getFlags() & SF_ER; } + void setADA(MCSectionGOFF *AssociatedDataArea) { ADA = AssociatedDataArea; AssociatedDataArea->RequiresNonZeroLength = true; } MCSectionGOFF *getADA() const { return ADA; } + + bool isExternal() const { return IsExternal; } + void setExternal(bool Value) const { IsExternal = Value; } + + void setHidden(bool Value = true) { + modifyFlags(Value ? SF_Hidden : 0, SF_Hidden); + } + bool isHidden() const { return getFlags() & SF_Hidden; } + bool isExported() const { return !isHidden(); } + + void setWeak(bool Value = true) { modifyFlags(Value ? SF_Weak : 0, SF_Weak); } + bool isWeak() const { return getFlags() & SF_Weak; } + + void setCodeData(GOFF::ESDExecutable Value) { CodeData = Value; } + GOFF::ESDExecutable getCodeData() const { return CodeData; } + + void setLinkage(GOFF::ESDLinkageType Value) { Linkage = Value; } + GOFF::ESDLinkageType getLinkage() const { return Linkage; } + + void initAttributes(); }; } // end namespace llvm diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt index 70c4577aeec0a..1388f130bb806 100644 --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/lib/MC/CMakeLists.txt @@ -50,6 +50,7 @@ add_llvm_component_library(LLVMMC MCSubtargetInfo.cpp MCSymbol.cpp MCSymbolELF.cpp + MCSymbolGOFF.cpp MCSymbolXCOFF.cpp MCTargetOptions.cpp MCTargetOptionsCommandFlags.cpp diff --git a/llvm/lib/MC/GOFFObjectWriter.cpp b/llvm/lib/MC/GOFFObjectWriter.cpp index a3eaaa743039d..860d0a50ccb35 100644 --- a/llvm/lib/MC/GOFFObjectWriter.cpp +++ b/llvm/lib/MC/GOFFObjectWriter.cpp @@ -266,11 +266,24 @@ class GOFFSymbol { BehavAttrs.setBindingScope(Attr.BindingScope); BehavAttrs.setAlignment(EDAttr.Alignment); } + + GOFFSymbol(StringRef Name, uint32_t EsdID, uint32_t ParentEsdID, + const GOFF::ERAttr &Attr) + : Name(Name.data(), Name.size()), EsdId(EsdID), ParentEsdId(ParentEsdID), + SymbolType(GOFF::ESD_ST_ExternalReference), + NameSpace(GOFF::ESD_NS_NormalName) { + BehavAttrs.setExecutable(Attr.Executable); + BehavAttrs.setBindingStrength(Attr.BindingStrength); + BehavAttrs.setLinkageType(Attr.Linkage); + BehavAttrs.setAmode(Attr.Amode); + BehavAttrs.setBindingScope(Attr.BindingScope); + } }; class GOFFWriter { GOFFOstream OS; MCAssembler &Asm; + MCSectionGOFF *RootSD; void writeHeader(); void writeSymbol(const GOFFSymbol &Symbol); @@ -279,16 +292,18 @@ class GOFFWriter { void defineSectionSymbols(const MCSectionGOFF &Section); void defineLabel(const MCSymbolGOFF &Symbol); + void defineExtern(const MCSymbolGOFF &Symbol); void defineSymbols(); public: - GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm); + GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm, MCSectionGOFF *RootSD); uint64_t writeObject(); }; } // namespace -GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm) - : OS(OS), Asm(Asm) {} +GOFFWriter::GOFFWriter(raw_pwrite_stream &OS, MCAssembler &Asm, + MCSectionGOFF *RootSD) + : OS(OS), Asm(Asm), RootSD(RootSD) {} void GOFFWriter::defineSectionSymbols(const MCSectionGOFF &Section) { if (Section.isSD()) { @@ -328,9 +343,16 @@ void GOFFWriter::defineLabel(const MCSymbolGOFF &Symbol) { Section.getEDAttributes().NameSpace, Symbol.getLDAttributes()); if (Symbol.getADA()) LD.ADAEsdId = Symbol.getADA()->getOrdinal(); + LD.Offset = Asm.getSymbolOffset(Symbol); writeSymbol(LD); } +void GOFFWriter::defineExtern(const MCSymbolGOFF &Symbol) { + GOFFSymbol ER(Symbol.getName(), Symbol.getIndex(), + RootSD->getOrdinal(), Symbol.getERAttributes()); + writeSymbol(ER); +} + void GOFFWriter::defineSymbols() { unsigned Ordinal = 0; // Process all sections. @@ -348,6 +370,9 @@ void GOFFWriter::defineSymbols() { if (Symbol.hasLDAttributes()) { Symbol.setIndex(++Ordinal); defineLabel(Symbol); + } else if (Symbol.hasERAttributes()) { + Symbol.setIndex(++Ordinal); + defineExtern(Symbol); } } } @@ -523,7 +548,7 @@ GOFFObjectWriter::GOFFObjectWriter( GOFFObjectWriter::~GOFFObjectWriter() = default; uint64_t GOFFObjectWriter::writeObject() { - uint64_t Size = GOFFWriter(OS, *Asm).writeObject(); + uint64_t Size = GOFFWriter(OS, *Asm, RootSD).writeObject(); return Size; } diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 885fa55b65d50..7ff570c7df402 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -774,6 +774,9 @@ bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol, // Assemblers currently do not support a .cold directive. case MCSA_Exported: // Non-AIX assemblers currently do not support exported visibility. + case MCSA_OSLinkage: + case MCSA_XPLinkage: + // Only for HLASM. return false; case MCSA_Memtag: OS << "\t.memtag\t"; diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 1bc1b92610871..13a7f6d999e61 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -18,6 +18,7 @@ #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixup.h" @@ -151,6 +152,8 @@ bool MCELFStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) { case MCSA_IndirectSymbol: case MCSA_Exported: case MCSA_WeakAntiDep: + case MCSA_OSLinkage: + case MCSA_XPLinkage: return false; case MCSA_NoDeadStrip: diff --git a/llvm/lib/MC/MCGOFFStreamer.cpp b/llvm/lib/MC/MCGOFFStreamer.cpp index ad6397bce70f0..e462864b55054 100644 --- a/llvm/lib/MC/MCGOFFStreamer.cpp +++ b/llvm/lib/MC/MCGOFFStreamer.cpp @@ -11,17 +11,35 @@ //===----------------------------------------------------------------------===// #include "llvm/MC/MCGOFFStreamer.h" +#include "llvm/BinaryFormat/GOFF.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCGOFFObjectWriter.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCSymbolGOFF.h" #include "llvm/MC/TargetRegistry.h" using namespace llvm; +MCGOFFStreamer::MCGOFFStreamer(MCContext &Context, + std::unique_ptr MAB, + std::unique_ptr OW, + std::unique_ptr Emitter) + : MCObjectStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter)) {} + MCGOFFStreamer::~MCGOFFStreamer() = default; +void MCGOFFStreamer::finishImpl() { + getWriter().setRootSD(static_cast( + getContext().getObjectFileInfo()->getTextSection()) + ->getParent()); + MCObjectStreamer::finishImpl(); +} + GOFFObjectWriter &MCGOFFStreamer::getWriter() { return static_cast(getAssembler().getWriter()); } @@ -37,6 +55,82 @@ void MCGOFFStreamer::changeSection(MCSection *Section, uint32_t Subsection) { } } +void MCGOFFStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { + MCObjectStreamer::emitLabel(Symbol, Loc); + static_cast(Symbol)->initAttributes(); +} + +bool MCGOFFStreamer::emitSymbolAttribute(MCSymbol *Sym, + MCSymbolAttr Attribute) { + auto *Symbol = static_cast(Sym); + switch (Attribute) { + case MCSA_Invalid: + case MCSA_Cold: + case MCSA_ELF_TypeIndFunction: + case MCSA_ELF_TypeTLS: + case MCSA_ELF_TypeCommon: + case MCSA_ELF_TypeNoType: + case MCSA_ELF_TypeGnuUniqueObject: + case MCSA_LGlobal: + case MCSA_Extern: + case MCSA_Exported: + case MCSA_IndirectSymbol: + case MCSA_Internal: + case MCSA_LazyReference: + case MCSA_NoDeadStrip: + case MCSA_SymbolResolver: + case MCSA_AltEntry: + case MCSA_PrivateExtern: + case MCSA_Protected: + case MCSA_Reference: + case MCSA_WeakDefinition: + case MCSA_WeakDefAutoPrivate: + case MCSA_WeakAntiDep: + case MCSA_Memtag: + return false; + + case MCSA_ELF_TypeFunction: + Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_CODE); + break; + case MCSA_ELF_TypeObject: + Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA); + break; + case MCSA_OSLinkage: + Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_OS); + break; + case MCSA_XPLinkage: + Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_XPLink); + break; + case MCSA_Global: + Symbol->setExternal(true); + break; + case MCSA_Local: + Symbol->setExternal(false); + break; + case MCSA_Weak: + case MCSA_WeakReference: + Symbol->setExternal(true); + Symbol->setWeak(); + break; + case MCSA_Hidden: + Symbol->setHidden(true); + break; + } + + return true; +} + +void MCGOFFStreamer::emitExterns() { + for (auto &Symbol : getAssembler().symbols()) { + if (Symbol.isTemporary()) + continue; + if (Symbol.isRegistered()) { + auto &Sym = static_cast(const_cast(Symbol)); + Sym.initAttributes(); + } + } +} + MCStreamer *llvm::createGOFFStreamer(MCContext &Context, std::unique_ptr &&MAB, std::unique_ptr &&OW, @@ -45,9 +139,3 @@ MCStreamer *llvm::createGOFFStreamer(MCContext &Context, new MCGOFFStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); return S; } -llvm::MCGOFFStreamer::MCGOFFStreamer(MCContext &Context, - std::unique_ptr MAB, - std::unique_ptr OW, - std::unique_ptr Emitter) - : MCObjectStreamer(Context, std::move(MAB), std::move(OW), - std::move(Emitter)) {} diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp index 2b7a248e6d109..b8e7772f4e16f 100644 --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -299,6 +299,8 @@ bool MCMachOStreamer::emitSymbolAttribute(MCSymbol *Sym, case MCSA_Exported: case MCSA_Memtag: case MCSA_WeakAntiDep: + case MCSA_OSLinkage: + case MCSA_XPLinkage: return false; case MCSA_Global: diff --git a/llvm/lib/MC/MCSymbolGOFF.cpp b/llvm/lib/MC/MCSymbolGOFF.cpp new file mode 100644 index 0000000000000..345572418ecd8 --- /dev/null +++ b/llvm/lib/MC/MCSymbolGOFF.cpp @@ -0,0 +1,46 @@ +//===- MCSymbolGOFF.cpp - GOFF Symbol Representation ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCSymbolGOFF.h" +#include "llvm/BinaryFormat/GOFF.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +void MCSymbolGOFF::initAttributes() { + // Temporary labels are not emitted into the object file. + if (isTemporary()) + return; + + // Do not initialize the attributes multiple times. + if (hasLDAttributes() || hasERAttributes()) + return; + + GOFF::ESDBindingScope BindingScope = + isExternal() + ? (isExported() ? GOFF::ESD_BSC_ImportExport : GOFF::ESD_BSC_Library) + : GOFF::ESD_BSC_Section; + GOFF::ESDBindingStrength BindingStrength = + isWeak() ? GOFF::ESDBindingStrength::ESD_BST_Weak + : GOFF::ESDBindingStrength::ESD_BST_Strong; + + if (isDefined()) { + MCSectionGOFF &Section = static_cast(getSection()); + if (Section.isED()) { + setLDAttributes(GOFF::LDAttr{false, CodeData, BindingStrength, Linkage, + GOFF::ESD_AMODE_64, BindingScope}); + } else if (Section.isPR()) { + // For data symbols, the attributes are already determind in TLOFI. + // TODO Does it make sense to it to here? + } else + llvm_unreachable("Unexpected section type for label"); + } else { + setERAttributes(GOFF::ERAttr{CodeData, BindingStrength, Linkage, + GOFF::ESD_AMODE_64, BindingScope}); + } +} diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp index 72bb37265259e..fe2470e374352 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.cpp @@ -8,12 +8,19 @@ #include "SystemZHLASMAsmStreamer.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/BinaryFormat/GOFF.h" +#include "llvm/MC/MCGOFFAttributes.h" +#include "llvm/MC/MCSymbolGOFF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Signals.h" #include using namespace llvm; +void SystemZHLASMAsmStreamer::visitUsedSymbol(const MCSymbol &Sym) { + Assembler->registerSymbol(Sym); +} + void SystemZHLASMAsmStreamer::EmitEOL() { // Comments are emitted on a new line before the instruction. if (IsVerboseAsm) @@ -183,17 +190,136 @@ void SystemZHLASMAsmStreamer::emitInstruction(const MCInst &Inst, EmitEOL(); } +static void emitXATTR(raw_ostream &OS, StringRef Name, + GOFF::ESDLinkageType Linkage, + GOFF::ESDExecutable Executable, + GOFF::ESDBindingScope BindingScope) { + llvm::ListSeparator Sep(","); + OS << Name << " XATTR "; + OS << Sep << "LINKAGE(" << (Linkage == GOFF::ESD_LT_OS ? "OS" : "XPLINK") + << ")"; + if (Executable != GOFF::ESD_EXE_Unspecified) + OS << Sep << "REFERENCE(" + << (Executable == GOFF::ESD_EXE_CODE ? "CODE" : "DATA") << ")"; + if (BindingScope != GOFF::ESD_BSC_Unspecified) { + OS << Sep << "SCOPE("; + switch (BindingScope) { + case GOFF::ESD_BSC_Section: + OS << "SECTION"; + break; + case GOFF::ESD_BSC_Module: + OS << "MODULE"; + break; + case GOFF::ESD_BSC_Library: + OS << "LIBRARY"; + break; + case GOFF::ESD_BSC_ImportExport: + OS << "EXPORT"; + break; + default: + break; + } + OS << ')'; + } + OS << '\n'; +} + +static bool sameNameAsCSECT(MCSymbolGOFF *Sym) { + if (Sym->hasLDAttributes() && Sym->isInSection()) { + MCSectionGOFF &ED = static_cast(Sym->getSection()); + return Sym->getName() == ED.getParent()->getName(); + } + return false; +} + void SystemZHLASMAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { + MCSymbolGOFF *Sym = static_cast(Symbol); + + MCStreamer::emitLabel(Sym, Loc); + Sym->initAttributes(); + + // Emit ENTRY statement only if not implied by CSECT. + bool EmitEntry = !sameNameAsCSECT(Sym); + + if (!Sym->isTemporary() && Sym->hasLDAttributes()) { + GOFF::LDAttr &LD = Sym->getLDAttributes(); + if (EmitEntry) { + OS << " ENTRY " << Sym->getName(); + EmitEOL(); + } - MCStreamer::emitLabel(Symbol, Loc); + emitXATTR(OS, Sym->getName(), LD.Linkage, LD.Executable, LD.BindingScope); + EmitEOL(); + } - Symbol->print(OS, MAI); // TODO Need to adjust this based on Label type - OS << " DS 0H"; - // TODO Update LabelSuffix in SystemZMCAsmInfoGOFF once tests have been - // moved to HLASM syntax. - // OS << MAI->getLabelSuffix(); - EmitEOL(); + if (EmitEntry) { + OS << Sym->getName() << " DS 0H"; + // TODO Update LabelSuffix in SystemZMCAsmInfoGOFF once tests have been + // moved to HLASM syntax. + // OS << MAI->getLabelSuffix(); + EmitEOL(); + } +} + +bool SystemZHLASMAsmStreamer::emitSymbolAttribute(MCSymbol *Sym, + MCSymbolAttr Attribute) { + auto *Symbol = static_cast(Sym); + switch (Attribute) { + case MCSA_Invalid: + case MCSA_Cold: + case MCSA_ELF_TypeIndFunction: + case MCSA_ELF_TypeTLS: + case MCSA_ELF_TypeCommon: + case MCSA_ELF_TypeNoType: + case MCSA_ELF_TypeGnuUniqueObject: + case MCSA_LGlobal: + case MCSA_Extern: + case MCSA_Exported: + case MCSA_IndirectSymbol: + case MCSA_Internal: + case MCSA_LazyReference: + case MCSA_NoDeadStrip: + case MCSA_SymbolResolver: + case MCSA_AltEntry: + case MCSA_PrivateExtern: + case MCSA_Protected: + case MCSA_Reference: + case MCSA_WeakDefinition: + case MCSA_WeakDefAutoPrivate: + case MCSA_WeakAntiDep: + case MCSA_Memtag: + return false; + + case MCSA_ELF_TypeFunction: + Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_CODE); + break; + case MCSA_ELF_TypeObject: + Symbol->setCodeData(GOFF::ESDExecutable::ESD_EXE_DATA); + break; + case MCSA_OSLinkage: + Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_OS); + break; + case MCSA_XPLinkage: + Symbol->setLinkage(GOFF::ESDLinkageType::ESD_LT_XPLink); + break; + case MCSA_Global: + Symbol->setExternal(true); + break; + case MCSA_Local: + Symbol->setExternal(false); + break; + case MCSA_Weak: + case MCSA_WeakReference: + Symbol->setExternal(true); + Symbol->setWeak(); + break; + case MCSA_Hidden: + Symbol->setHidden(true); + break; + } + + return true; } void SystemZHLASMAsmStreamer::emitRawTextImpl(StringRef String) { @@ -276,6 +402,7 @@ void SystemZHLASMAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, assert(getCurrentSectionOnly() && "Cannot emit contents before setting section!"); + MCStreamer::emitValueImpl(Value, Size, Loc); OS << " DC "; emitHLASMValueImpl(Value, Size, true); EmitEOL(); @@ -285,3 +412,23 @@ void SystemZHLASMAsmStreamer::emitEnd() { OS << " END"; EmitEOL(); } + +void SystemZHLASMAsmStreamer::emitExterns() { + for (auto &Symbol : getAssembler().symbols()) { + if (Symbol.isTemporary()) + continue; + if (Symbol.isRegistered()) { + auto &Sym = static_cast(const_cast(Symbol)); + Sym.initAttributes(); + GOFF::ERAttr &ER = Sym.getERAttributes(); + OS << " " + << (ER.BindingStrength == GOFF::ESDBindingStrength::ESD_BST_Weak + ? "WXTRN" + : "EXTRN") + << " " << Sym.getName(); + EmitEOL(); + emitXATTR(OS, Sym.getName(), ER.Linkage, ER.Executable, ER.BindingScope); + EmitEOL(); + } + } +} diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h index 93b1ac4d901aa..81cfdad472244 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h @@ -28,6 +28,7 @@ #include "llvm/Support/FormattedStream.h" namespace llvm { +class MCSymbolGOFF; class SystemZHLASMAsmStreamer final : public MCStreamer { constexpr static size_t InstLimit = 80; @@ -100,14 +101,13 @@ class SystemZHLASMAsmStreamer final : public MCStreamer { /// @name MCStreamer Interface /// @{ + void visitUsedSymbol(const MCSymbol &Sym) override; void changeSection(MCSection *Section, uint32_t Subsection) override; void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; void emitLabel(MCSymbol *Symbol, SMLoc Loc) override; - bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { - return false; - } + bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, Align ByteAlignment) override {} @@ -123,6 +123,7 @@ class SystemZHLASMAsmStreamer final : public MCStreamer { /// @} void emitEnd(); + void emitExterns(); }; } // namespace llvm diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp index 1a3e373f25374..d25c77e21d9f8 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.cpp @@ -15,6 +15,7 @@ #include "SystemZTargetStreamer.h" #include "SystemZHLASMAsmStreamer.h" #include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCGOFFStreamer.h" #include "llvm/MC/MCObjectFileInfo.h" using namespace llvm; @@ -38,12 +39,12 @@ SystemZHLASMAsmStreamer &SystemZTargetHLASMStreamer::getHLASMStreamer() { return static_cast(getStreamer()); } -void SystemZTargetHLASMStreamer::emitExtern(StringRef Sym) { - getStreamer().emitRawText(Twine(" EXTRN ") + Twine(Sym)); -} - void SystemZTargetHLASMStreamer::emitEnd() { getHLASMStreamer().emitEnd(); } +void SystemZTargetHLASMStreamer::emitExterns() { + getHLASMStreamer().emitExterns(); +} + // HLASM statements can only perform a single operation at a time const MCExpr *SystemZTargetHLASMStreamer::createWordDiffExpr( MCContext &Ctx, const MCSymbol *Hi, const MCSymbol *Lo) { @@ -66,3 +67,11 @@ const MCExpr *SystemZTargetGOFFStreamer::createWordDiffExpr( MCSymbolRefExpr::create(Lo, Ctx), Ctx), MCConstantExpr::create(1, Ctx), Ctx); } + +MCGOFFStreamer &SystemZTargetGOFFStreamer::getGOFFStreamer() { + return static_cast(getStreamer()); +} + +void SystemZTargetGOFFStreamer::emitExterns() { + getGOFFStreamer().emitExterns(); +} diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h index 3fc09bc8c683a..31433cc9739c6 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZTargetStreamer.h @@ -20,6 +20,7 @@ #include namespace llvm { +class MCGOFFStreamer; class SystemZHLASMAsmStreamer; class SystemZTargetStreamer : public MCTargetStreamer { @@ -57,8 +58,8 @@ class SystemZTargetStreamer : public MCTargetStreamer { virtual void emitMachine(StringRef CPUOrCommand) {}; - virtual void emitExtern(StringRef Str) {}; virtual void emitEnd() {}; + virtual void emitExterns() {}; virtual const MCExpr *createWordDiffExpr(MCContext &Ctx, const MCSymbol *Hi, const MCSymbol *Lo) { @@ -69,6 +70,8 @@ class SystemZTargetStreamer : public MCTargetStreamer { class SystemZTargetGOFFStreamer : public SystemZTargetStreamer { public: SystemZTargetGOFFStreamer(MCStreamer &S) : SystemZTargetStreamer(S) {} + MCGOFFStreamer &getGOFFStreamer(); + void emitExterns() override; const MCExpr *createWordDiffExpr(MCContext &Ctx, const MCSymbol *Hi, const MCSymbol *Lo) override; }; @@ -80,8 +83,8 @@ class SystemZTargetHLASMStreamer : public SystemZTargetStreamer { SystemZTargetHLASMStreamer(MCStreamer &S, formatted_raw_ostream &OS) : SystemZTargetStreamer(S), OS(OS) {} SystemZHLASMAsmStreamer &getHLASMStreamer(); - void emitExtern(StringRef Sym) override; void emitEnd() override; + void emitExterns() override; const MCExpr *createWordDiffExpr(MCContext &Ctx, const MCSymbol *Hi, const MCSymbol *Lo) override; }; diff --git a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp index e31d7c6a86476..cddda67e91674 100644 --- a/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp +++ b/llvm/lib/Target/SystemZ/SystemZAsmPrinter.cpp @@ -26,6 +26,7 @@ #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/IR/Mangler.h" #include "llvm/IR/Module.h" +#include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCSectionELF.h" @@ -1112,6 +1113,20 @@ void SystemZAsmPrinter::emitEndOfAsmFile(Module &M) { if (TT.isOSzOS()) { emitADASection(); emitIDRLSection(M); + // Emit EXTRN declarations. + for (auto &GO : M.global_objects()) { + if (GO.isDeclaration()) { + MCSymbol *Sym = TM.getSymbol(&GO); + OutStreamer->emitSymbolAttribute(Sym, GO.hasExternalWeakLinkage() + ? MCSA_WeakReference + : MCSA_Global); + OutStreamer->emitSymbolAttribute(Sym, isa(GO) + ? MCSA_ELF_TypeFunction + : MCSA_ELF_TypeObject); + } + } + getTargetStreamer()->emitExterns(); + OutStreamer->popSection(); } emitAttributes(M); // Emit the END instruction in case of HLASM output. This must be the last @@ -1570,6 +1585,9 @@ void SystemZAsmPrinter::emitPPA2(Module &M) { // Make CELQSTRT symbol. const char *StartSymbolName = "CELQSTRT"; MCSymbol *CELQSTRT = OutContext.getOrCreateSymbol(StartSymbolName); + OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_ELF_TypeFunction); + OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_OSLinkage); + OutStreamer->emitSymbolAttribute(CELQSTRT, MCSA_Global); // Create symbol and assign to class field for use in PPA1. PPA2Sym = OutContext.createTempSymbol("PPA2", false); @@ -1734,6 +1752,9 @@ void SystemZAsmPrinter::emitFunctionEntryLabel() { OutStreamer->AddComment(" Bit 2: 0 = Does not use alloca"); } OutStreamer->emitInt32(DSAAndFlags); + + // Functions denote CODE. + OutStreamer->emitSymbolAttribute(CurrentFnSym, MCSA_ELF_TypeFunction); } AsmPrinter::emitFunctionEntryLabel(); diff --git a/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll b/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll index b28f0dd17599a..43ac737b29778 100644 --- a/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll +++ b/llvm/test/CodeGen/SystemZ/zos-no-eh-label.ll @@ -1,4 +1,4 @@ -; RUN: llc -mtriple s390x-ibm-zos < %s | FileCheck %s +; RUN: llc -mtriple s390x-ibm-zos -emit-gnuas-syntax-on-zos=false < %s | FileCheck %s define signext i32 @_Z9computeitv() personality ptr @__zos_cxx_personality_v2 { ret i32 0 diff --git a/llvm/test/CodeGen/SystemZ/zos-section-1.ll b/llvm/test/CodeGen/SystemZ/zos-section-1.ll index b98584df54d5a..cae371b07d0eb 100644 --- a/llvm/test/CodeGen/SystemZ/zos-section-1.ll +++ b/llvm/test/CodeGen/SystemZ/zos-section-1.ll @@ -104,26 +104,50 @@ entry: ; CHECK-NEXT: 000300 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 ; CHECK-NEXT: 000310 00 01 20 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00 +; ESD record, type ER. +; The name is CELQSTRT. +; CHECK-NEXT: 000320 03 00 00 04 [[CELQSTRT:00 00 00 09]] [[ROOTSD]] 00 00 00 00 +; CHECK-NEXT: 000330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000340 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 +; CHECK-NEXT: 000350 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 +; CHECK-NEXT: 000360 00 04 00 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3 + +; ESD record, type LD. +; The name is me. +; CHECK-NEXT: 000370 03 00 00 02 [[ME:00 00 00 0a]] [[C_CODE64]] 00 00 00 00 +; CHECK-NEXT: 000380 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000390 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 +; CHECK-NEXT: 0003a0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 +; CHECK-NEXT: 0003b0 00 04 20 00 00 00 00 02 94 85 00 00 00 00 00 00 + +; ESD record, type ER. +; The name is other. +; CHECK-NEXT: 0003c0 03 00 00 04 [[OTHER:00 00 00 0b]] [[ROOTSD]] 00 00 00 00 +; CHECK-NEXT: 0003d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0003e0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 +; CHECK-NEXT: 0003f0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 +; CHECK-NEXT: 000400 00 04 20 00 00 00 00 05 96 a3 88 85 99 00 00 00 + ; Text record for the code section C_CODE64. ; The regular expression matches the lower byte of the length. -; CHECK-NEXT: 000320 03 11 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000330 00 00 00 00 00 00 00 {{..}} 00 c3 00 c5 00 c5 00 f1 +; CHECK-NEXT: 000410 03 11 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000420 00 00 00 00 00 00 00 {{..}} 00 c3 00 c5 00 c5 00 f1 ; Text record for the section .&ppa2. -; CHECK: 0003c0 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0003d0 00 00 00 00 00 00 00 {{..}} {{.*}} +; CHECK: 0004b0 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0004c0 00 00 00 00 00 00 00 {{..}} {{.*}} ; Text record for the ADA section test#S. -; CHECK: 000410 03 10 00 00 [[TESTS]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000420 00 00 00 00 00 00 00 {{..}} {{.*}} +; CHECK: 000500 03 10 00 00 [[TESTS]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000510 00 00 00 00 00 00 00 {{..}} {{.*}} ; Text record for the section B_IDRL. -; CHECK: 000460 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000470 00 00 00 00 00 00 00 {{..}} {{.*}} +; CHECK: 000550 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}} ; End record. -; CHECK: 0004b0 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0004c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0004d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0004e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0004f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK: 0005a0 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0005c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0005d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0005e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 diff --git a/llvm/test/CodeGen/SystemZ/zos-section-2.ll b/llvm/test/CodeGen/SystemZ/zos-section-2.ll index 0f608c1206b96..f59bace65ca73 100644 --- a/llvm/test/CodeGen/SystemZ/zos-section-2.ll +++ b/llvm/test/CodeGen/SystemZ/zos-section-2.ll @@ -147,29 +147,37 @@ source_filename = "test.ll" ; CHECK-NEXT: 0004e0 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 ; CHECK-NEXT: 0004f0 00 01 20 00 00 00 00 06 a3 85 a2 a3 7b c3 00 00 +; ESD record, type ER. +; The name is CELQSTRT. +; CHECK-NEXT: 000500 03 00 00 04 [[CELQSTRT:00 00 00 0f]] [[ROOTSD]] 00 00 00 00 +; CHECK-NEXT: 000510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000520 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 +; CHECK-NEXT: 000530 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 02 +; CHECK-NEXT: 000540 00 04 00 00 00 00 00 08 c3 c5 d3 d8 e2 e3 d9 e3 + ; Text record for the code section C_CODE64. ; The regular expression matches the lower byte of the length. -; CHECK-NEXT: 000500 03 10 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000510 00 00 00 00 00 00 00 {{..}} {{.*}} +; CHECK-NEXT: 000550 03 10 00 00 [[C_CODE64]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}} ; Text record for the section .&ppa2. -; CHECK: 000550 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000560 00 00 00 00 00 00 00 {{..}} {{.*}} +; CHECK: 0005a0 03 10 00 00 [[PPA2]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 {{..}} {{.*}} ; Text record for the section data. ; Length is 4, and the content is 0x2a = 42. -; CHECK: 0005a0 03 10 00 00 [[DATA_PR]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 0005b0 00 00 00 00 00 00 00 04 00 00 00 2a 00 00 00 00 +; CHECK: 0005f0 03 10 00 00 [[DATA_PR]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000600 00 00 00 00 00 00 00 04 00 00 00 2a 00 00 00 00 ; There is no text record for section bss! ; Text record for the section B_IDRL. -; CHECK: 0005f0 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000600 00 00 00 00 00 00 00 {{..}} {{.*}} +; CHECK: 000640 03 10 00 01 [[BIDRL]] 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 000650 00 00 00 00 00 00 00 {{..}} {{.*}} ; End record. -; CHECK: 000640 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -; CHECK-NEXT: 000680 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK: 000690 03 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0006a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0006b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0006c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +; CHECK-NEXT: 0006d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 diff --git a/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll b/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll new file mode 100644 index 0000000000000..bd4361aaafdcb --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/zos-symbol-1.ll @@ -0,0 +1,37 @@ +; RUN: llc <%s --mtriple s390x-ibm-zos -emit-gnuas-syntax-on-zos=false | FileCheck %s + +declare extern_weak void @other1(...) +declare void @other2(...) + +define internal void @me1() { +entry: + ret void +} + +define hidden void @me2() { +entry: + tail call void @other1() + ret void +} + +define default void @me3() { +entry: + tail call void @other2() + ret void +} + +; CHECK: ENTRY me1 +; CHECK-NEXT: me1 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(SECTION) + +; CHECK: ENTRY me2 +; CHECK-NEXT: me2 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(LIBRARY) + +; CHECK: ENTRY me3 +; CHECK-NEXT: me3 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT) + +; CHECK: EXTRN CELQSTRT +; CHECK-NEXT: CELQSTRT XATTR LINKAGE(OS),REFERENCE(CODE),SCOPE(EXPORT) +; CHECK-NEXT: WXTRN other1 +; CHECK-NEXT: other1 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT) +; CHECK-NEXT: EXTRN other2 +; CHECK-NEXT: other2 XATTR LINKAGE(XPLINK),REFERENCE(CODE),SCOPE(EXPORT) \ No newline at end of file