diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -49,6 +49,7 @@ class MCRegisterInfo; class MCSection; class MCSectionCOFF; +class MCSectionDXContainer; class MCSectionELF; class MCSectionGOFF; class MCSectionMachO; @@ -129,6 +130,7 @@ BumpPtrAllocator Allocator; SpecificBumpPtrAllocator COFFAllocator; + SpecificBumpPtrAllocator DXCAllocator; SpecificBumpPtrAllocator ELFAllocator; SpecificBumpPtrAllocator MachOAllocator; SpecificBumpPtrAllocator GOFFAllocator; @@ -343,6 +345,7 @@ std::map GOFFUniquingMap; std::map WasmUniquingMap; std::map XCOFFUniquingMap; + StringMap DXCUniquingMap; StringMap RelSecNames; SpecificBumpPtrAllocator MCSubtargetAllocator; @@ -660,6 +663,9 @@ MCSectionWasm *getWasmSection(const Twine &Section, SectionKind K, unsigned Flags, const MCSymbolWasm *Group, unsigned UniqueID, const char *BeginSymName); + + /// Get the section for the provided Section name + MCSectionDXContainer *getDXContainerSection(StringRef Section, SectionKind K); bool hasXCOFFSection(StringRef Section, XCOFF::CsectProperties CsectProp) const; diff --git a/llvm/include/llvm/MC/MCDXContainerStreamer.h b/llvm/include/llvm/MC/MCDXContainerStreamer.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/MC/MCDXContainerStreamer.h @@ -0,0 +1,49 @@ +//===- MCDXContainerStreamer.h - MCDXContainerStreamer 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 +// +//===----------------------------------------------------------------------===// +// +// Overrides MCObjectStreamer to disable all unnecessary features with stubs. +// The DXContainer format isn't a fully featured object format. It doesn't +// support symbols, and initially it will not support instruction data since it +// is used as a bitcode container for DXIL. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCDXCONTAINERSTREAMER_H +#define LLVM_MC_MCDXCONTAINERSTREAMER_H + +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCObjectWriter.h" + +namespace llvm { +class MCAssembler; +class MCExpr; +class MCInst; +class raw_ostream; + +class MCDXContainerStreamer : public MCObjectStreamer { +public: + MCDXContainerStreamer(MCContext &Context, std::unique_ptr TAB, + std::unique_ptr OW, + std::unique_ptr Emitter) + : MCObjectStreamer(Context, std::move(TAB), std::move(OW), + std::move(Emitter)) {} + + bool emitSymbolAttribute(MCSymbol *, MCSymbolAttr) override { return false; } + void emitCommonSymbol(MCSymbol *, uint64_t, unsigned) override {} + void emitZerofill(MCSection *, MCSymbol *Symbol = nullptr, uint64_t Size = 0, + unsigned ByteAlignment = 0, SMLoc Loc = SMLoc()) override {} + +private: + void emitInstToData(const MCInst &, const MCSubtargetInfo &) override; +}; + +} // end namespace llvm + +#endif // LLVM_MC_MCDXCONTAINERSTREAMER_H diff --git a/llvm/include/llvm/MC/MCDXContainerWriter.h b/llvm/include/llvm/MC/MCDXContainerWriter.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/MC/MCDXContainerWriter.h @@ -0,0 +1,45 @@ +//===- llvm/MC/MCDXContainerWriter.h - DXContainer 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_MCDXCONTAINERWRITER_H +#define LLVM_MC_MCDXCONTAINERWRITER_H + +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCObjectWriter.h" + +namespace llvm { + +class raw_pwrite_stream; + +class MCDXContainerTargetWriter : public MCObjectTargetWriter { +protected: + MCDXContainerTargetWriter() {} + +public: + virtual ~MCDXContainerTargetWriter(); + + Triple::ObjectFormatType getFormat() const override { + return Triple::DXContainer; + } + static bool classof(const MCObjectTargetWriter *W) { + return W->getFormat() == Triple::DXContainer; + } +}; + +/// Construct a new DXContainer writer instance. +/// +/// \param MOTW - The target specific DXContainer writer subclass. +/// \param OS - The stream to write to. +/// \returns The constructed object writer. +std::unique_ptr +createDXContainerObjectWriter(std::unique_ptr MOTW, + raw_pwrite_stream &OS); + +} // end namespace llvm + +#endif // LLVM_MC_MCDXCONTAINERWRITER_H diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -459,6 +459,7 @@ void initSPIRVMCObjectFileInfo(const Triple &T); void initWasmMCObjectFileInfo(const Triple &T); void initXCOFFMCObjectFileInfo(const Triple &T); + void initDXContainerObjectFileInfo(const Triple &T); MCSection *getDwarfComdatSection(const char *Name, uint64_t Hash) const; public: diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h --- a/llvm/include/llvm/MC/MCSection.h +++ b/llvm/include/llvm/MC/MCSection.h @@ -48,6 +48,7 @@ SV_Wasm, SV_XCOFF, SV_SPIRV, + SV_DXContainer, }; /// Express the state of bundle locked groups while emitting code. diff --git a/llvm/include/llvm/MC/MCSectionDXContainer.h b/llvm/include/llvm/MC/MCSectionDXContainer.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/MC/MCSectionDXContainer.h @@ -0,0 +1,38 @@ +//===- MCSectionDXContainer.h - DXContainer MC Sections ---------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the MCSectionDXContainer class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCSECTIONDXCONTAINER_H +#define LLVM_MC_MCSECTIONDXCONTAINER_H + +#include "llvm/MC/MCSection.h" +#include "llvm/MC/SectionKind.h" + +namespace llvm { + +class MCSymbol; + +class MCSectionDXContainer final : public MCSection { + friend class MCContext; + + MCSectionDXContainer(StringRef Name, SectionKind K, MCSymbol *Begin) + : MCSection(SV_DXContainer, Name, K, Begin) {} + +public: + void printSwitchToSection(const MCAsmInfo &, const Triple &, raw_ostream &, + const MCExpr *) const override; + bool useCodeAlign() const override { return false; } + bool isVirtualSection() const override { return false; } +}; + +} // end namespace llvm + +#endif // LLVM_MC_MCSECTIONDXCONTAINER_H diff --git a/llvm/include/llvm/MC/TargetRegistry.h b/llvm/include/llvm/MC/TargetRegistry.h --- a/llvm/include/llvm/MC/TargetRegistry.h +++ b/llvm/include/llvm/MC/TargetRegistry.h @@ -114,6 +114,11 @@ std::unique_ptr &&OW, std::unique_ptr &&CE, bool RelaxAll); +MCStreamer *createDXContainerStreamer(MCContext &Ctx, + std::unique_ptr &&TAB, + std::unique_ptr &&OW, + std::unique_ptr &&CE, + bool RelaxAll); MCRelocationInfo *createMCRelocationInfo(const Triple &TT, MCContext &Ctx); @@ -211,6 +216,12 @@ std::unique_ptr &&TAB, std::unique_ptr &&OW, std::unique_ptr &&Emitter, bool RelaxAll); + + using DXContainerStreamerCtorTy = + MCStreamer *(*)(const Triple &T, MCContext &Ctx, + std::unique_ptr &&TAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, bool RelaxAll); using NullTargetStreamerCtorTy = MCTargetStreamer *(*)(MCStreamer &S); using AsmTargetStreamerCtorTy = MCTargetStreamer *(*)( @@ -313,6 +324,7 @@ WasmStreamerCtorTy WasmStreamerCtorFn = nullptr; XCOFFStreamerCtorTy XCOFFStreamerCtorFn = nullptr; SPIRVStreamerCtorTy SPIRVStreamerCtorFn = nullptr; + DXContainerStreamerCtorTy DXContainerStreamerCtorFn = nullptr; /// Construction function for this target's null TargetStreamer, if /// registered (default = nullptr). @@ -541,8 +553,6 @@ switch (T.getObjectFormat()) { case Triple::UnknownObjectFormat: llvm_unreachable("Unknown object format"); - case Triple::DXContainer: - llvm_unreachable("DXContainer is unsupported through MC"); case Triple::COFF: assert(T.isOSWindows() && "only Windows COFF is supported"); S = COFFStreamerCtorFn(Ctx, std::move(TAB), std::move(OW), @@ -593,6 +603,14 @@ S = createSPIRVStreamer(Ctx, std::move(TAB), std::move(OW), std::move(Emitter), RelaxAll); break; + case Triple::DXContainer: + if (DXContainerStreamerCtorFn) + S = DXContainerStreamerCtorFn(T, Ctx, std::move(TAB), std::move(OW), + std::move(Emitter), RelaxAll); + else + S = createDXContainerStreamer(Ctx, std::move(TAB), std::move(OW), + std::move(Emitter), RelaxAll); + break; } if (ObjectTargetStreamerCtorFn) ObjectTargetStreamerCtorFn(*S, STI); @@ -977,6 +995,10 @@ T.SPIRVStreamerCtorFn = Fn; } + static void RegisterDXContainerStreamer(Target &T, Target::DXContainerStreamerCtorTy Fn) { + T.DXContainerStreamerCtorFn = Fn; + } + static void RegisterWasmStreamer(Target &T, Target::WasmStreamerCtorTy Fn) { T.WasmStreamerCtorFn = Fn; } diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/lib/MC/CMakeLists.txt @@ -16,6 +16,8 @@ MCCodeView.cpp MCContext.cpp MCDwarf.cpp + MCDXContainerStreamer.cpp + MCDXContainerWriter.cpp MCELFObjectTargetWriter.cpp MCELFStreamer.cpp MCExpr.cpp @@ -38,6 +40,7 @@ MCSchedule.cpp MCSection.cpp MCSectionCOFF.cpp + MCSectionDXContainer.cpp MCSectionELF.cpp MCSectionMachO.cpp MCSectionWasm.cpp diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp --- a/llvm/lib/MC/MCAsmBackend.cpp +++ b/llvm/lib/MC/MCAsmBackend.cpp @@ -9,6 +9,7 @@ #include "llvm/MC/MCAsmBackend.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLArrayExtras.h" +#include "llvm/MC/MCDXContainerWriter.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCMachObjectWriter.h" @@ -49,6 +50,9 @@ case Triple::XCOFF: return createXCOFFObjectWriter( cast(std::move(TW)), OS); + case Triple::DXContainer: + return createDXContainerObjectWriter( + cast(std::move(TW)), OS); default: llvm_unreachable("unexpected object format"); } diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/lib/MC/MCContext.cpp @@ -26,6 +26,7 @@ #include "llvm/MC/MCInst.h" #include "llvm/MC/MCLabel.h" #include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionDXContainer.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionGOFF.h" #include "llvm/MC/MCSectionMachO.h" @@ -145,6 +146,7 @@ // Call the destructors so the fragments are freed COFFAllocator.DestroyAll(); + DXCAllocator.DestroyAll(); ELFAllocator.DestroyAll(); GOFFAllocator.DestroyAll(); MachOAllocator.DestroyAll(); @@ -176,6 +178,7 @@ COFFUniquingMap.clear(); WasmUniquingMap.clear(); XCOFFUniquingMap.clear(); + DXCUniquingMap.clear(); ELFEntrySizeMap.clear(); ELFSeenGenericMergeableSections.clear(); @@ -838,6 +841,29 @@ return Result; } +MCSectionDXContainer *MCContext::getDXContainerSection(StringRef Section, + SectionKind K) { + // Do the lookup, if we have a hit, return it. + auto ItInsertedPair = DXCUniquingMap.try_emplace(Section); + if (!ItInsertedPair.second) + return ItInsertedPair.first->second; + + auto MapIt = ItInsertedPair.first; + // Grab the name from the StringMap. Since the Section is going to keep a + // copy of this StringRef we need to make sure the underlying string stays + // alive as long as we need it. + StringRef Name = MapIt->first(); + MapIt->second = + new (DXCAllocator.Allocate()) MCSectionDXContainer(Name, K, nullptr); + + // The first fragment will store the header + auto *F = new MCDataFragment(); + MapIt->second->getFragmentList().insert(MapIt->second->begin(), F); + F->setParent(MapIt->second); + + return MapIt->second; +} + MCSubtargetInfo &MCContext::getSubtargetCopy(const MCSubtargetInfo &STI) { return *new (MCSubtargetAllocator.Allocate()) MCSubtargetInfo(STI); } diff --git a/llvm/lib/MC/MCDXContainerStreamer.cpp b/llvm/lib/MC/MCDXContainerStreamer.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/MC/MCDXContainerStreamer.cpp @@ -0,0 +1,31 @@ +//===- lib/MC/MCDXContainerStreamer.cpp - DXContainer Impl ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains the object streamer for DXContainer files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCDXContainerStreamer.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/TargetRegistry.h" + +using namespace llvm; + +void MCDXContainerStreamer::emitInstToData(const MCInst &, + const MCSubtargetInfo &) {} + +MCStreamer *llvm::createDXContainerStreamer( + MCContext &Context, std::unique_ptr &&MAB, + std::unique_ptr &&OW, std::unique_ptr &&CE, + bool RelaxAll) { + auto *S = new MCDXContainerStreamer(Context, std::move(MAB), std::move(OW), + std::move(CE)); + if (RelaxAll) + S->getAssembler().setRelaxAll(true); + return S; +} diff --git a/llvm/lib/MC/MCDXContainerWriter.cpp b/llvm/lib/MC/MCDXContainerWriter.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/MC/MCDXContainerWriter.cpp @@ -0,0 +1,143 @@ +//===- llvm/MC/MCDXContainerWriter.cpp - DXContainer 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCDXContainerWriter.h" +#include "llvm/BinaryFormat/DXContainer.h" +#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/EndianStream.h" + +using namespace llvm; + +MCDXContainerTargetWriter::~MCDXContainerTargetWriter() {} + +namespace { +class DXContainerObjectWriter : public MCObjectWriter { + ::support::endian::Writer W; + + /// The target specific DXContainer writer instance. + std::unique_ptr TargetObjectWriter; + +public: + DXContainerObjectWriter(std::unique_ptr MOTW, + raw_pwrite_stream &OS) + : W(OS, support::little), TargetObjectWriter(std::move(MOTW)) {} + + ~DXContainerObjectWriter() override {} + +private: + 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; +}; +} // namespace + +uint64_t DXContainerObjectWriter::writeObject(MCAssembler &Asm, + const MCAsmLayout &Layout) { + // Start the file size as the header plus the size of the part offsets. + // Presently DXContainer files usually contain 7-10 parts. Reserving space for + // 16 part offsets gives us a little room for growth. + llvm::SmallVector PartOffsets; + uint64_t PartOffset = 0; + for (const MCSection &Sec : Asm) { + uint64_t SectionSize = Layout.getSectionAddressSize(&Sec); + // Skip empty sections. + if (SectionSize == 0) + continue; + + assert(SectionSize < std::numeric_limits::max() && + "Section size too large for DXContainer"); + + PartOffsets.push_back(PartOffset); + PartOffset += sizeof(dxbc::PartHeader) + SectionSize; + PartOffset = alignTo(PartOffset, Align(4ul)); + } + assert(PartOffset < std::numeric_limits::max() && + "Part data too large for DXContainer"); + + uint64_t PartStart = + sizeof(dxbc::Header) + (PartOffsets.size() * sizeof(uint32_t)); + uint64_t FileSize = PartStart + PartOffset; + assert(FileSize < std::numeric_limits::max() && + "File size too large for DXContainer"); + + // Write the header. + W.write({'D', 'X', 'B', 'C'}); + // Write 16-bytes of 0's for the hash. + W.OS.write_zeros(16); + // Write 1.0 for file format version. + W.write(1u); + W.write(0u); + // Write the file size. + W.write(static_cast(FileSize)); + // Write the number of parts. + W.write(static_cast(PartOffsets.size())); + // Write the offsets for the part headers for each part. + for (uint64_t Offset : PartOffsets) + W.write(static_cast(PartStart + Offset)); + + for (const MCSection &Sec : Asm) { + uint64_t SectionSize = Layout.getSectionAddressSize(&Sec); + // Skip empty sections. + if (SectionSize == 0) + continue; + + unsigned Start = W.OS.tell(); + // Write section header. + W.write(ArrayRef(Sec.getName().data(), 4)); + + uint64_t PartSize = SectionSize + sizeof(dxbc::PartHeader); + + if (Sec.getName() == "DXIL") + PartSize += sizeof(dxbc::ProgramHeader); + // DXContainer parts should be 4-byte aligned. + PartSize = alignTo(PartSize, Align(4)); + W.write(static_cast(PartSize)); + if (Sec.getName() == "DXIL") { + dxbc::ProgramHeader Header; + bzero(reinterpret_cast(&Header), sizeof(dxbc::ProgramHeader)); + + const Triple &TT = Asm.getContext().getTargetTriple(); + VersionTuple Version = TT.getOSVersion(); + Header.MajorVersion = static_cast(Version.getMajor()); + if (Version.getMinor().hasValue()) + Header.MinorVersion = static_cast(*Version.getMinor()); + if (TT.hasEnvironment()) + Header.ShaderKind = + static_cast(TT.getEnvironment() - Triple::Pixel); + + // The program header's size field is in 32-bit words. + Header.Size = (SectionSize + sizeof(dxbc::ProgramHeader) + 3) / 4; + memcpy(Header.Bitcode.Magic, "DXIL", 4); + Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader); + Header.Bitcode.Size = SectionSize; + if (sys::IsBigEndianHost) + Header.swapBytes(); + W.write(ArrayRef(reinterpret_cast(&Header), + sizeof(dxbc::ProgramHeader))); + } + Asm.writeSectionData(W.OS, &Sec, Layout); + unsigned Size = W.OS.tell() - Start; + W.OS.write_zeros(offsetToAlignment(Size, Align(4))); + } + return 0; +} + +std::unique_ptr llvm::createDXContainerObjectWriter( + std::unique_ptr MOTW, raw_pwrite_stream &OS) { + return std::make_unique(std::move(MOTW), OS); +} diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -16,6 +16,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCSectionCOFF.h" +#include "llvm/MC/MCSectionDXContainer.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionGOFF.h" #include "llvm/MC/MCSectionMachO.h" @@ -1019,6 +1020,11 @@ /* MultiSymbolsAllowed */ true, ".dwmac", XCOFF::SSUBTYP_DWMAC); } +void MCObjectFileInfo::initDXContainerObjectFileInfo(const Triple &T) { + // At the moment the DXBC section should end up empty. + TextSection = Ctx->getDXContainerSection("DXBC", SectionKind::getText()); +} + MCObjectFileInfo::~MCObjectFileInfo() = default; void MCObjectFileInfo::initMCObjectFileInfo(MCContext &MCCtx, bool PIC, @@ -1067,6 +1073,7 @@ initXCOFFMCObjectFileInfo(TheTriple); break; case MCContext::IsDXContainer: + initDXContainerObjectFileInfo(TheTriple); break; } } diff --git a/llvm/lib/MC/MCSectionDXContainer.cpp b/llvm/lib/MC/MCSectionDXContainer.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/MC/MCSectionDXContainer.cpp @@ -0,0 +1,15 @@ +//===- lib/MC/MCSectionDXContainer.cpp - DXContainer Section --------------===// +// +// 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/MCSectionDXContainer.h" + +using namespace llvm; + +void MCSectionDXContainer::printSwitchToSection(const MCAsmInfo &, + const Triple &, raw_ostream &, + const MCExpr *) const {}