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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Binary emitter for yaml to DXContainer binary
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/BinaryFormat/DXContainer.h"
15 #include "llvm/ObjectYAML/ObjectYAML.h"
16 #include "llvm/ObjectYAML/yaml2obj.h"
17 #include "llvm/Support/Errc.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/raw_ostream.h"
20
21 using namespace llvm;
22
23 namespace {
24 class DXContainerWriter {
25 public:
DXContainerWriter(DXContainerYAML::Object & ObjectFile)26 DXContainerWriter(DXContainerYAML::Object &ObjectFile)
27 : ObjectFile(ObjectFile) {}
28
29 Error write(raw_ostream &OS);
30
31 private:
32 DXContainerYAML::Object &ObjectFile;
33
34 Error computePartOffsets();
35 Error validatePartOffsets();
36 Error validateSize(uint32_t Computed);
37
38 void writeHeader(raw_ostream &OS);
39 void writeParts(raw_ostream &OS);
40 };
41 } // namespace
42
validateSize(uint32_t Computed)43 Error DXContainerWriter::validateSize(uint32_t Computed) {
44 if (!ObjectFile.Header.FileSize)
45 ObjectFile.Header.FileSize = Computed;
46 else if (*ObjectFile.Header.FileSize < Computed)
47 return createStringError(errc::result_out_of_range,
48 "File size specified is too small.");
49 return Error::success();
50 }
51
validatePartOffsets()52 Error DXContainerWriter::validatePartOffsets() {
53 if (ObjectFile.Parts.size() != ObjectFile.Header.PartOffsets->size())
54 return createStringError(
55 errc::invalid_argument,
56 "Mismatch between number of parts and part offsets.");
57 uint32_t RollingOffset =
58 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
59 for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
60 if (RollingOffset > std::get<1>(I))
61 return createStringError(errc::invalid_argument,
62 "Offset mismatch, not enough space for data.");
63 RollingOffset =
64 std::get<1>(I) + sizeof(dxbc::PartHeader) + std::get<0>(I).Size;
65 }
66 if (Error Err = validateSize(RollingOffset))
67 return Err;
68
69 return Error::success();
70 }
71
computePartOffsets()72 Error DXContainerWriter::computePartOffsets() {
73 if (ObjectFile.Header.PartOffsets)
74 return validatePartOffsets();
75 uint32_t RollingOffset =
76 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
77 ObjectFile.Header.PartOffsets = std::vector<uint32_t>();
78 for (const auto &Part : ObjectFile.Parts) {
79 ObjectFile.Header.PartOffsets->push_back(RollingOffset);
80 RollingOffset += sizeof(dxbc::PartHeader) + Part.Size;
81 }
82 if (Error Err = validateSize(RollingOffset))
83 return Err;
84
85 return Error::success();
86 }
87
writeHeader(raw_ostream & OS)88 void DXContainerWriter::writeHeader(raw_ostream &OS) {
89 dxbc::Header Header;
90 memcpy(Header.Magic, "DXBC", 4);
91 memcpy(Header.FileHash.Digest, ObjectFile.Header.Hash.data(), 16);
92 Header.Version.Major = ObjectFile.Header.Version.Major;
93 Header.Version.Minor = ObjectFile.Header.Version.Minor;
94 Header.FileSize = *ObjectFile.Header.FileSize;
95 Header.PartCount = ObjectFile.Parts.size();
96 if (sys::IsBigEndianHost)
97 Header.swapBytes();
98 OS.write(reinterpret_cast<char *>(&Header), sizeof(Header));
99 SmallVector<uint32_t> Offsets(ObjectFile.Header.PartOffsets->begin(),
100 ObjectFile.Header.PartOffsets->end());
101 if (sys::IsBigEndianHost)
102 for (auto &O : Offsets)
103 sys::swapByteOrder(O);
104 OS.write(reinterpret_cast<char *>(Offsets.data()),
105 Offsets.size() * sizeof(uint32_t));
106 }
107
writeParts(raw_ostream & OS)108 void DXContainerWriter::writeParts(raw_ostream &OS) {
109 uint32_t RollingOffset =
110 sizeof(dxbc::Header) + (ObjectFile.Header.PartCount * sizeof(uint32_t));
111 for (auto I : llvm::zip(ObjectFile.Parts, *ObjectFile.Header.PartOffsets)) {
112 if (RollingOffset < std::get<1>(I)) {
113 uint32_t PadBytes = std::get<1>(I) - RollingOffset;
114 OS.write_zeros(PadBytes);
115 }
116 DXContainerYAML::Part P = std::get<0>(I);
117 OS.write(P.Name.c_str(), 4);
118 if (sys::IsBigEndianHost)
119 sys::swapByteOrder(P.Size);
120 OS.write(reinterpret_cast<const char *>(&P.Size), sizeof(uint32_t));
121 RollingOffset = std::get<1>(I) + sizeof(dxbc::PartHeader);
122
123 if (P.Name == "DXIL" && P.Program) {
124 dxbc::ProgramHeader Header;
125 Header.MajorVersion = P.Program->MajorVersion;
126 Header.MinorVersion = P.Program->MinorVersion;
127 Header.Unused = 0;
128 Header.ShaderKind = P.Program->ShaderKind;
129 memcpy(Header.Bitcode.Magic, "DXIL", 4);
130 Header.Bitcode.MajorVersion = P.Program->DXILMajorVersion;
131 Header.Bitcode.MinorVersion = P.Program->DXILMinorVersion;
132 Header.Bitcode.Unused = 0;
133
134 // Compute the optional fields if needed...
135 if (P.Program->DXILOffset)
136 Header.Bitcode.Offset = P.Program->DXILOffset.value();
137 else
138 Header.Bitcode.Offset = sizeof(dxbc::BitcodeHeader);
139
140 if (P.Program->DXILSize)
141 Header.Bitcode.Size = P.Program->DXILSize.value();
142 else
143 Header.Bitcode.Size = P.Program->DXIL ? P.Program->DXIL->size() : 0;
144
145 if (P.Program->Size)
146 Header.Size = P.Program->Size.value();
147 else
148 Header.Size = sizeof(dxbc::ProgramHeader) + Header.Bitcode.Size;
149
150 uint32_t BitcodeOffset = Header.Bitcode.Offset;
151 if (sys::IsBigEndianHost)
152 Header.swapBytes();
153 OS.write(reinterpret_cast<const char *>(&Header),
154 sizeof(dxbc::ProgramHeader));
155 if (P.Program->DXIL) {
156 if (BitcodeOffset > sizeof(dxbc::BitcodeHeader)) {
157 uint32_t PadBytes = BitcodeOffset - sizeof(dxbc::BitcodeHeader);
158 OS.write_zeros(PadBytes);
159 }
160 OS.write(reinterpret_cast<char *>(P.Program->DXIL->data()),
161 P.Program->DXIL->size());
162 }
163 }
164 }
165 }
166
write(raw_ostream & OS)167 Error DXContainerWriter::write(raw_ostream &OS) {
168 if (Error Err = computePartOffsets())
169 return Err;
170 writeHeader(OS);
171 writeParts(OS);
172 return Error::success();
173 }
174
175 namespace llvm {
176 namespace yaml {
177
yaml2dxcontainer(DXContainerYAML::Object & Doc,raw_ostream & Out,ErrorHandler EH)178 bool yaml2dxcontainer(DXContainerYAML::Object &Doc, raw_ostream &Out,
179 ErrorHandler EH) {
180 DXContainerWriter Writer(Doc);
181 if (Error Err = Writer.write(Out)) {
182 handleAllErrors(std::move(Err),
183 [&](const ErrorInfoBase &Err) { EH(Err.message()); });
184 return false;
185 }
186 return true;
187 }
188
189 } // namespace yaml
190 } // namespace llvm
191