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 25 template <typename T> void ResourceTable<T>::collect(Module &M) { 26 NamedMDNode *Entry = M.getNamedMetadata(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>(Res)))); 33 } 34 } 35 36 template <> void ResourceTable<ConstantBuffer>::collect(Module &M) { 37 NamedMDNode *Entry = M.getNamedMetadata(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 ConstantBuffer(Counter++, hlsl::FrontendResource(cast<MDNode>(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 53 void Resources::collect(Module &M) { 54 UAVs.collect(M); 55 CBuffers.collect(M); 56 } 57 58 ResourceBase::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>(GV->getValueType())) 62 RangeSize = ArrTy->getNumElements(); 63 } 64 65 StringRef 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 109 void 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(getElementTypeName(ElTy), Alignment); 115 break; 116 case ResourceKind::RawBuffer: 117 OS << right_justify("byte", Alignment); 118 break; 119 case ResourceKind::StructuredBuffer: 120 OS << right_justify("struct", Alignment); 121 break; 122 case ResourceKind::CBuffer: 123 case ResourceKind::Sampler: 124 OS << right_justify("NA", Alignment); 125 break; 126 case ResourceKind::Invalid: 127 case ResourceKind::NumEntries: 128 break; 129 } 130 } 131 132 StringRef 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 177 void 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(getKindName(Kind), Alignment); 183 break; 184 185 case ResourceKind::RawBuffer: 186 case ResourceKind::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 ResourceKind::TypedBuffer: 197 OS << right_justify("buf", 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(SampleCount); 204 OS << right_justify(DimName, Alignment); 205 } break; 206 case ResourceKind::CBuffer: 207 case ResourceKind::Sampler: 208 OS << right_justify("NA", Alignment); 209 break; 210 case ResourceKind::Invalid: 211 case ResourceKind::NumEntries: 212 break; 213 } 214 } 215 216 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 234 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 249 ConstantBuffer::ConstantBuffer(uint32_t I, hlsl::FrontendResource R) 250 : ResourceBase(I, R) {} 251 252 void ConstantBuffer::setSize(CBufferDataLayout &DL) { 253 CBufferSizeInBytes = DL.getTypeAllocSizeInBytes(GV->getValueType()); 254 } 255 256 void ConstantBuffer::print(raw_ostream &OS) const { 257 OS << "; " << left_justify(Name, 31); 258 259 OS << right_justify("cbuffer", 10); 260 261 printElementType(ResourceKind::CBuffer, ElementType::Invalid, 8, OS); 262 263 printKind(ResourceKind::CBuffer, 12, OS, /*SRV*/ false, /*HasCounter*/ false); 264 // Print the binding part. 265 ResourceBase::print(OS, "CB", "cb"); 266 } 267 268 template <typename T> void ResourceTable<T>::print(raw_ostream &OS) const { 269 for (auto &Res : Data) 270 Res.print(OS); 271 } 272 273 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 287 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 298 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 312 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 322 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 336 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 356 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 369 void Resources::dump() const { print(dbgs()); } 370