| 1 | //===- DXILTranslateMetadata.cpp - Pass to emit DXIL metadata -------------===// |
| 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 | #include "DXILTranslateMetadata.h" |
| 10 | #include "DXILRootSignature.h" |
| 11 | #include "DXILShaderFlags.h" |
| 12 | #include "DirectX.h" |
| 13 | #include "llvm/ADT/STLExtras.h" |
| 14 | #include "llvm/ADT/SmallVector.h" |
| 15 | #include "llvm/ADT/Twine.h" |
| 16 | #include "llvm/Analysis/DXILMetadataAnalysis.h" |
| 17 | #include "llvm/Analysis/DXILResource.h" |
| 18 | #include "llvm/IR/BasicBlock.h" |
| 19 | #include "llvm/IR/Constants.h" |
| 20 | #include "llvm/IR/DiagnosticInfo.h" |
| 21 | #include "llvm/IR/DiagnosticPrinter.h" |
| 22 | #include "llvm/IR/Function.h" |
| 23 | #include "llvm/IR/IRBuilder.h" |
| 24 | #include "llvm/IR/LLVMContext.h" |
| 25 | #include "llvm/IR/MDBuilder.h" |
| 26 | #include "llvm/IR/Metadata.h" |
| 27 | #include "llvm/IR/Module.h" |
| 28 | #include "llvm/InitializePasses.h" |
| 29 | #include "llvm/Pass.h" |
| 30 | #include "llvm/Support/ErrorHandling.h" |
| 31 | #include "llvm/Support/VersionTuple.h" |
| 32 | #include "llvm/TargetParser/Triple.h" |
| 33 | #include <cstdint> |
| 34 | |
| 35 | using namespace llvm; |
| 36 | using namespace llvm::dxil; |
| 37 | |
| 38 | namespace { |
| 39 | |
| 40 | /// A simple wrapper of DiagnosticInfo that generates module-level diagnostic |
| 41 | /// for the DXILValidateMetadata pass |
| 42 | class DiagnosticInfoValidateMD : public DiagnosticInfo { |
| 43 | private: |
| 44 | const Twine &Msg; |
| 45 | const Module &Mod; |
| 46 | |
| 47 | public: |
| 48 | /// \p M is the module for which the diagnostic is being emitted. \p Msg is |
| 49 | /// the message to show. Note that this class does not copy this message, so |
| 50 | /// this reference must be valid for the whole life time of the diagnostic. |
| 51 | DiagnosticInfoValidateMD(const Module &M, |
| 52 | const Twine &Msg LLVM_LIFETIME_BOUND, |
| 53 | DiagnosticSeverity Severity = DS_Error) |
| 54 | : DiagnosticInfo(DK_Unsupported, Severity), Msg(Msg), Mod(M) {} |
| 55 | |
| 56 | void print(DiagnosticPrinter &DP) const override { |
| 57 | DP << Mod.getName() << ": " << Msg << '\n'; |
| 58 | } |
| 59 | }; |
| 60 | |
| 61 | static void reportError(Module &M, Twine Message, |
| 62 | DiagnosticSeverity Severity = DS_Error) { |
| 63 | M.getContext().diagnose(DI: DiagnosticInfoValidateMD(M, Message, Severity)); |
| 64 | } |
| 65 | |
| 66 | static void reportLoopError(Module &M, Twine Message, |
| 67 | DiagnosticSeverity Severity = DS_Error) { |
| 68 | reportError(M, Message: Twine("Invalid \"llvm.loop\" metadata: " ) + Message, Severity); |
| 69 | } |
| 70 | |
| 71 | enum class EntryPropsTag { |
| 72 | ShaderFlags = 0, |
| 73 | GSState, |
| 74 | DSState, |
| 75 | HSState, |
| 76 | NumThreads, |
| 77 | AutoBindingSpace, |
| 78 | RayPayloadSize, |
| 79 | RayAttribSize, |
| 80 | ShaderKind, |
| 81 | MSState, |
| 82 | ASStateTag, |
| 83 | WaveSize, |
| 84 | EntryRootSig, |
| 85 | WaveRange = 23, |
| 86 | }; |
| 87 | |
| 88 | } // namespace |
| 89 | |
| 90 | static NamedMDNode *emitResourceMetadata(Module &M, DXILResourceMap &DRM, |
| 91 | DXILResourceTypeMap &DRTM) { |
| 92 | LLVMContext &Context = M.getContext(); |
| 93 | |
| 94 | for (ResourceInfo &RI : DRM) |
| 95 | if (RI.hasBinding() && !RI.hasSymbol()) |
| 96 | RI.createSymbol(M, |
| 97 | Ty: DRTM[RI.getHandleTy()].createElementStruct(CBufferName: RI.getName())); |
| 98 | |
| 99 | SmallVector<Metadata *> SRVs, UAVs, CBufs, Smps; |
| 100 | for (const ResourceInfo &RI : DRM.srvs()) |
| 101 | if (RI.hasBinding()) |
| 102 | SRVs.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()])); |
| 103 | for (const ResourceInfo &RI : DRM.uavs()) |
| 104 | if (RI.hasBinding()) |
| 105 | UAVs.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()])); |
| 106 | for (const ResourceInfo &RI : DRM.cbuffers()) |
| 107 | if (RI.hasBinding()) |
| 108 | CBufs.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()])); |
| 109 | for (const ResourceInfo &RI : DRM.samplers()) |
| 110 | if (RI.hasBinding()) |
| 111 | Smps.push_back(Elt: RI.getAsMetadata(M, RTI&: DRTM[RI.getHandleTy()])); |
| 112 | |
| 113 | Metadata *SRVMD = SRVs.empty() ? nullptr : MDNode::get(Context, MDs: SRVs); |
| 114 | Metadata *UAVMD = UAVs.empty() ? nullptr : MDNode::get(Context, MDs: UAVs); |
| 115 | Metadata *CBufMD = CBufs.empty() ? nullptr : MDNode::get(Context, MDs: CBufs); |
| 116 | Metadata *SmpMD = Smps.empty() ? nullptr : MDNode::get(Context, MDs: Smps); |
| 117 | |
| 118 | if (DRM.empty()) |
| 119 | return nullptr; |
| 120 | |
| 121 | NamedMDNode *ResourceMD = M.getOrInsertNamedMetadata(Name: "dx.resources" ); |
| 122 | ResourceMD->addOperand( |
| 123 | M: MDNode::get(Context&: M.getContext(), MDs: {SRVMD, UAVMD, CBufMD, SmpMD})); |
| 124 | |
| 125 | return ResourceMD; |
| 126 | } |
| 127 | |
| 128 | static StringRef getShortShaderStage(Triple::EnvironmentType Env) { |
| 129 | switch (Env) { |
| 130 | case Triple::Pixel: |
| 131 | return "ps" ; |
| 132 | case Triple::Vertex: |
| 133 | return "vs" ; |
| 134 | case Triple::Geometry: |
| 135 | return "gs" ; |
| 136 | case Triple::Hull: |
| 137 | return "hs" ; |
| 138 | case Triple::Domain: |
| 139 | return "ds" ; |
| 140 | case Triple::Compute: |
| 141 | return "cs" ; |
| 142 | case Triple::Library: |
| 143 | return "lib" ; |
| 144 | case Triple::Mesh: |
| 145 | return "ms" ; |
| 146 | case Triple::Amplification: |
| 147 | return "as" ; |
| 148 | case Triple::RootSignature: |
| 149 | return "rootsig" ; |
| 150 | default: |
| 151 | break; |
| 152 | } |
| 153 | llvm_unreachable("Unsupported environment for DXIL generation." ); |
| 154 | } |
| 155 | |
| 156 | static uint32_t getShaderStage(Triple::EnvironmentType Env) { |
| 157 | return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel; |
| 158 | } |
| 159 | |
| 160 | static SmallVector<Metadata *> |
| 161 | getTagValueAsMetadata(EntryPropsTag Tag, uint64_t Value, LLVMContext &Ctx) { |
| 162 | SmallVector<Metadata *> MDVals; |
| 163 | MDVals.emplace_back(Args: ConstantAsMetadata::get( |
| 164 | C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: static_cast<int>(Tag)))); |
| 165 | switch (Tag) { |
| 166 | case EntryPropsTag::ShaderFlags: |
| 167 | MDVals.emplace_back(Args: ConstantAsMetadata::get( |
| 168 | C: ConstantInt::get(Ty: Type::getInt64Ty(C&: Ctx), V: Value))); |
| 169 | break; |
| 170 | case EntryPropsTag::ShaderKind: |
| 171 | MDVals.emplace_back(Args: ConstantAsMetadata::get( |
| 172 | C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: Value))); |
| 173 | break; |
| 174 | case EntryPropsTag::GSState: |
| 175 | case EntryPropsTag::DSState: |
| 176 | case EntryPropsTag::HSState: |
| 177 | case EntryPropsTag::NumThreads: |
| 178 | case EntryPropsTag::AutoBindingSpace: |
| 179 | case EntryPropsTag::RayPayloadSize: |
| 180 | case EntryPropsTag::RayAttribSize: |
| 181 | case EntryPropsTag::MSState: |
| 182 | case EntryPropsTag::ASStateTag: |
| 183 | case EntryPropsTag::WaveSize: |
| 184 | case EntryPropsTag::EntryRootSig: |
| 185 | case EntryPropsTag::WaveRange: |
| 186 | llvm_unreachable("NYI: Unhandled entry property tag" ); |
| 187 | } |
| 188 | return MDVals; |
| 189 | } |
| 190 | |
| 191 | static MDTuple *getEntryPropAsMetadata(Module &M, const EntryProperties &EP, |
| 192 | uint64_t EntryShaderFlags, |
| 193 | const ModuleMetadataInfo &MMDI) { |
| 194 | SmallVector<Metadata *> MDVals; |
| 195 | LLVMContext &Ctx = EP.Entry->getContext(); |
| 196 | if (EntryShaderFlags != 0) |
| 197 | MDVals.append(RHS: getTagValueAsMetadata(Tag: EntryPropsTag::ShaderFlags, |
| 198 | Value: EntryShaderFlags, Ctx)); |
| 199 | |
| 200 | if (EP.Entry != nullptr) { |
| 201 | // FIXME: support more props. |
| 202 | // See https://github.com/llvm/llvm-project/issues/57948. |
| 203 | // Add shader kind for lib entries. |
| 204 | if (MMDI.ShaderProfile == Triple::EnvironmentType::Library && |
| 205 | EP.ShaderStage != Triple::EnvironmentType::Library) |
| 206 | MDVals.append(RHS: getTagValueAsMetadata(Tag: EntryPropsTag::ShaderKind, |
| 207 | Value: getShaderStage(Env: EP.ShaderStage), Ctx)); |
| 208 | |
| 209 | if (EP.ShaderStage == Triple::EnvironmentType::Compute) { |
| 210 | // Handle mandatory "hlsl.numthreads" |
| 211 | MDVals.emplace_back(Args: ConstantAsMetadata::get(C: ConstantInt::get( |
| 212 | Ty: Type::getInt32Ty(C&: Ctx), V: static_cast<int>(EntryPropsTag::NumThreads)))); |
| 213 | Metadata *NumThreadVals[] = {ConstantAsMetadata::get(C: ConstantInt::get( |
| 214 | Ty: Type::getInt32Ty(C&: Ctx), V: EP.NumThreadsX)), |
| 215 | ConstantAsMetadata::get(C: ConstantInt::get( |
| 216 | Ty: Type::getInt32Ty(C&: Ctx), V: EP.NumThreadsY)), |
| 217 | ConstantAsMetadata::get(C: ConstantInt::get( |
| 218 | Ty: Type::getInt32Ty(C&: Ctx), V: EP.NumThreadsZ))}; |
| 219 | MDVals.emplace_back(Args: MDNode::get(Context&: Ctx, MDs: NumThreadVals)); |
| 220 | |
| 221 | // Handle optional "hlsl.wavesize". The fields are optionally represented |
| 222 | // if they are non-zero. |
| 223 | if (EP.WaveSizeMin != 0) { |
| 224 | bool IsWaveRange = VersionTuple(6, 8) <= MMDI.ShaderModelVersion; |
| 225 | bool IsWaveSize = |
| 226 | !IsWaveRange && VersionTuple(6, 6) <= MMDI.ShaderModelVersion; |
| 227 | |
| 228 | if (!IsWaveRange && !IsWaveSize) { |
| 229 | reportError(M, Message: "Shader model 6.6 or greater is required to specify " |
| 230 | "the \"hlsl.wavesize\" function attribute" ); |
| 231 | return nullptr; |
| 232 | } |
| 233 | |
| 234 | // A range is being specified if EP.WaveSizeMax != 0 |
| 235 | if (EP.WaveSizeMax && !IsWaveRange) { |
| 236 | reportError( |
| 237 | M, Message: "Shader model 6.8 or greater is required to specify " |
| 238 | "wave size range values of the \"hlsl.wavesize\" function " |
| 239 | "attribute" ); |
| 240 | return nullptr; |
| 241 | } |
| 242 | |
| 243 | EntryPropsTag Tag = |
| 244 | IsWaveSize ? EntryPropsTag::WaveSize : EntryPropsTag::WaveRange; |
| 245 | MDVals.emplace_back(Args: ConstantAsMetadata::get( |
| 246 | C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: static_cast<int>(Tag)))); |
| 247 | |
| 248 | SmallVector<Metadata *> WaveSizeVals = {ConstantAsMetadata::get( |
| 249 | C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: EP.WaveSizeMin))}; |
| 250 | if (IsWaveRange) { |
| 251 | WaveSizeVals.push_back(Elt: ConstantAsMetadata::get( |
| 252 | C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: EP.WaveSizeMax))); |
| 253 | WaveSizeVals.push_back(Elt: ConstantAsMetadata::get( |
| 254 | C: ConstantInt::get(Ty: Type::getInt32Ty(C&: Ctx), V: EP.WaveSizePref))); |
| 255 | } |
| 256 | |
| 257 | MDVals.emplace_back(Args: MDNode::get(Context&: Ctx, MDs: WaveSizeVals)); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | if (MDVals.empty()) |
| 263 | return nullptr; |
| 264 | return MDNode::get(Context&: Ctx, MDs: MDVals); |
| 265 | } |
| 266 | |
| 267 | static MDTuple *constructEntryMetadata(const Function *EntryFn, |
| 268 | MDTuple *Signatures, MDNode *Resources, |
| 269 | MDTuple *Properties, LLVMContext &Ctx) { |
| 270 | // Each entry point metadata record specifies: |
| 271 | // * reference to the entry point function global symbol |
| 272 | // * unmangled name |
| 273 | // * list of signatures |
| 274 | // * list of resources |
| 275 | // * list of tag-value pairs of shader capabilities and other properties |
| 276 | Metadata *MDVals[5]; |
| 277 | MDVals[0] = |
| 278 | EntryFn ? ValueAsMetadata::get(V: const_cast<Function *>(EntryFn)) : nullptr; |
| 279 | MDVals[1] = MDString::get(Context&: Ctx, Str: EntryFn ? EntryFn->getName() : "" ); |
| 280 | MDVals[2] = Signatures; |
| 281 | MDVals[3] = Resources; |
| 282 | MDVals[4] = Properties; |
| 283 | return MDNode::get(Context&: Ctx, MDs: MDVals); |
| 284 | } |
| 285 | |
| 286 | static MDTuple *emitEntryMD(Module &M, const EntryProperties &EP, |
| 287 | MDTuple *Signatures, MDNode *MDResources, |
| 288 | const uint64_t EntryShaderFlags, |
| 289 | const ModuleMetadataInfo &MMDI) { |
| 290 | MDTuple *Properties = getEntryPropAsMetadata(M, EP, EntryShaderFlags, MMDI); |
| 291 | return constructEntryMetadata(EntryFn: EP.Entry, Signatures, Resources: MDResources, Properties, |
| 292 | Ctx&: EP.Entry->getContext()); |
| 293 | } |
| 294 | |
| 295 | static void emitValidatorVersionMD(Module &M, const ModuleMetadataInfo &MMDI) { |
| 296 | if (MMDI.ValidatorVersion.empty()) |
| 297 | return; |
| 298 | |
| 299 | LLVMContext &Ctx = M.getContext(); |
| 300 | IRBuilder<> IRB(Ctx); |
| 301 | Metadata *MDVals[2]; |
| 302 | MDVals[0] = |
| 303 | ConstantAsMetadata::get(C: IRB.getInt32(C: MMDI.ValidatorVersion.getMajor())); |
| 304 | MDVals[1] = ConstantAsMetadata::get( |
| 305 | C: IRB.getInt32(C: MMDI.ValidatorVersion.getMinor().value_or(u: 0))); |
| 306 | NamedMDNode *ValVerNode = M.getOrInsertNamedMetadata(Name: "dx.valver" ); |
| 307 | // Set validator version obtained from DXIL Metadata Analysis pass |
| 308 | ValVerNode->clearOperands(); |
| 309 | ValVerNode->addOperand(M: MDNode::get(Context&: Ctx, MDs: MDVals)); |
| 310 | } |
| 311 | |
| 312 | static void emitShaderModelVersionMD(Module &M, |
| 313 | const ModuleMetadataInfo &MMDI) { |
| 314 | LLVMContext &Ctx = M.getContext(); |
| 315 | IRBuilder<> IRB(Ctx); |
| 316 | Metadata *SMVals[3]; |
| 317 | VersionTuple SM = MMDI.ShaderModelVersion; |
| 318 | SMVals[0] = MDString::get(Context&: Ctx, Str: getShortShaderStage(Env: MMDI.ShaderProfile)); |
| 319 | SMVals[1] = ConstantAsMetadata::get(C: IRB.getInt32(C: SM.getMajor())); |
| 320 | SMVals[2] = ConstantAsMetadata::get(C: IRB.getInt32(C: SM.getMinor().value_or(u: 0))); |
| 321 | NamedMDNode *SMMDNode = M.getOrInsertNamedMetadata(Name: "dx.shaderModel" ); |
| 322 | SMMDNode->addOperand(M: MDNode::get(Context&: Ctx, MDs: SMVals)); |
| 323 | } |
| 324 | |
| 325 | static void emitDXILVersionTupleMD(Module &M, const ModuleMetadataInfo &MMDI) { |
| 326 | LLVMContext &Ctx = M.getContext(); |
| 327 | IRBuilder<> IRB(Ctx); |
| 328 | VersionTuple DXILVer = MMDI.DXILVersion; |
| 329 | Metadata *DXILVals[2]; |
| 330 | DXILVals[0] = ConstantAsMetadata::get(C: IRB.getInt32(C: DXILVer.getMajor())); |
| 331 | DXILVals[1] = |
| 332 | ConstantAsMetadata::get(C: IRB.getInt32(C: DXILVer.getMinor().value_or(u: 0))); |
| 333 | NamedMDNode *DXILVerMDNode = M.getOrInsertNamedMetadata(Name: "dx.version" ); |
| 334 | DXILVerMDNode->addOperand(M: MDNode::get(Context&: Ctx, MDs: DXILVals)); |
| 335 | } |
| 336 | |
| 337 | static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD, |
| 338 | uint64_t ShaderFlags) { |
| 339 | LLVMContext &Ctx = M.getContext(); |
| 340 | MDTuple *Properties = nullptr; |
| 341 | if (ShaderFlags != 0) { |
| 342 | SmallVector<Metadata *> MDVals; |
| 343 | MDVals.append( |
| 344 | RHS: getTagValueAsMetadata(Tag: EntryPropsTag::ShaderFlags, Value: ShaderFlags, Ctx)); |
| 345 | Properties = MDNode::get(Context&: Ctx, MDs: MDVals); |
| 346 | } |
| 347 | // Library has an entry metadata with resource table metadata and all other |
| 348 | // MDNodes as null. |
| 349 | return constructEntryMetadata(EntryFn: nullptr, Signatures: nullptr, Resources: RMD, Properties, Ctx); |
| 350 | } |
| 351 | |
| 352 | static void translateBranchMetadata(Module &M, Instruction *BBTerminatorInst) { |
| 353 | MDNode *HlslControlFlowMD = |
| 354 | BBTerminatorInst->getMetadata(Kind: "hlsl.controlflow.hint" ); |
| 355 | |
| 356 | if (!HlslControlFlowMD) |
| 357 | return; |
| 358 | |
| 359 | assert(HlslControlFlowMD->getNumOperands() == 2 && |
| 360 | "invalid operands for hlsl.controlflow.hint" ); |
| 361 | |
| 362 | MDBuilder MDHelper(M.getContext()); |
| 363 | |
| 364 | llvm::Metadata *HintsStr = MDHelper.createString(Str: "dx.controlflow.hints" ); |
| 365 | llvm::Metadata *HintsValue = MDHelper.createConstant( |
| 366 | C: mdconst::extract<ConstantInt>(MD: HlslControlFlowMD->getOperand(I: 1))); |
| 367 | |
| 368 | MDNode *MDNode = llvm::MDNode::get(Context&: M.getContext(), MDs: {HintsStr, HintsValue}); |
| 369 | |
| 370 | BBTerminatorInst->setMetadata(Kind: "dx.controlflow.hints" , Node: MDNode); |
| 371 | BBTerminatorInst->setMetadata(Kind: "hlsl.controlflow.hint" , Node: nullptr); |
| 372 | } |
| 373 | |
| 374 | // Determines if the metadata node will be compatible with DXIL's loop metadata |
| 375 | // representation. |
| 376 | // |
| 377 | // Reports an error for compatible metadata that is ill-formed. |
| 378 | static bool isLoopMDCompatible(Module &M, Metadata *MD) { |
| 379 | // DXIL only accepts the following loop hints: |
| 380 | std::array<StringLiteral, 3> ValidHintNames = {"llvm.loop.unroll.count" , |
| 381 | "llvm.loop.unroll.disable" , |
| 382 | "llvm.loop.unroll.full" }; |
| 383 | |
| 384 | MDNode *HintMD = dyn_cast<MDNode>(Val: MD); |
| 385 | if (!HintMD || HintMD->getNumOperands() == 0) |
| 386 | return false; |
| 387 | |
| 388 | auto *HintStr = dyn_cast<MDString>(Val: HintMD->getOperand(I: 0)); |
| 389 | if (!HintStr) |
| 390 | return false; |
| 391 | |
| 392 | if (!llvm::is_contained(Range&: ValidHintNames, Element: HintStr->getString())) |
| 393 | return false; |
| 394 | |
| 395 | auto ValidCountNode = [](MDNode *CountMD) -> bool { |
| 396 | if (CountMD->getNumOperands() == 2) |
| 397 | if (auto *Count = dyn_cast<ConstantAsMetadata>(Val: CountMD->getOperand(I: 1))) |
| 398 | if (isa<ConstantInt>(Val: Count->getValue())) |
| 399 | return true; |
| 400 | return false; |
| 401 | }; |
| 402 | |
| 403 | if (HintStr->getString() == "llvm.loop.unroll.count" ) { |
| 404 | if (!ValidCountNode(HintMD)) { |
| 405 | reportLoopError(M, Message: "\"llvm.loop.unroll.count\" must have 2 operands and " |
| 406 | "the second must be a constant integer" ); |
| 407 | return false; |
| 408 | } |
| 409 | } else if (HintMD->getNumOperands() != 1) { |
| 410 | reportLoopError( |
| 411 | M, Message: "\"llvm.loop.unroll.disable\" and \"llvm.loop.unroll.full\" " |
| 412 | "must be provided as a single operand" ); |
| 413 | return false; |
| 414 | } |
| 415 | |
| 416 | return true; |
| 417 | } |
| 418 | |
| 419 | static void translateLoopMetadata(Module &M, Instruction *I, MDNode *BaseMD) { |
| 420 | // A distinct node has the self-referential form: !0 = !{ !0, ... } |
| 421 | auto IsDistinctNode = [](MDNode *Node) -> bool { |
| 422 | return Node && Node->getNumOperands() != 0 && Node == Node->getOperand(I: 0); |
| 423 | }; |
| 424 | |
| 425 | // Set metadata to null to remove empty/ill-formed metadata from instruction |
| 426 | if (BaseMD->getNumOperands() == 0 || !IsDistinctNode(BaseMD)) |
| 427 | return I->setMetadata(Kind: "llvm.loop" , Node: nullptr); |
| 428 | |
| 429 | // It is valid to have a chain of self-refential loop metadata nodes, as |
| 430 | // below. We will collapse these into just one when we reconstruct the |
| 431 | // metadata. |
| 432 | // |
| 433 | // Eg: |
| 434 | // !0 = !{!0, !1} |
| 435 | // !1 = !{!1, !2} |
| 436 | // !2 = !{!"llvm.loop.unroll.disable"} |
| 437 | // |
| 438 | // So, traverse down a potential self-referential chain |
| 439 | while (1 < BaseMD->getNumOperands() && |
| 440 | IsDistinctNode(dyn_cast<MDNode>(Val: BaseMD->getOperand(I: 1)))) |
| 441 | BaseMD = dyn_cast<MDNode>(Val: BaseMD->getOperand(I: 1)); |
| 442 | |
| 443 | // To reconstruct a distinct node we create a temporary node that we will |
| 444 | // then update to create a self-reference. |
| 445 | llvm::TempMDTuple TempNode = llvm::MDNode::getTemporary(Context&: M.getContext(), MDs: {}); |
| 446 | SmallVector<Metadata *> CompatibleOperands = {TempNode.get()}; |
| 447 | |
| 448 | // Iterate and reconstruct the metadata nodes that contains any hints, |
| 449 | // stripping any unrecognized metadata. |
| 450 | ArrayRef<MDOperand> Operands = BaseMD->operands(); |
| 451 | for (auto &Op : Operands.drop_front()) |
| 452 | if (isLoopMDCompatible(M, MD: Op.get())) |
| 453 | CompatibleOperands.push_back(Elt: Op.get()); |
| 454 | |
| 455 | if (2 < CompatibleOperands.size()) |
| 456 | reportLoopError(M, Message: "Provided conflicting hints" ); |
| 457 | |
| 458 | MDNode *CompatibleLoopMD = MDNode::get(Context&: M.getContext(), MDs: CompatibleOperands); |
| 459 | TempNode->replaceAllUsesWith(MD: CompatibleLoopMD); |
| 460 | |
| 461 | I->setMetadata(Kind: "llvm.loop" , Node: CompatibleLoopMD); |
| 462 | } |
| 463 | |
| 464 | using InstructionMDList = std::array<unsigned, 7>; |
| 465 | |
| 466 | static InstructionMDList getCompatibleInstructionMDs(llvm::Module &M) { |
| 467 | return { |
| 468 | M.getMDKindID(Name: "dx.nonuniform" ), M.getMDKindID(Name: "dx.controlflow.hints" ), |
| 469 | M.getMDKindID(Name: "dx.precise" ), llvm::LLVMContext::MD_range, |
| 470 | llvm::LLVMContext::MD_alias_scope, llvm::LLVMContext::MD_noalias, |
| 471 | M.getMDKindID(Name: "llvm.loop" )}; |
| 472 | } |
| 473 | |
| 474 | static void translateInstructionMetadata(Module &M) { |
| 475 | // construct allowlist of valid metadata node kinds |
| 476 | InstructionMDList DXILCompatibleMDs = getCompatibleInstructionMDs(M); |
| 477 | unsigned char MDLoopKind = M.getContext().getMDKindID(Name: "llvm.loop" ); |
| 478 | |
| 479 | for (Function &F : M) { |
| 480 | for (BasicBlock &BB : F) { |
| 481 | // This needs to be done first so that "hlsl.controlflow.hints" isn't |
| 482 | // removed in the allow-list below |
| 483 | if (auto *I = BB.getTerminator()) |
| 484 | translateBranchMetadata(M, BBTerminatorInst: I); |
| 485 | |
| 486 | for (auto &I : make_early_inc_range(Range&: BB)) { |
| 487 | if (isa<UncondBrInst, CondBrInst>(Val: I)) |
| 488 | if (MDNode *LoopMD = I.getMetadata(KindID: MDLoopKind)) |
| 489 | translateLoopMetadata(M, I: &I, BaseMD: LoopMD); |
| 490 | I.dropUnknownNonDebugMetadata(KnownIDs: DXILCompatibleMDs); |
| 491 | } |
| 492 | } |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | static void cleanModuleFlags(Module &M) { |
| 497 | NamedMDNode *MDFlags = M.getModuleFlagsMetadata(); |
| 498 | if (!MDFlags) |
| 499 | return; |
| 500 | |
| 501 | SmallVector<llvm::Module::ModuleFlagEntry> FlagEntries; |
| 502 | M.getModuleFlagsMetadata(Flags&: FlagEntries); |
| 503 | bool Updated = false; |
| 504 | for (auto &Flag : FlagEntries) { |
| 505 | // llvm 3.7 only supports behavior up to AppendUnique. |
| 506 | if (Flag.Behavior <= Module::ModFlagBehavior::AppendUnique) |
| 507 | continue; |
| 508 | Flag.Behavior = Module::ModFlagBehavior::Warning; |
| 509 | Updated = true; |
| 510 | } |
| 511 | |
| 512 | if (!Updated) |
| 513 | return; |
| 514 | |
| 515 | MDFlags->eraseFromParent(); |
| 516 | |
| 517 | for (auto &Flag : FlagEntries) |
| 518 | M.addModuleFlag(Behavior: Flag.Behavior, Key: Flag.Key->getString(), Val: Flag.Val); |
| 519 | } |
| 520 | |
| 521 | using GlobalMDList = std::array<StringLiteral, 11>; |
| 522 | |
| 523 | // The following are compatible with DXIL but not emit with clang, they can |
| 524 | // be added when applicable: |
| 525 | // dx.typeAnnotations, dx.viewIDState, dx.dxrPayloadAnnotations |
| 526 | static GlobalMDList CompatibleNamedModuleMDs = { |
| 527 | "llvm.ident" , "llvm.module.flags" , |
| 528 | "dx.resources" , "dx.valver" , |
| 529 | "dx.shaderModel" , "dx.version" , |
| 530 | "dx.entryPoints" , "dx.source.contents" , |
| 531 | "dx.source.defines" , "dx.source.mainFileName" , |
| 532 | "dx.source.args" }; |
| 533 | |
| 534 | static void translateGlobalMetadata(Module &M, DXILResourceMap &DRM, |
| 535 | DXILResourceTypeMap &DRTM, |
| 536 | const ModuleShaderFlags &ShaderFlags, |
| 537 | const ModuleMetadataInfo &MMDI) { |
| 538 | LLVMContext &Ctx = M.getContext(); |
| 539 | IRBuilder<> IRB(Ctx); |
| 540 | SmallVector<MDNode *> EntryFnMDNodes; |
| 541 | |
| 542 | emitValidatorVersionMD(M, MMDI); |
| 543 | emitShaderModelVersionMD(M, MMDI); |
| 544 | emitDXILVersionTupleMD(M, MMDI); |
| 545 | NamedMDNode *NamedResourceMD = emitResourceMetadata(M, DRM, DRTM); |
| 546 | auto *ResourceMD = |
| 547 | (NamedResourceMD != nullptr) ? NamedResourceMD->getOperand(i: 0) : nullptr; |
| 548 | // FIXME: Add support to construct Signatures |
| 549 | // See https://github.com/llvm/llvm-project/issues/57928 |
| 550 | MDTuple *Signatures = nullptr; |
| 551 | |
| 552 | if (MMDI.ShaderProfile == Triple::EnvironmentType::Library) { |
| 553 | // Get the combined shader flag mask of all functions in the library to be |
| 554 | // used as shader flags mask value associated with top-level library entry |
| 555 | // metadata. |
| 556 | uint64_t CombinedMask = ShaderFlags.getCombinedFlags(); |
| 557 | EntryFnMDNodes.emplace_back( |
| 558 | Args: emitTopLevelLibraryNode(M, RMD: ResourceMD, ShaderFlags: CombinedMask)); |
| 559 | } else if (1 < MMDI.EntryPropertyVec.size()) |
| 560 | reportError(M, Message: "Non-library shader: One and only one entry expected" ); |
| 561 | |
| 562 | for (const EntryProperties &EntryProp : MMDI.EntryPropertyVec) { |
| 563 | uint64_t EntryShaderFlags = 0; |
| 564 | if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) { |
| 565 | EntryShaderFlags = ShaderFlags.getFunctionFlags(EntryProp.Entry); |
| 566 | if (EntryProp.ShaderStage != MMDI.ShaderProfile) |
| 567 | reportError( |
| 568 | M, Message: "Shader stage '" + |
| 569 | Twine(getShortShaderStage(Env: EntryProp.ShaderStage)) + |
| 570 | "' for entry '" + Twine(EntryProp.Entry->getName()) + |
| 571 | "' different from specified target profile '" + |
| 572 | Twine(Triple::getEnvironmentTypeName(Kind: MMDI.ShaderProfile) + |
| 573 | "'" )); |
| 574 | } |
| 575 | EntryFnMDNodes.emplace_back(Args: emitEntryMD( |
| 576 | M, EP: EntryProp, Signatures, MDResources: ResourceMD, EntryShaderFlags, MMDI)); |
| 577 | } |
| 578 | |
| 579 | NamedMDNode *EntryPointsNamedMD = |
| 580 | M.getOrInsertNamedMetadata(Name: "dx.entryPoints" ); |
| 581 | for (auto *Entry : EntryFnMDNodes) |
| 582 | EntryPointsNamedMD->addOperand(M: Entry); |
| 583 | |
| 584 | cleanModuleFlags(M); |
| 585 | |
| 586 | // Finally, strip all module metadata that is not explicitly specified in the |
| 587 | // allow-list |
| 588 | SmallVector<NamedMDNode *> ToStrip; |
| 589 | |
| 590 | for (NamedMDNode &NamedMD : M.named_metadata()) |
| 591 | if (!NamedMD.getName().starts_with(Prefix: "llvm.dbg." ) && |
| 592 | !llvm::is_contained(Range&: CompatibleNamedModuleMDs, Element: NamedMD.getName())) |
| 593 | ToStrip.push_back(Elt: &NamedMD); |
| 594 | |
| 595 | for (NamedMDNode *NamedMD : ToStrip) |
| 596 | NamedMD->eraseFromParent(); |
| 597 | } |
| 598 | |
| 599 | PreservedAnalyses DXILTranslateMetadata::run(Module &M, |
| 600 | ModuleAnalysisManager &MAM) { |
| 601 | DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(IR&: M); |
| 602 | DXILResourceTypeMap &DRTM = MAM.getResult<DXILResourceTypeAnalysis>(IR&: M); |
| 603 | const ModuleShaderFlags &ShaderFlags = MAM.getResult<ShaderFlagsAnalysis>(IR&: M); |
| 604 | const dxil::ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(IR&: M); |
| 605 | |
| 606 | translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI); |
| 607 | translateInstructionMetadata(M); |
| 608 | |
| 609 | return PreservedAnalyses::all(); |
| 610 | } |
| 611 | |
| 612 | void DXILTranslateMetadataLegacy::getAnalysisUsage(AnalysisUsage &AU) const { |
| 613 | AU.addRequired<DXILResourceTypeWrapperPass>(); |
| 614 | AU.addRequired<DXILResourceWrapperPass>(); |
| 615 | AU.addRequired<ShaderFlagsAnalysisWrapper>(); |
| 616 | AU.addRequired<DXILMetadataAnalysisWrapperPass>(); |
| 617 | AU.addRequired<RootSignatureAnalysisWrapper>(); |
| 618 | |
| 619 | AU.addPreserved<DXILMetadataAnalysisWrapperPass>(); |
| 620 | AU.addPreserved<DXILResourceBindingWrapperPass>(); |
| 621 | AU.addPreserved<DXILResourceWrapperPass>(); |
| 622 | AU.addPreserved<RootSignatureAnalysisWrapper>(); |
| 623 | AU.addPreserved<ShaderFlagsAnalysisWrapper>(); |
| 624 | } |
| 625 | |
| 626 | bool DXILTranslateMetadataLegacy::runOnModule(Module &M) { |
| 627 | DXILResourceMap &DRM = |
| 628 | getAnalysis<DXILResourceWrapperPass>().getResourceMap(); |
| 629 | DXILResourceTypeMap &DRTM = |
| 630 | getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap(); |
| 631 | const ModuleShaderFlags &ShaderFlags = |
| 632 | getAnalysis<ShaderFlagsAnalysisWrapper>().getShaderFlags(); |
| 633 | dxil::ModuleMetadataInfo MMDI = |
| 634 | getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata(); |
| 635 | |
| 636 | translateGlobalMetadata(M, DRM, DRTM, ShaderFlags, MMDI); |
| 637 | translateInstructionMetadata(M); |
| 638 | return true; |
| 639 | } |
| 640 | |
| 641 | char DXILTranslateMetadataLegacy::ID = 0; |
| 642 | |
| 643 | ModulePass *llvm::createDXILTranslateMetadataLegacyPass() { |
| 644 | return new DXILTranslateMetadataLegacy(); |
| 645 | } |
| 646 | |
| 647 | INITIALIZE_PASS_BEGIN(DXILTranslateMetadataLegacy, "dxil-translate-metadata" , |
| 648 | "DXIL Translate Metadata" , false, false) |
| 649 | INITIALIZE_PASS_DEPENDENCY(DXILResourceWrapperPass) |
| 650 | INITIALIZE_PASS_DEPENDENCY(ShaderFlagsAnalysisWrapper) |
| 651 | INITIALIZE_PASS_DEPENDENCY(RootSignatureAnalysisWrapper) |
| 652 | INITIALIZE_PASS_DEPENDENCY(DXILMetadataAnalysisWrapperPass) |
| 653 | INITIALIZE_PASS_END(DXILTranslateMetadataLegacy, "dxil-translate-metadata" , |
| 654 | "DXIL Translate Metadata" , false, false) |
| 655 | |