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

source code of llvm/lib/Target/DirectX/DXILResource.cpp