296 lines
10 KiB
C++
296 lines
10 KiB
C++
|
//===- DXContainerEmitter.cpp - Convert YAML to a DXContainer -------------===//
|
||
|
//
|
||
|
// 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
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
///
|
||
|
/// \file
|
||
|
/// Binary emitter for yaml to DXContainer binary
|
||
|
///
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "llvm/BinaryFormat/DXContainer.h"
|
||
|
#include "llvm/MC/DXContainerPSVInfo.h"
|
||
|
#include "llvm/ObjectYAML/ObjectYAML.h"
|
||
|
#include "llvm/ObjectYAML/yaml2obj.h"
|
||
|
#include "llvm/Support/Errc.h"
|
||
|
#include "llvm/Support/Error.h"
|
||
|
#include "llvm/Support/raw_ostream.h"
|
||
|
|
||
|
using namespace llvm;
|
||
|
|
||
|
namespace {
|
||
|
class DXContainerWriter {
|
||
|
public:
|
||
|
DXContainerWriter(DXContainerYAML::Object &ObjectFile)
|
||
|
: ObjectFile(ObjectFile) {}
|
||
|
|
||
|
Error write(raw_ostream &OS);
|
||
|
|
||
|
private:
|
||
|
DXContainerYAML::Object &ObjectFile;
|
||
|
|
||
|
Error computePartOffsets();
|
||
|
Error validatePartOffsets();
|
||
|
Error validateSize(uint32_t Computed);
|
||
|
|
||
|
void writeHeader(raw_ostream &OS);
|
||
|
void writeParts(raw_ostream &OS);
|
||
|
};
|
||
|
} // namespace
|
||
|
|
||
|
Error DXContainerWriter::validateSize(uint32_t Computed) {
|
||
|
if (!ObjectFile.Header.FileSize)
|
||
|
ObjectFile.Header.FileSize = Computed;
|
||
|
else if (*ObjectFile.Header.FileSize < Computed)
|
||
|
return createStringError(errc::result_out_of_range,
|
||
|
"File size specified is too small.");
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
Error DXContainerWriter::validatePartOffsets() {
|
||
|
if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size())
|
||
|
return createStringError(
|
||
|
errc::invalid_argument,
|
||
|
"Mismatch between number of parts and part offsets.");
|
||
|
uint32_t RollingOffset =
|
||
|
sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
|
||
|
for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
|
||
|
if (RollingOffset > std::get<1>(I))
|
||
|
return createStringError(errc::invalid_argument,
|
||
|
"Offset mismatch, not enough space for data.");
|
||
|
RollingOffset =
|
||
|
std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size;
|
||
|
}
|
||
|
if (Error Err = validateSize(RollingOffset))
|
||
|
return Err;
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
Error DXContainerWriter::computePartOffsets() {
|
||
|
if (ObjectFile.Header.PartOffsets)
|
||
|
return validatePartOffsets();
|
||
|
uint32_t RollingOffset =
|
||
|
sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
|
||
|
ObjectFile.Header.PartOffsets = std::vector<uint32_t>();
|
||
|
for (const auto &Part : ObjectFile.Parts) {
|
||
|
ObjectFile.Header.PartOffsets->push_back(RollingOffset);
|
||
|
RollingOffset += sizeof(dxbc::PartHeader) + Part.Size;
|
||
|
}
|
||
|
if (Error Err = validateSize(RollingOffset))
|
||
|
return Err;
|
||
|
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
void DXContainerWriter::writeHeader(raw_ostream &OS) {
|
||
|
dxbc::Header Header;
|
||
|
memcpy(Header.Magic, "DXBC", 4);
|
||
|
memcpy(Header.FileHash.Digest, ObjectFile.Header.Hash.data(), 16);
|
||
|
Header.Version.Major = ObjectFile.Header.Version.Major;
|
||
|
Header.Version.Minor = ObjectFile.Header.Version.Minor;
|
||
|
Header.FileSize = *ObjectFile.Header.FileSize;
|
||
|
Header.PartCount = ObjectFile.Parts.size();
|
||
|
if (sys::IsBigEndianHost)
|
||
|
Header.swapBytes();
|
||
|
OS.write(reinterpret_cast<char *>(&Header), sizeof(Header));
|
||
|
SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(),
|
||
|
ObjectFile.Header.PartOffsets->end());
|
||
|
if (sys::IsBigEndianHost)
|
||
|
for (auto &O : Offsets)
|
||
|
sys::swapByteOrder(O);
|
||
|
OS.write(reinterpret_cast<char *>(Offsets.data()),
|
||
|
Offsets.size() * sizeof(uint32_t));
|
||
|
}
|
||
|
|
||
|
void DXContainerWriter::writeParts(raw_ostream &OS) {
|
||
|
uint32_t RollingOffset =
|
||
|
sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
|
||
|
for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
|
||
|
if (RollingOffset < std::get<1>(I)) {
|
||
|
uint32_t PadBytes = std::get<1>(I) - RollingOffset;
|
||
|
OS.write_zeros(PadBytes);
|
||
|
}
|
||
|
DXContainerYAML::Part P = std::get<0>(I);
|
||
|
RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader);
|
||
|
uint32_t PartSize = P.Size;
|
||
|
|
||
|
OS.write(P.Name.c_str(), 4);
|
||
|
if (sys::IsBigEndianHost)
|
||
|
sys::swapByteOrder(P.Size);
|
||
|
OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t));
|
||
|
|
||
|
dxbc::PartType PT = dxbc::parsePartType(P.Name);
|
||
|
|
||
|
uint64_t DataStart = OS.tell();
|
||
|
switch (PT) {
|
||
|
case dxbc::PartType::DXIL: {
|
||
|
if (!P.Program)
|
||
|
continue;
|
||
|
dxbc::ProgramHeader Header;
|
||
|
Header.MajorVersion = P.Program->MajorVersion;
|
||
|
Header.MinorVersion = P.Program->MinorVersion;
|
||
|
Header.Unused = 0;
|
||
|
Header.ShaderKind = P.Program->ShaderKind;
|
||
|
memcpy(Header.Bitcode.Magic, "DXIL", 4);
|
||
|
Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion;
|
||
|
Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion;
|
||
|
Header.Bitcode.Unused = 0;
|
||
|
|
||
|
// Compute the optional fields if needed...
|
||
|
if (P.Program->DXILOffset)
|
||
|
Header.Bitcode.Offset = *P.Program->DXILOffset;
|
||
|
else
|
||
|
Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader);
|
||
|
|
||
|
if (P.Program->DXILSize)
|
||
|
Header.Bitcode.Size = *P.Program->DXILSize;
|
||
|
else
|
||
|
Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0;
|
||
|
|
||
|
if (P.Program->Size)
|
||
|
Header.Size = *P.Program->Size;
|
||
|
else
|
||
|
Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size;
|
||
|
|
||
|
uint32_t BitcodeOffset = Header.Bitcode.Offset;
|
||
|
if (sys::IsBigEndianHost)
|
||
|
Header.swapBytes();
|
||
|
OS.write(reinterpret_cast<const char *>(&Header),
|
||
|
sizeof(dxbc::ProgramHeader));
|
||
|
if (P.Program->DXIL) {
|
||
|
if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) {
|
||
|
uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader);
|
||
|
OS.write_zeros(PadBytes);
|
||
|
}
|
||
|
OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()),
|
||
|
P.Program->DXIL->size());
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case dxbc::PartType::SFI0: {
|
||
|
// If we don't have any flags we can continue here and the data will be
|
||
|
// zeroed out.
|
||
|
if (!P.Flags.has_value())
|
||
|
continue;
|
||
|
uint64_t Flags = P.Flags->getEncodedFlags();
|
||
|
if (sys::IsBigEndianHost)
|
||
|
sys::swapByteOrder(Flags);
|
||
|
OS.write(reinterpret_cast<char *>(&Flags), sizeof(uint64_t));
|
||
|
break;
|
||
|
}
|
||
|
case dxbc::PartType::HASH: {
|
||
|
if (!P.Hash.has_value())
|
||
|
continue;
|
||
|
dxbc::ShaderHash Hash = {0, {0}};
|
||
|
if (P.Hash->IncludesSource)
|
||
|
Hash.Flags |= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource);
|
||
|
memcpy(&Hash.Digest[0], &P.Hash->Digest[0], 16);
|
||
|
if (sys::IsBigEndianHost)
|
||
|
Hash.swapBytes();
|
||
|
OS.write(reinterpret_cast<char *>(&Hash), sizeof(dxbc::ShaderHash));
|
||
|
break;
|
||
|
}
|
||
|
case dxbc::PartType::PSV0: {
|
||
|
if (!P.Info.has_value())
|
||
|
continue;
|
||
|
mcdxbc::PSVRuntimeInfo PSV;
|
||
|
memcpy(&PSV.BaseData, &P.Info->Info, sizeof(dxbc::PSV::v2::RuntimeInfo));
|
||
|
PSV.Resources = P.Info->Resources;
|
||
|
|
||
|
for (auto El : P.Info->SigInputElements)
|
||
|
PSV.InputElements.push_back(mcdxbc::PSVSignatureElement{
|
||
|
El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
|
||
|
El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
|
||
|
El.Stream});
|
||
|
|
||
|
for (auto El : P.Info->SigOutputElements)
|
||
|
PSV.OutputElements.push_back(mcdxbc::PSVSignatureElement{
|
||
|
El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
|
||
|
El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
|
||
|
El.Stream});
|
||
|
|
||
|
for (auto El : P.Info->SigPatchOrPrimElements)
|
||
|
PSV.PatchOrPrimElements.push_back(mcdxbc::PSVSignatureElement{
|
||
|
El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,
|
||
|
El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,
|
||
|
El.Stream});
|
||
|
|
||
|
static_assert(PSV.OutputVectorMasks.size() == PSV.InputOutputMap.size());
|
||
|
for (unsigned I = 0; I < PSV.OutputVectorMasks.size(); ++I) {
|
||
|
PSV.OutputVectorMasks[I].insert(PSV.OutputVectorMasks[I].begin(),
|
||
|
P.Info->OutputVectorMasks[I].begin(),
|
||
|
P.Info->OutputVectorMasks[I].end());
|
||
|
PSV.InputOutputMap[I].insert(PSV.InputOutputMap[I].begin(),
|
||
|
P.Info->InputOutputMap[I].begin(),
|
||
|
P.Info->InputOutputMap[I].end());
|
||
|
}
|
||
|
|
||
|
PSV.PatchOrPrimMasks.insert(PSV.PatchOrPrimMasks.begin(),
|
||
|
P.Info->PatchOrPrimMasks.begin(),
|
||
|
P.Info->PatchOrPrimMasks.end());
|
||
|
PSV.InputPatchMap.insert(PSV.InputPatchMap.begin(),
|
||
|
P.Info->InputPatchMap.begin(),
|
||
|
P.Info->InputPatchMap.end());
|
||
|
PSV.PatchOutputMap.insert(PSV.PatchOutputMap.begin(),
|
||
|
P.Info->PatchOutputMap.begin(),
|
||
|
P.Info->PatchOutputMap.end());
|
||
|
|
||
|
PSV.finalize(static_cast<Triple::EnvironmentType>(
|
||
|
Triple::Pixel + P.Info->Info.ShaderStage));
|
||
|
PSV.write(OS, P.Info->Version);
|
||
|
break;
|
||
|
}
|
||
|
case dxbc::PartType::ISG1:
|
||
|
case dxbc::PartType::OSG1:
|
||
|
case dxbc::PartType::PSG1: {
|
||
|
mcdxbc::Signature Sig;
|
||
|
if (P.Signature.has_value()) {
|
||
|
for (const auto &Param : P.Signature->Parameters) {
|
||
|
Sig.addParam(Param.Stream, Param.Name, Param.Index, Param.SystemValue,
|
||
|
Param.CompType, Param.Register, Param.Mask,
|
||
|
Param.ExclusiveMask, Param.MinPrecision);
|
||
|
}
|
||
|
}
|
||
|
Sig.write(OS);
|
||
|
break;
|
||
|
}
|
||
|
case dxbc::PartType::Unknown:
|
||
|
break; // Skip any handling for unrecognized parts.
|
||
|
}
|
||
|
uint64_t BytesWritten = OS.tell() - DataStart;
|
||
|
RollingOffset += BytesWritten;
|
||
|
if (BytesWritten < PartSize)
|
||
|
OS.write_zeros(PartSize - BytesWritten);
|
||
|
RollingOffset += PartSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Error DXContainerWriter::write(raw_ostream &OS) {
|
||
|
if (Error Err = computePartOffsets())
|
||
|
return Err;
|
||
|
writeHeader(OS);
|
||
|
writeParts(OS);
|
||
|
return Error::success();
|
||
|
}
|
||
|
|
||
|
namespace llvm {
|
||
|
namespace yaml {
|
||
|
|
||
|
bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,
|
||
|
ErrorHandler EH) {
|
||
|
DXContainerWriter Writer(Doc);
|
||
|
if (Error Err = Writer.write(Out)) {
|
||
|
handleAllErrors(std::move(Err),
|
||
|
[&](const ErrorInfoBase &Err) { EH(Err.message()); });
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace yaml
|
||
|
} // namespace llvm
|