1 //===- DXILResource.cpp - DXIL Resource helper objects --------------------===//
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 This file contains helper objects for working with DXIL Resources.
10 ///
11 //===----------------------------------------------------------------------===//
12
13 #include "DXILResource.h"
14 #include "CBufferDataLayout.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include "llvm/IR/IRBuilder.h"
17 #include "llvm/IR/Metadata.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/Format.h"
21
22 using namespace llvm;
23 using namespace llvm::dxil;
24 using namespace llvm::hlsl;
25
collect(Module & M)26 template <typename T> void ResourceTable<T>::collect(Module &M) {
27 NamedMDNode *Entry = M.getNamedMetadata(MDName);
28 if (!Entry || Entry->getNumOperands() == 0)
29 return;
30
31 uint32_t Counter = 0;
32 for (auto *Res : Entry->operands()) {
33 Data.push_back(T(Counter++, FrontendResource(cast<MDNode>(Res))));
34 }
35 }
36
collect(Module & M)37 template <> void ResourceTable<ConstantBuffer>::collect(Module &M) {
38 NamedMDNode *Entry = M.getNamedMetadata(MDName);
39 if (!Entry || Entry->getNumOperands() == 0)
40 return;
41
42 uint32_t Counter = 0;
43 for (auto *Res : Entry->operands()) {
44 Data.push_back(
45 ConstantBuffer(Counter++, FrontendResource(cast<MDNode>(Res))));
46 }
47 // FIXME: share CBufferDataLayout with CBuffer load lowering.
48 // See https://github.com/llvm/llvm-project/issues/58381
49 CBufferDataLayout CBDL(M.getDataLayout(), /*IsLegacy*/ true);
50 for (auto &CB : Data)
51 CB.setSize(CBDL);
52 }
53
collect(Module & M)54 void Resources::collect(Module &M) {
55 UAVs.collect(M);
56 CBuffers.collect(M);
57 }
58
ResourceBase(uint32_t I,FrontendResource R)59 ResourceBase::ResourceBase(uint32_t I, FrontendResource R)
60 : ID(I), GV(R.getGlobalVariable()), Name(""), Space(R.getSpace()),
61 LowerBound(R.getResourceIndex()), RangeSize(1) {
62 if (auto *ArrTy = dyn_cast<ArrayType>(GV->getValueType()))
63 RangeSize = ArrTy->getNumElements();
64 }
65
getElementTypeName(ElementType ElTy)66 StringRef ResourceBase::getElementTypeName(ElementType ElTy) {
67 switch (ElTy) {
68 case ElementType::Invalid:
69 return "invalid";
70 case ElementType::I1:
71 return "i1";
72 case ElementType::I16:
73 return "i16";
74 case ElementType::U16:
75 return "u16";
76 case ElementType::I32:
77 return "i32";
78 case ElementType::U32:
79 return "u32";
80 case ElementType::I64:
81 return "i64";
82 case ElementType::U64:
83 return "u64";
84 case ElementType::F16:
85 return "f16";
86 case ElementType::F32:
87 return "f32";
88 case ElementType::F64:
89 return "f64";
90 case ElementType::SNormF16:
91 return "snorm_f16";
92 case ElementType::UNormF16:
93 return "unorm_f16";
94 case ElementType::SNormF32:
95 return "snorm_f32";
96 case ElementType::UNormF32:
97 return "unorm_f32";
98 case ElementType::SNormF64:
99 return "snorm_f64";
100 case ElementType::UNormF64:
101 return "unorm_f64";
102 case ElementType::PackedS8x32:
103 return "p32i8";
104 case ElementType::PackedU8x32:
105 return "p32u8";
106 }
107 llvm_unreachable("All ElementType enums are handled in switch");
108 }
109
printElementType(Kinds Kind,ElementType ElTy,unsigned Alignment,raw_ostream & OS)110 void ResourceBase::printElementType(Kinds Kind, ElementType ElTy,
111 unsigned Alignment, raw_ostream &OS) {
112 switch (Kind) {
113 default:
114 // TODO: add vector size.
115 OS << right_justify(getElementTypeName(ElTy), Alignment);
116 break;
117 case Kinds::RawBuffer:
118 OS << right_justify("byte", Alignment);
119 break;
120 case Kinds::StructuredBuffer:
121 OS << right_justify("struct", Alignment);
122 break;
123 case Kinds::CBuffer:
124 case Kinds::Sampler:
125 OS << right_justify("NA", Alignment);
126 break;
127 case Kinds::Invalid:
128 case Kinds::NumEntries:
129 break;
130 }
131 }
132
getKindName(Kinds Kind)133 StringRef ResourceBase::getKindName(Kinds Kind) {
134 switch (Kind) {
135 case Kinds::NumEntries:
136 case Kinds::Invalid:
137 return "invalid";
138 case Kinds::Texture1D:
139 return "1d";
140 case Kinds::Texture2D:
141 return "2d";
142 case Kinds::Texture2DMS:
143 return "2dMS";
144 case Kinds::Texture3D:
145 return "3d";
146 case Kinds::TextureCube:
147 return "cube";
148 case Kinds::Texture1DArray:
149 return "1darray";
150 case Kinds::Texture2DArray:
151 return "2darray";
152 case Kinds::Texture2DMSArray:
153 return "2darrayMS";
154 case Kinds::TextureCubeArray:
155 return "cubearray";
156 case Kinds::TypedBuffer:
157 return "buf";
158 case Kinds::RawBuffer:
159 return "rawbuf";
160 case Kinds::StructuredBuffer:
161 return "structbuf";
162 case Kinds::CBuffer:
163 return "cbuffer";
164 case Kinds::Sampler:
165 return "sampler";
166 case Kinds::TBuffer:
167 return "tbuffer";
168 case Kinds::RTAccelerationStructure:
169 return "ras";
170 case Kinds::FeedbackTexture2D:
171 return "fbtex2d";
172 case Kinds::FeedbackTexture2DArray:
173 return "fbtex2darray";
174 }
175 llvm_unreachable("All Kinds enums are handled in switch");
176 }
177
printKind(Kinds Kind,unsigned Alignment,raw_ostream & OS,bool SRV,bool HasCounter,uint32_t SampleCount)178 void ResourceBase::printKind(Kinds Kind, unsigned Alignment, raw_ostream &OS,
179 bool SRV, bool HasCounter, uint32_t SampleCount) {
180 switch (Kind) {
181 default:
182 OS << right_justify(getKindName(Kind), Alignment);
183 break;
184
185 case Kinds::RawBuffer:
186 case Kinds::StructuredBuffer:
187 if (SRV)
188 OS << right_justify("r/o", Alignment);
189 else {
190 if (!HasCounter)
191 OS << right_justify("r/w", Alignment);
192 else
193 OS << right_justify("r/w+cnt", Alignment);
194 }
195 break;
196 case Kinds::TypedBuffer:
197 OS << right_justify("buf", Alignment);
198 break;
199 case Kinds::Texture2DMS:
200 case Kinds::Texture2DMSArray: {
201 std::string DimName = getKindName(Kind).str();
202 if (SampleCount)
203 DimName += std::to_string(SampleCount);
204 OS << right_justify(DimName, Alignment);
205 } break;
206 case Kinds::CBuffer:
207 case Kinds::Sampler:
208 OS << right_justify("NA", Alignment);
209 break;
210 case Kinds::Invalid:
211 case Kinds::NumEntries:
212 break;
213 }
214 }
215
print(raw_ostream & OS,StringRef IDPrefix,StringRef BindingPrefix) const216 void ResourceBase::print(raw_ostream &OS, StringRef IDPrefix,
217 StringRef BindingPrefix) const {
218 std::string ResID = IDPrefix.str();
219 ResID += std::to_string(ID);
220 OS << right_justify(ResID, 8);
221
222 std::string Bind = BindingPrefix.str();
223 Bind += std::to_string(LowerBound);
224 if (Space)
225 Bind += ",space" + std::to_string(Space);
226
227 OS << right_justify(Bind, 15);
228 if (RangeSize != UINT_MAX)
229 OS << right_justify(std::to_string(RangeSize), 6) << "\n";
230 else
231 OS << right_justify("unbounded", 6) << "\n";
232 }
233
print(raw_ostream & OS) const234 void UAVResource::print(raw_ostream &OS) const {
235 OS << "; " << left_justify(Name, 31);
236
237 OS << right_justify("UAV", 10);
238
239 printElementType(Shape, ExtProps.ElementType.value_or(ElementType::Invalid),
240 8, OS);
241
242 // FIXME: support SampleCount.
243 // See https://github.com/llvm/llvm-project/issues/58175
244 printKind(Shape, 12, OS, /*SRV*/ false, HasCounter);
245 // Print the binding part.
246 ResourceBase::print(OS, "U", "u");
247 }
248
ConstantBuffer(uint32_t I,hlsl::FrontendResource R)249 ConstantBuffer::ConstantBuffer(uint32_t I, hlsl::FrontendResource R)
250 : ResourceBase(I, R) {}
251
setSize(CBufferDataLayout & DL)252 void ConstantBuffer::setSize(CBufferDataLayout &DL) {
253 CBufferSizeInBytes = DL.getTypeAllocSizeInBytes(GV->getValueType());
254 }
255
print(raw_ostream & OS) const256 void ConstantBuffer::print(raw_ostream &OS) const {
257 OS << "; " << left_justify(Name, 31);
258
259 OS << right_justify("cbuffer", 10);
260
261 printElementType(Kinds::CBuffer, ElementType::Invalid, 8, OS);
262
263 printKind(Kinds::CBuffer, 12, OS, /*SRV*/ false, /*HasCounter*/ false);
264 // Print the binding part.
265 ResourceBase::print(OS, "CB", "cb");
266 }
267
print(raw_ostream & OS) const268 template <typename T> void ResourceTable<T>::print(raw_ostream &OS) const {
269 for (auto &Res : Data)
270 Res.print(OS);
271 }
272
write(LLVMContext & Ctx) const273 MDNode *ResourceBase::ExtendedProperties::write(LLVMContext &Ctx) const {
274 IRBuilder<> B(Ctx);
275 SmallVector<Metadata *> Entries;
276 if (ElementType) {
277 Entries.emplace_back(
278 ConstantAsMetadata::get(B.getInt32(TypedBufferElementType)));
279 Entries.emplace_back(ConstantAsMetadata::get(
280 B.getInt32(static_cast<uint32_t>(*ElementType))));
281 }
282 if (Entries.empty())
283 return nullptr;
284 return MDNode::get(Ctx, Entries);
285 }
286
write(LLVMContext & Ctx,MutableArrayRef<Metadata * > Entries) const287 void ResourceBase::write(LLVMContext &Ctx,
288 MutableArrayRef<Metadata *> Entries) const {
289 IRBuilder<> B(Ctx);
290 Entries[0] = ConstantAsMetadata::get(B.getInt32(ID));
291 Entries[1] = ConstantAsMetadata::get(GV);
292 Entries[2] = MDString::get(Ctx, Name);
293 Entries[3] = ConstantAsMetadata::get(B.getInt32(Space));
294 Entries[4] = ConstantAsMetadata::get(B.getInt32(LowerBound));
295 Entries[5] = ConstantAsMetadata::get(B.getInt32(RangeSize));
296 }
297
write() const298 MDNode *UAVResource::write() const {
299 auto &Ctx = GV->getContext();
300 IRBuilder<> B(Ctx);
301 Metadata *Entries[11];
302 ResourceBase::write(Ctx, Entries);
303 Entries[6] =
304 ConstantAsMetadata::get(B.getInt32(static_cast<uint32_t>(Shape)));
305 Entries[7] = ConstantAsMetadata::get(B.getInt1(GloballyCoherent));
306 Entries[8] = ConstantAsMetadata::get(B.getInt1(HasCounter));
307 Entries[9] = ConstantAsMetadata::get(B.getInt1(IsROV));
308 Entries[10] = ExtProps.write(Ctx);
309 return MDNode::get(Ctx, Entries);
310 }
311
write() const312 MDNode *ConstantBuffer::write() const {
313 auto &Ctx = GV->getContext();
314 IRBuilder<> B(Ctx);
315 Metadata *Entries[7];
316 ResourceBase::write(Ctx, Entries);
317
318 Entries[6] = ConstantAsMetadata::get(B.getInt32(CBufferSizeInBytes));
319 return MDNode::get(Ctx, Entries);
320 }
321
write(Module & M) const322 template <typename T> MDNode *ResourceTable<T>::write(Module &M) const {
323 if (Data.empty())
324 return nullptr;
325 SmallVector<Metadata *> MDs;
326 for (auto &Res : Data)
327 MDs.emplace_back(Res.write());
328
329 NamedMDNode *Entry = M.getNamedMetadata(MDName);
330 if (Entry)
331 Entry->eraseFromParent();
332
333 return MDNode::get(M.getContext(), MDs);
334 }
335
write(Module & M) const336 void Resources::write(Module &M) const {
337 Metadata *ResourceMDs[4] = {nullptr, nullptr, nullptr, nullptr};
338
339 ResourceMDs[1] = UAVs.write(M);
340
341 ResourceMDs[2] = CBuffers.write(M);
342
343 bool HasResource = ResourceMDs[0] != nullptr || ResourceMDs[1] != nullptr ||
344 ResourceMDs[2] != nullptr || ResourceMDs[3] != nullptr;
345
346 if (HasResource) {
347 NamedMDNode *DXResMD = M.getOrInsertNamedMetadata("dx.resources");
348 DXResMD->addOperand(MDNode::get(M.getContext(), ResourceMDs));
349 }
350
351 NamedMDNode *Entry = M.getNamedMetadata("hlsl.uavs");
352 if (Entry)
353 Entry->eraseFromParent();
354 }
355
print(raw_ostream & O) const356 void Resources::print(raw_ostream &O) const {
357 O << ";\n"
358 << "; Resource Bindings:\n"
359 << ";\n"
360 << "; Name Type Format Dim "
361 "ID HLSL Bind Count\n"
362 << "; ------------------------------ ---------- ------- ----------- "
363 "------- -------------- ------\n";
364
365 CBuffers.print(O);
366 UAVs.print(O);
367 }
368
dump() const369 void Resources::dump() const { print(dbgs()); }
370