LLVM 17.0.0git
DXContainer.cpp
Go to the documentation of this file.
1//===- DXContainer.cpp - DXContainer object file implementation -----------===//
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
11#include "llvm/Object/Error.h"
13
14using namespace llvm;
15using namespace llvm::object;
16
17static Error parseFailed(const Twine &Msg) {
18 return make_error<GenericBinaryError>(Msg.str(), object_error::parse_failed);
19}
20
21template <typename T>
22static Error readStruct(StringRef Buffer, const char *Src, T &Struct) {
23 // Don't read before the beginning or past the end of the file
24 if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end())
25 return parseFailed("Reading structure out of file bounds");
26
27 memcpy(&Struct, Src, sizeof(T));
28 // DXContainer is always little endian
30 Struct.swapBytes();
31 return Error::success();
32}
33
34template <typename T>
35static Error readInteger(StringRef Buffer, const char *Src, T &Val,
36 Twine Str = "structure") {
37 static_assert(std::is_integral_v<T>,
38 "Cannot call readInteger on non-integral type.");
39 // Don't read before the beginning or past the end of the file
40 if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end())
41 return parseFailed(Twine("Reading ") + Str + " out of file bounds");
42
43 // The DXContainer offset table is comprised of uint32_t values but not padded
44 // to a 64-bit boundary. So Parts may start unaligned if there is an odd
45 // number of parts and part data itself is not required to be padded.
46 if (reinterpret_cast<uintptr_t>(Src) % alignof(T) != 0)
47 memcpy(reinterpret_cast<char *>(&Val), Src, sizeof(T));
48 else
49 Val = *reinterpret_cast<const T *>(Src);
50 // DXContainer is always little endian
53 return Error::success();
54}
55
56DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {}
57
58Error DXContainer::parseHeader() {
59 return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header);
60}
61
62Error DXContainer::parseDXILHeader(StringRef Part) {
63 if (DXIL)
64 return parseFailed("More than one DXIL part is present in the file");
65 const char *Current = Part.begin();
67 if (Error Err = readStruct(Part, Current, Header))
68 return Err;
69 Current += offsetof(dxbc::ProgramHeader, Bitcode) + Header.Bitcode.Offset;
70 DXIL.emplace(std::make_pair(Header, Current));
71 return Error::success();
72}
73
74Error DXContainer::parseShaderFlags(StringRef Part) {
75 if (ShaderFlags)
76 return parseFailed("More than one SFI0 part is present in the file");
77 uint64_t FlagValue = 0;
78 if (Error Err = readInteger(Part, Part.begin(), FlagValue))
79 return Err;
80 ShaderFlags = FlagValue;
81 return Error::success();
82}
83
84Error DXContainer::parseHash(StringRef Part) {
85 if (Hash)
86 return parseFailed("More than one HASH part is present in the file");
87 dxbc::ShaderHash ReadHash;
88 if (Error Err = readStruct(Part, Part.begin(), ReadHash))
89 return Err;
90 Hash = ReadHash;
91 return Error::success();
92}
93
94Error DXContainer::parsePSVInfo(StringRef Part) {
95 if (PSVInfo)
96 return parseFailed("More than one PSV0 part is present in the file");
97 PSVInfo = DirectX::PSVRuntimeInfo(Part);
98 // Parsing the PSVRuntime info occurs late because we need to read data from
99 // other parts first.
100 return Error::success();
101}
102
103Error DXContainer::parsePartOffsets() {
104 uint32_t LastOffset =
105 sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t));
106 const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header);
107 for (uint32_t Part = 0; Part < Header.PartCount; ++Part) {
108 uint32_t PartOffset;
109 if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset))
110 return Err;
111 if (PartOffset < LastOffset)
112 return parseFailed(
113 formatv(
114 "Part offset for part {0} begins before the previous part ends",
115 Part)
116 .str());
117 Current += sizeof(uint32_t);
118 if (PartOffset >= Data.getBufferSize())
119 return parseFailed("Part offset points beyond boundary of the file");
120 // To prevent overflow when reading the part name, we subtract the part name
121 // size from the buffer size, rather than adding to the offset. Since the
122 // file header is larger than the part header we can't reach this code
123 // unless the buffer is at least as large as a part header, so this
124 // subtraction can't underflow.
125 if (PartOffset >= Data.getBufferSize() - sizeof(dxbc::PartHeader::Name))
126 return parseFailed("File not large enough to read part name");
127 PartOffsets.push_back(PartOffset);
128
129 dxbc::PartType PT =
130 dxbc::parsePartType(Data.getBuffer().substr(PartOffset, 4));
131 uint32_t PartDataStart = PartOffset + sizeof(dxbc::PartHeader);
132 uint32_t PartSize;
133 if (Error Err = readInteger(Data.getBuffer(),
134 Data.getBufferStart() + PartOffset + 4,
135 PartSize, "part size"))
136 return Err;
137 StringRef PartData = Data.getBuffer().substr(PartDataStart, PartSize);
138 LastOffset = PartOffset + PartSize;
139 switch (PT) {
140 case dxbc::PartType::DXIL:
141 if (Error Err = parseDXILHeader(PartData))
142 return Err;
143 break;
144 case dxbc::PartType::SFI0:
145 if (Error Err = parseShaderFlags(PartData))
146 return Err;
147 break;
148 case dxbc::PartType::HASH:
149 if (Error Err = parseHash(PartData))
150 return Err;
151 break;
152 case dxbc::PartType::PSV0:
153 if (Error Err = parsePSVInfo(PartData))
154 return Err;
155 break;
157 break;
158 }
159 }
160
161 // Fully parsing the PSVInfo requires knowing the shader kind which we read
162 // out of the program header in the DXIL part.
163 if (PSVInfo) {
164 if (!DXIL)
165 return parseFailed("Cannot fully parse pipeline state validation "
166 "information without DXIL part.");
167 if (Error Err = PSVInfo->parse(DXIL->first.ShaderKind))
168 return Err;
169 }
170 return Error::success();
171}
172
174 DXContainer Container(Object);
175 if (Error Err = Container.parseHeader())
176 return std::move(Err);
177 if (Error Err = Container.parsePartOffsets())
178 return std::move(Err);
179 return Container;
180}
181
182void DXContainer::PartIterator::updateIteratorImpl(const uint32_t Offset) {
183 StringRef Buffer = Container.Data.getBuffer();
184 const char *Current = Buffer.data() + Offset;
185 // Offsets are validated during parsing, so all offsets in the container are
186 // valid and contain enough readable data to read a header.
187 cantFail(readStruct(Buffer, Current, IteratorState.Part));
188 IteratorState.Data =
189 StringRef(Current + sizeof(dxbc::PartHeader), IteratorState.Part.Size);
190 IteratorState.Offset = Offset;
191}
192
194 Triple::EnvironmentType ShaderStage = dxbc::getShaderStage(ShaderKind);
195
196 const char *Current = Data.begin();
197 if (Error Err = readInteger(Data, Current, Size))
198 return Err;
199 Current += sizeof(uint32_t);
200
201 StringRef PSVInfoData = Data.substr(sizeof(uint32_t), Size);
202
203 using namespace dxbc::PSV;
204
205 const uint32_t PSVVersion = getVersion();
206
207 // Detect the PSVVersion by looking at the size field.
208 if (PSVVersion == 2) {
209 v2::RuntimeInfo Info;
210 if (Error Err = readStruct(PSVInfoData, Current, Info))
211 return Err;
213 Info.swapBytes(ShaderStage);
214 BasicInfo = Info;
215 } else if (PSVVersion == 1) {
216 v1::RuntimeInfo Info;
217 if (Error Err = readStruct(PSVInfoData, Current, Info))
218 return Err;
220 Info.swapBytes(ShaderStage);
221 BasicInfo = Info;
222 } else {
223 v0::RuntimeInfo Info;
224 if (Error Err = readStruct(PSVInfoData, Current, Info))
225 return Err;
227 Info.swapBytes(ShaderStage);
228 BasicInfo = Info;
229 }
230 Current += Size;
231
232 uint32_t ResourceCount = 0;
233 if (Error Err = readInteger(Data, Current, ResourceCount))
234 return Err;
235 Current += sizeof(uint32_t);
236
237 Resources.Stride = (PSVVersion < 2) ? sizeof(v0::ResourceBindInfo)
238 : sizeof(v2::ResourceBindInfo);
239 size_t BindingDataSize = Resources.Stride * ResourceCount;
240 Resources.Data = Data.substr(Current - Data.begin(), BindingDataSize);
241
242 Current += BindingDataSize;
243
244 return Error::success();
245}
#define offsetof(TYPE, MEMBER)
write Write Bitcode
Analysis containing CSE Info
Definition: CSEInfo.cpp:27
uint64_t Size
static Error parseFailed(const Twine &Msg)
Definition: DXContainer.cpp:17
static Error readStruct(StringRef Buffer, const char *Src, T &Struct)
Definition: DXContainer.cpp:22
static Error readInteger(StringRef Buffer, const char *Src, T &Val, Twine Str="structure")
Definition: DXContainer.cpp:35
@ Struct
Lightweight error class with error context and mandatory checking.
Definition: Error.h:156
static ErrorSuccess success()
Create a success value.
Definition: Error.h:330
Tagged union holding either a T or a Error.
Definition: Error.h:470
size_t getBufferSize() const
const char * getBufferStart() const
StringRef getBuffer() const
void push_back(const T &Elt)
Definition: SmallVector.h:416
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition: StringRef.h:569
iterator begin() const
Definition: StringRef.h:111
iterator end() const
Definition: StringRef.h:113
const char * data() const
data - Get a pointer to the start of the string (which may not be null terminated).
Definition: StringRef.h:131
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
static Expected< DXContainer > create(MemoryBufferRef Object)
Error parse(uint16_t ShaderKind)
PartType parsePartType(StringRef S)
Definition: DXContainer.cpp:20
Triple::EnvironmentType getShaderStage(uint32_t Kind)
Definition: DXContainer.h:40
constexpr bool IsBigEndianHost
Definition: SwapByteOrder.h:67
void swapByteOrder(T &Value)
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
@ Offset
Definition: DWP.cpp:440
auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object< decltype(std::make_tuple(detail::build_format_adapter(std::forward< Ts >(Vals))...))>
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:745
Use this type to describe the size and type of a DXIL container part.
Definition: DXContainer.h:92