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 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 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 54 void Resources::collect(Module &M) { 55 UAVs.collect(M); 56 CBuffers.collect(M); 57 } 58 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 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 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 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 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 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(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 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