409 lines · cpp
1//===- DXContainerEmitter.cpp - Convert YAML to a DXContainer -------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// Binary emitter for yaml to DXContainer binary11///12//===----------------------------------------------------------------------===//13 14#include "llvm/BinaryFormat/DXContainer.h"15#include "llvm/MC/DXContainerPSVInfo.h"16#include "llvm/MC/DXContainerRootSignature.h"17#include "llvm/ObjectYAML/ObjectYAML.h"18#include "llvm/ObjectYAML/yaml2obj.h"19#include "llvm/Support/Errc.h"20#include "llvm/Support/Error.h"21#include "llvm/Support/raw_ostream.h"22 23using namespace llvm;24 25namespace {26class DXContainerWriter {27public:28 DXContainerWriter(DXContainerYAML::Object &ObjectFile)29 : ObjectFile(ObjectFile) {}30 31 Error write(raw_ostream &OS);32 33private:34 DXContainerYAML::Object &ObjectFile;35 36 Error computePartOffsets();37 Error validatePartOffsets();38 Error validateSize(uint32_t Computed);39 40 void writeHeader(raw_ostream &OS);41 Error writeParts(raw_ostream &OS);42};43} // namespace44 45Error DXContainerWriter::validateSize(uint32_t Computed) {46 if (!ObjectFile.Header.FileSize)47 ObjectFile.Header.FileSize = Computed;48 else if (*ObjectFile.Header.FileSize < Computed)49 return createStringError(errc::result_out_of_range,50 "File size specified is too small.");51 return Error::success();52}53 54Error DXContainerWriter::validatePartOffsets() {55 if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size())56 return createStringError(57 errc::invalid_argument,58 "Mismatch between number of parts and part offsets.");59 uint32_t RollingOffset =60 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));61 for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {62 if (RollingOffset > std::get<1>(I))63 return createStringError(errc::invalid_argument,64 "Offset mismatch, not enough space for data.");65 RollingOffset =66 std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size;67 }68 if (Error Err = validateSize(RollingOffset))69 return Err;70 71 return Error::success();72}73 74Error DXContainerWriter::computePartOffsets() {75 if (ObjectFile.Header.PartOffsets)76 return validatePartOffsets();77 uint32_t RollingOffset =78 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));79 ObjectFile.Header.PartOffsets = std::vector<uint32_t>();80 for (const auto &Part : ObjectFile.Parts) {81 ObjectFile.Header.PartOffsets->push_back(RollingOffset);82 RollingOffset += sizeof(dxbc::PartHeader) + Part.Size;83 }84 if (Error Err = validateSize(RollingOffset))85 return Err;86 87 return Error::success();88}89 90void DXContainerWriter::writeHeader(raw_ostream &OS) {91 dxbc::Header Header;92 memcpy(Header.Magic, "DXBC", 4);93 memcpy(Header.FileHash.Digest, ObjectFile.Header.Hash.data(), 16);94 Header.Version.Major = ObjectFile.Header.Version.Major;95 Header.Version.Minor = ObjectFile.Header.Version.Minor;96 Header.FileSize = *ObjectFile.Header.FileSize;97 Header.PartCount = ObjectFile.Parts.size();98 if (sys::IsBigEndianHost)99 Header.swapBytes();100 OS.write(reinterpret_cast<char *>(&Header), sizeof(Header));101 SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(),102 ObjectFile.Header.PartOffsets->end());103 if (sys::IsBigEndianHost)104 for (auto &O : Offsets)105 sys::swapByteOrder(O);106 OS.write(reinterpret_cast<char *>(Offsets.data()),107 Offsets.size() * sizeof(uint32_t));108}109 110Error DXContainerWriter::writeParts(raw_ostream &OS) {111 uint32_t RollingOffset =112 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));113 for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {114 if (RollingOffset < std::get<1>(I)) {115 uint32_t PadBytes = std::get<1>(I) - RollingOffset;116 OS.write_zeros(PadBytes);117 }118 DXContainerYAML::Part P = std::get<0>(I);119 RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader);120 uint32_t PartSize = P.Size;121 122 OS.write(P.Name.c_str(), 4);123 if (sys::IsBigEndianHost)124 sys::swapByteOrder(P.Size);125 OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t));126 127 dxbc::PartType PT = dxbc::parsePartType(P.Name);128 129 uint64_t DataStart = OS.tell();130 switch (PT) {131 case dxbc::PartType::DXIL: {132 if (!P.Program)133 continue;134 dxbc::ProgramHeader Header;135 Header.Version = dxbc::ProgramHeader::getVersion(P.Program->MajorVersion,136 P.Program->MinorVersion);137 Header.Unused = 0;138 Header.ShaderKind = P.Program->ShaderKind;139 memcpy(Header.Bitcode.Magic, "DXIL", 4);140 Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion;141 Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion;142 Header.Bitcode.Unused = 0;143 144 // Compute the optional fields if needed...145 if (P.Program->DXILOffset)146 Header.Bitcode.Offset = *P.Program->DXILOffset;147 else148 Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader);149 150 if (P.Program->DXILSize)151 Header.Bitcode.Size = *P.Program->DXILSize;152 else153 Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0;154 155 if (P.Program->Size)156 Header.Size = *P.Program->Size;157 else158 Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size;159 160 uint32_t BitcodeOffset = Header.Bitcode.Offset;161 if (sys::IsBigEndianHost)162 Header.swapBytes();163 OS.write(reinterpret_cast<const char *>(&Header),164 sizeof(dxbc::ProgramHeader));165 if (P.Program->DXIL) {166 if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) {167 uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader);168 OS.write_zeros(PadBytes);169 }170 OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()),171 P.Program->DXIL->size());172 }173 break;174 }175 case dxbc::PartType::SFI0: {176 // If we don't have any flags we can continue here and the data will be177 // zeroed out.178 if (!P.Flags.has_value())179 continue;180 uint64_t Flags = P.Flags->getEncodedFlags();181 if (sys::IsBigEndianHost)182 sys::swapByteOrder(Flags);183 OS.write(reinterpret_cast<char *>(&Flags), sizeof(uint64_t));184 break;185 }186 case dxbc::PartType::HASH: {187 if (!P.Hash.has_value())188 continue;189 dxbc::ShaderHash Hash = {0, {0}};190 if (P.Hash->IncludesSource)191 Hash.Flags |= static_cast<uint32_t>(dxbc::HashFlags::IncludesSource);192 memcpy(&Hash.Digest[0], &P.Hash->Digest[0], 16);193 if (sys::IsBigEndianHost)194 Hash.swapBytes();195 OS.write(reinterpret_cast<char *>(&Hash), sizeof(dxbc::ShaderHash));196 break;197 }198 case dxbc::PartType::PSV0: {199 if (!P.Info.has_value())200 continue;201 mcdxbc::PSVRuntimeInfo PSV;202 memcpy(&PSV.BaseData, &P.Info->Info, sizeof(dxbc::PSV::v3::RuntimeInfo));203 PSV.Resources = P.Info->Resources;204 PSV.EntryName = P.Info->EntryName;205 206 for (auto El : P.Info->SigInputElements)207 PSV.InputElements.push_back(mcdxbc::PSVSignatureElement{208 El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,209 El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,210 El.Stream});211 212 for (auto El : P.Info->SigOutputElements)213 PSV.OutputElements.push_back(mcdxbc::PSVSignatureElement{214 El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,215 El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,216 El.Stream});217 218 for (auto El : P.Info->SigPatchOrPrimElements)219 PSV.PatchOrPrimElements.push_back(mcdxbc::PSVSignatureElement{220 El.Name, El.Indices, El.StartRow, El.Cols, El.StartCol,221 El.Allocated, El.Kind, El.Type, El.Mode, El.DynamicMask,222 El.Stream});223 224 static_assert(PSV.OutputVectorMasks.size() == PSV.InputOutputMap.size());225 for (unsigned I = 0; I < PSV.OutputVectorMasks.size(); ++I) {226 PSV.OutputVectorMasks[I].insert(PSV.OutputVectorMasks[I].begin(),227 P.Info->OutputVectorMasks[I].begin(),228 P.Info->OutputVectorMasks[I].end());229 PSV.InputOutputMap[I].insert(PSV.InputOutputMap[I].begin(),230 P.Info->InputOutputMap[I].begin(),231 P.Info->InputOutputMap[I].end());232 }233 234 PSV.PatchOrPrimMasks.insert(PSV.PatchOrPrimMasks.begin(),235 P.Info->PatchOrPrimMasks.begin(),236 P.Info->PatchOrPrimMasks.end());237 PSV.InputPatchMap.insert(PSV.InputPatchMap.begin(),238 P.Info->InputPatchMap.begin(),239 P.Info->InputPatchMap.end());240 PSV.PatchOutputMap.insert(PSV.PatchOutputMap.begin(),241 P.Info->PatchOutputMap.begin(),242 P.Info->PatchOutputMap.end());243 244 PSV.finalize(static_cast<Triple::EnvironmentType>(245 Triple::Pixel + P.Info->Info.ShaderStage));246 PSV.write(OS, P.Info->Version);247 break;248 }249 case dxbc::PartType::ISG1:250 case dxbc::PartType::OSG1:251 case dxbc::PartType::PSG1: {252 mcdxbc::Signature Sig;253 if (P.Signature.has_value()) {254 for (const auto &Param : P.Signature->Parameters) {255 Sig.addParam(Param.Stream, Param.Name, Param.Index, Param.SystemValue,256 Param.CompType, Param.Register, Param.Mask,257 Param.ExclusiveMask, Param.MinPrecision);258 }259 }260 Sig.write(OS);261 break;262 }263 case dxbc::PartType::Unknown:264 break; // Skip any handling for unrecognized parts.265 case dxbc::PartType::RTS0:266 if (!P.RootSignature.has_value())267 continue;268 269 mcdxbc::RootSignatureDesc RS;270 RS.Flags = P.RootSignature->getEncodedFlags();271 RS.Version = P.RootSignature->Version;272 RS.NumStaticSamplers = P.RootSignature->NumStaticSamplers;273 274 for (DXContainerYAML::RootParameterLocationYaml &L :275 P.RootSignature->Parameters.Locations) {276 277 const dxbc::RootParameterType Type = L.Header.Type;278 const dxbc::ShaderVisibility Visibility = L.Header.Visibility;279 280 switch (Type) {281 case dxbc::RootParameterType::Constants32Bit: {282 const DXContainerYAML::RootConstantsYaml &ConstantYaml =283 P.RootSignature->Parameters.getOrInsertConstants(L);284 mcdxbc::RootConstants Constants;285 286 Constants.Num32BitValues = ConstantYaml.Num32BitValues;287 Constants.RegisterSpace = ConstantYaml.RegisterSpace;288 Constants.ShaderRegister = ConstantYaml.ShaderRegister;289 RS.ParametersContainer.addParameter(Type, Visibility, Constants);290 break;291 }292 case dxbc::RootParameterType::CBV:293 case dxbc::RootParameterType::SRV:294 case dxbc::RootParameterType::UAV: {295 const DXContainerYAML::RootDescriptorYaml &DescriptorYaml =296 P.RootSignature->Parameters.getOrInsertDescriptor(L);297 298 mcdxbc::RootDescriptor Descriptor;299 Descriptor.RegisterSpace = DescriptorYaml.RegisterSpace;300 Descriptor.ShaderRegister = DescriptorYaml.ShaderRegister;301 if (RS.Version > 1)302 Descriptor.Flags = DescriptorYaml.getEncodedFlags();303 RS.ParametersContainer.addParameter(Type, Visibility, Descriptor);304 break;305 }306 case dxbc::RootParameterType::DescriptorTable: {307 const DXContainerYAML::DescriptorTableYaml &TableYaml =308 P.RootSignature->Parameters.getOrInsertTable(L);309 mcdxbc::DescriptorTable Table;310 for (const auto &R : TableYaml.Ranges) {311 mcdxbc::DescriptorRange Range;312 Range.RangeType = R.RangeType;313 Range.NumDescriptors = R.NumDescriptors;314 Range.BaseShaderRegister = R.BaseShaderRegister;315 Range.RegisterSpace = R.RegisterSpace;316 Range.OffsetInDescriptorsFromTableStart =317 R.OffsetInDescriptorsFromTableStart;318 319 if (RS.Version > 1)320 Range.Flags = R.getEncodedFlags();321 322 Table.Ranges.push_back(Range);323 }324 RS.ParametersContainer.addParameter(Type, Visibility, Table);325 break;326 }327 }328 }329 330 for (const auto &Param : P.RootSignature->samplers()) {331 mcdxbc::StaticSampler NewSampler;332 NewSampler.Filter = Param.Filter;333 NewSampler.AddressU = Param.AddressU;334 NewSampler.AddressV = Param.AddressV;335 NewSampler.AddressW = Param.AddressW;336 NewSampler.MipLODBias = Param.MipLODBias;337 NewSampler.MaxAnisotropy = Param.MaxAnisotropy;338 NewSampler.ComparisonFunc = Param.ComparisonFunc;339 NewSampler.BorderColor = Param.BorderColor;340 NewSampler.MinLOD = Param.MinLOD;341 NewSampler.MaxLOD = Param.MaxLOD;342 NewSampler.ShaderRegister = Param.ShaderRegister;343 NewSampler.RegisterSpace = Param.RegisterSpace;344 NewSampler.ShaderVisibility = Param.ShaderVisibility;345 346 if (RS.Version > 2)347 NewSampler.Flags = Param.getEncodedFlags();348 349 RS.StaticSamplers.push_back(NewSampler);350 }351 352 // Handling of offsets353 RS.RootParameterOffset = RS.computeRootParametersOffset();354 if (P.RootSignature->RootParametersOffset &&355 P.RootSignature->RootParametersOffset.value() !=356 RS.RootParameterOffset) {357 return createStringError(358 errc::invalid_argument,359 "Specified RootParametersOffset does not match required value: %d.",360 RS.RootParameterOffset);361 }362 363 RS.StaticSamplersOffset = RS.computeStaticSamplersOffset();364 if (P.RootSignature->StaticSamplersOffset &&365 P.RootSignature->StaticSamplersOffset.value() !=366 RS.StaticSamplersOffset) {367 return createStringError(368 errc::invalid_argument,369 "Specified StaticSamplersOffset does not match computed value: %d.",370 RS.StaticSamplersOffset);371 }372 373 RS.write(OS);374 break;375 }376 uint64_t BytesWritten = OS.tell() - DataStart;377 RollingOffset += BytesWritten;378 if (BytesWritten < PartSize)379 OS.write_zeros(PartSize - BytesWritten);380 RollingOffset += PartSize;381 }382 383 return Error::success();384}385 386Error DXContainerWriter::write(raw_ostream &OS) {387 if (Error Err = computePartOffsets())388 return Err;389 writeHeader(OS);390 return writeParts(OS);391}392 393namespace llvm {394namespace yaml {395 396bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,397 ErrorHandler EH) {398 DXContainerWriter Writer(Doc);399 if (Error Err = Writer.write(Out)) {400 handleAllErrors(std::move(Err),401 [&](const ErrorInfoBase &Err) { EH(Err.message()); });402 return false;403 }404 return true;405}406 407} // namespace yaml408} // namespace llvm409