[llvm] [DXIL] Consume Metadata Analysis information in passes (PR #108034)

S. Bharadwaj Yadavalli via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 18 14:08:53 PDT 2024


================
@@ -58,25 +94,258 @@ static void emitResourceMetadata(Module &M, const DXILResourceMap &DRM,
   }
 
   if (!HasResources)
-    return;
+    return nullptr;
 
   NamedMDNode *ResourceMD = M.getOrInsertNamedMetadata("dx.resources");
   ResourceMD->addOperand(
       MDNode::get(M.getContext(), {SRVMD, UAVMD, CBufMD, SmpMD}));
+
+  return ResourceMD;
+}
+
+static StringRef getShortShaderStage(Triple::EnvironmentType Env) {
+  switch (Env) {
+  case Triple::Pixel:
+    return "ps";
+  case Triple::Vertex:
+    return "vs";
+  case Triple::Geometry:
+    return "gs";
+  case Triple::Hull:
+    return "hs";
+  case Triple::Domain:
+    return "ds";
+  case Triple::Compute:
+    return "cs";
+  case Triple::Library:
+    return "lib";
+  case Triple::Mesh:
+    return "ms";
+  case Triple::Amplification:
+    return "as";
+  default:
+    break;
+  }
+  llvm_unreachable("Unsupported environment for DXIL generation.");
+}
+
+static uint32_t getShaderStage(Triple::EnvironmentType Env) {
+  return (uint32_t)Env - (uint32_t)llvm::Triple::Pixel;
+}
+
+namespace {
+enum EntryPropsTag {
+  ShaderFlagsTag = 0,
+  GSStateTag,
+  DSStateTag,
+  HSStateTag,
+  NumThreadsTag,
+  AutoBindingSpaceTag,
+  RayPayloadSizeTag,
+  RayAttribSizeTag,
+  ShaderKindTag,
+  MSStateTag,
+  ASStateTag,
+  WaveSizeTag,
+  EntryRootSigTag,
+};
+} // namespace
+
+static SmallVector<Metadata *>
+getTagValueAsMetadata(EntryPropsTag Tag, uint64_t Value, LLVMContext &Ctx) {
+  SmallVector<Metadata *> MDVals;
+  MDVals.emplace_back(
+      ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Ctx), Tag)));
+  switch (Tag) {
+  case ShaderFlagsTag:
+    MDVals.emplace_back(ConstantAsMetadata::get(
+        ConstantInt::get(Type::getInt64Ty(Ctx), Value)));
+    break;
+  case ShaderKindTag:
+    MDVals.emplace_back(ConstantAsMetadata::get(
+        ConstantInt::get(Type::getInt32Ty(Ctx), Value)));
+    break;
+  default:
+    assert(false && "NYI: Unhandled entry property tag");
+  }
+  return MDVals;
+}
+
+static MDTuple *
+getEntryPropAsMetadata(const EntryProperties &EP, uint64_t EntryShaderFlags,
+                       const Triple::EnvironmentType ShaderProfile) {
+  SmallVector<Metadata *> MDVals;
+  LLVMContext &Ctx = EP.Entry->getContext();
+  if (EntryShaderFlags != 0)
+    MDVals.append(getTagValueAsMetadata(ShaderFlagsTag, EntryShaderFlags, Ctx));
+
+  if (EP.Entry != nullptr) {
+    // FIXME: support more props.
+    // See https://github.com/llvm/llvm-project/issues/57948.
+    // Add shader kind for lib entries.
+    if (ShaderProfile == Triple::EnvironmentType::Library &&
+        EP.ShaderStage != Triple::EnvironmentType::Library)
+      MDVals.append(getTagValueAsMetadata(ShaderKindTag,
+                                          getShaderStage(EP.ShaderStage), Ctx));
+
+    if (EP.ShaderStage == Triple::EnvironmentType::Compute) {
+      MDVals.emplace_back(ConstantAsMetadata::get(
+          ConstantInt::get(Type::getInt32Ty(Ctx), NumThreadsTag)));
+      std::vector<Metadata *> NumThreadVals;
+      NumThreadVals.emplace_back(ConstantAsMetadata::get(
+          ConstantInt::get(Type::getInt32Ty(Ctx), EP.NumThreadsX)));
+      NumThreadVals.emplace_back(ConstantAsMetadata::get(
+          ConstantInt::get(Type::getInt32Ty(Ctx), EP.NumThreadsY)));
+      NumThreadVals.emplace_back(ConstantAsMetadata::get(
+          ConstantInt::get(Type::getInt32Ty(Ctx), EP.NumThreadsZ)));
+      MDVals.emplace_back(MDNode::get(Ctx, NumThreadVals));
+    }
+  }
+  if (MDVals.empty())
+    return nullptr;
+  return MDNode::get(Ctx, MDVals);
+}
+
+// Each entry point metadata record specifies:
+//  * reference to the entry point function global symbol
+//  * unmangled name
+//  * list of signatures
+//  * list of resources
+//  * list of tag-value pairs of shader capabilities and other properties
+
+MDTuple *constructEntryMetadata(const Function *EntryFn, MDTuple *Signatures,
+                                MDNode *Resources, MDTuple *Properties,
+                                LLVMContext &Ctx) {
+  Metadata *MDVals[5];
+  MDVals[0] =
+      EntryFn ? ValueAsMetadata::get(const_cast<Function *>(EntryFn)) : nullptr;
+  MDVals[1] = MDString::get(Ctx, EntryFn ? EntryFn->getName() : "");
+  MDVals[2] = Signatures;
+  MDVals[3] = Resources;
+  MDVals[4] = Properties;
+  return MDNode::get(Ctx, MDVals);
+}
+
+static MDTuple *emitEntryMD(const EntryProperties &EP, MDTuple *Signatures,
+                            MDNode *MDResources,
+                            const uint64_t EntryShaderFlags,
+                            const Triple::EnvironmentType ShaderProfile) {
+  MDTuple *Properties =
+      getEntryPropAsMetadata(EP, EntryShaderFlags, ShaderProfile);
+  return constructEntryMetadata(EP.Entry, Signatures, MDResources, Properties,
+                                EP.Entry->getContext());
+}
+
+static void emitValidatorVersionMD(Module &M, const ModuleMetadataInfo &MMDI) {
+  if (!MMDI.ValidatorVersion.empty()) {
+    LLVMContext &Ctx = M.getContext();
+    IRBuilder<> IRB(Ctx);
+    Metadata *MDVals[2];
+    MDVals[0] =
+        ConstantAsMetadata::get(IRB.getInt32(MMDI.ValidatorVersion.getMajor()));
+    MDVals[1] = ConstantAsMetadata::get(
+        IRB.getInt32(MMDI.ValidatorVersion.getMinor().value_or(0)));
+    NamedMDNode *ValVerNode = M.getOrInsertNamedMetadata("dx.valver");
+    // Set validator version obtained from DXIL Metadata Analysis pass
+    ValVerNode->clearOperands();
+    ValVerNode->addOperand(MDNode::get(Ctx, MDVals));
+  }
+}
+
+static void emitShaderModelVersionMD(Module &M,
+                                     const ModuleMetadataInfo &MMDI) {
+  LLVMContext &Ctx = M.getContext();
+  IRBuilder<> IRB(Ctx);
+  Metadata *SMVals[3];
+  VersionTuple SM = MMDI.ShaderModelVersion;
+  SMVals[0] = MDString::get(Ctx, getShortShaderStage(MMDI.ShaderProfile));
+  SMVals[1] = ConstantAsMetadata::get(IRB.getInt32(SM.getMajor()));
+  SMVals[2] = ConstantAsMetadata::get(IRB.getInt32(SM.getMinor().value_or(0)));
+  NamedMDNode *SMMDNode = M.getOrInsertNamedMetadata("dx.shaderModel");
+  SMMDNode->addOperand(MDNode::get(Ctx, SMVals));
+}
+
+static void emitDXILVersionTupleMD(Module &M, const ModuleMetadataInfo &MMDI) {
+  LLVMContext &Ctx = M.getContext();
+  IRBuilder<> IRB(Ctx);
+  VersionTuple DXILVer = MMDI.DXILVersion;
+  Metadata *DXILVals[2];
+  DXILVals[0] = ConstantAsMetadata::get(IRB.getInt32(DXILVer.getMajor()));
+  DXILVals[1] =
+      ConstantAsMetadata::get(IRB.getInt32(DXILVer.getMinor().value_or(0)));
+  NamedMDNode *DXILVerMDNode = M.getOrInsertNamedMetadata("dx.version");
+  DXILVerMDNode->addOperand(MDNode::get(Ctx, DXILVals));
+}
+
+static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD,
+                                        uint64_t ShaderFlags) {
+  LLVMContext &Ctx = M.getContext();
+  MDTuple *Properties = nullptr;
+  if (ShaderFlags != 0) {
+    SmallVector<Metadata *> MDVals;
+    // FIXME: ShaderFlagsAnalysis pass needs to collect and provide
+    // ShaderFlags for each entry function. Currently, ShaderFlags value
+    // provided by ShaderFlagsAnalysis pass is created by walking *all* the
+    // function instructions of the module. Is it is correct to use this value
+    // for metadata of the empty library entry?
+    MDVals.append(getTagValueAsMetadata(ShaderFlagsTag, ShaderFlags, Ctx));
+    Properties = MDNode::get(Ctx, MDVals);
+  }
+  // Library has an entry metadata with resource table metadata and all other
+  // MDNodes as null.
+  return constructEntryMetadata(nullptr, nullptr, RMD, Properties, Ctx);
 }
 
 static void translateMetadata(Module &M, const DXILResourceMap &DRM,
-                              const dxil::Resources &MDResources,
-                              const ComputedShaderFlags &ShaderFlags) {
-  dxil::ValidatorVersionMD ValVerMD(M);
-  if (ValVerMD.isEmpty())
-    ValVerMD.update(VersionTuple(1, 0));
-  dxil::createShaderModelMD(M);
-  dxil::createDXILVersionMD(M);
+                              const Resources &MDResources,
+                              const ComputedShaderFlags &ShaderFlags,
+                              const ModuleMetadataInfo &MMDI) {
+  LLVMContext &Ctx = M.getContext();
+  IRBuilder<> IRB(Ctx);
+  SmallVector<MDNode *> EntryFnMDNodes;
+
+  emitValidatorVersionMD(M, MMDI);
+  emitShaderModelVersionMD(M, MMDI);
+  emitDXILVersionTupleMD(M, MMDI);
+  NamedMDNode *NamedResourceMD = emitResourceMetadata(M, DRM, MDResources);
+  auto *ResourceMD =
+      (NamedResourceMD != nullptr) ? NamedResourceMD->getOperand(0) : nullptr;
+  // FIXME: Add support to construct Signatures
+  // See https://github.com/llvm/llvm-project/issues/57928
+  MDTuple *Signatures = nullptr;
 
-  emitResourceMetadata(M, DRM, MDResources);
+  if (MMDI.ShaderProfile == Triple::EnvironmentType::Library)
+    EntryFnMDNodes.emplace_back(
+        emitTopLevelLibraryNode(M, ResourceMD, ShaderFlags));
+  else if (MMDI.EntryPropertyVec.size() > 1) {
+    M.getContext().diagnose(DiagnosticInfoModuleFormat(
+        M, "Non-library shader: One and only one entry expected"));
+  }
+
+  for (const EntryProperties &EntryProp : MMDI.EntryPropertyVec) {
+    // FIXME: ShaderFlagsAnalysis pass needs to collect and provide
+    // ShaderFlags for each entry function. For now, assume shader flags value
+    // of entry functions being compiled for lib_* shader profile viz.,
+    // EntryPro.Entry is 0.
+    uint64_t EntryShaderFlags =
+        (MMDI.ShaderProfile == Triple::EnvironmentType::Library) ? 0
+                                                                 : ShaderFlags;
+    if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) {
+      if (EntryProp.ShaderStage != MMDI.ShaderProfile) {
+        M.getContext().diagnose(DiagnosticInfoModuleFormat(
+            M, "Non-library shader: Stage of Shader entry different from "
----------------
bharadwajy wrote:

> > "Non-library shader: Stage of shader entry different from shader target profile"
> 
> So, I think: if this diagnostic is emitted for IR generated by Clang then this is a bug in Clang. Does that sound right? The only way to really hit this is for some other frontend to do the wrong thing?
> 
> Given that the case we can probably frame the diagnostic using llvm terms like "environment" and "triple" if that helps at all?

How about - 
```
Shader stage '<entry-shader-stage>' of entry '<entry>' different from specified target profile '<target-shader-profile>'
```
 where the exact mismatching component is spelt out? An instance of such a message would look like  

```
Shader stage 'cs' of entry 'cs_entry' different from specified target profile 'pixel'
```



https://github.com/llvm/llvm-project/pull/108034


More information about the llvm-commits mailing list