Skip to content

[DirectX] Infrastructure to collect shader flags for each function #112967

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Nov 25, 2024

Conversation

bharadwajy
Copy link
Contributor

@bharadwajy bharadwajy commented Oct 18, 2024

Currently, ShaderFlagsAnalysis pass represents various module-level properties as well as function-level properties of a DXIL Module using a single mask. However, one mask per function is needed for accurate computation of shader flags mask, such as for entry function metadata creation.

This change introduces a structure that wraps a sorted vector of function-shader flag mask pairs that represent function properties instead of a single shader flag mask that represents module properties and properties of all functions. The result type of ShaderFlagsAnalysis pass is changed to newly-defined structure type instead of a single shader flags mask.

This allows accurate computation of shader flags of an entry function (and all functions in a library shader) for use during its metadata generation (DXILTranslateMetadata pass) and its feature flags in DX container globals construction (DXContainerGlobals pass) based on the shader flags mask of functions. However, note that the change to implement propagation of such callee-based shader flags mask computation is planned in a follow-on PR. Consequently, this PR changes shader flag mask computation in DXILTranslateMetadata and DXContainerGlobals passes to simply be a union of module flags and shader flags of all functions, thereby retaining the existing effect of using a single shader flag mask.

@llvmbot
Copy link
Member

llvmbot commented Oct 18, 2024

@llvm/pr-subscribers-backend-directx

Author: S. Bharadwaj Yadavalli (bharadwajy)

Changes

Currently, ShaderFlagsAnalysis pass represents various module-level properties as well as function-level properties of a DXIL Module using a single mask. However, separate flags to represent module-level properties and function-level properties are needed for accurate computation of shader flags mask, such as for entry function metadata creation.

This change introduces a structure that allows separate representation of

(a) shader flag mask to represent module properties
(b) a map of function to shader flag mask that represent function properties

instead of a single shader flag mask that represents module properties and properties of all function. The result type of ShaderFlagsAnalysis pass is changed to newly-defined structure type instead of a single shader flags mask.

This seperation allows accurate computation of shader flags of an entry function for use during its metadata generation (DXILTranslateMetadata pass) and its feature flags in DX container globals construction (DXContainerGlobals pass) based on the shader flags mask of functions called in entry function. However, note that the change to implement such callee-based shader flags mask computation is planned in a follow-on PR. Consequently, this PR changes shader flag mask computation in DXILTranslateMetadata and DXContainerGlobals passes to simply be a union of module flags and shader flags of all functions, thereby retaining the existing effect of using a single shader flag mask.


Full diff: https://github.com/llvm/llvm-project/pull/112967.diff

4 Files Affected:

  • (modified) llvm/lib/Target/DirectX/DXContainerGlobals.cpp (+10-5)
  • (modified) llvm/lib/Target/DirectX/DXILShaderFlags.cpp (+32-14)
  • (modified) llvm/lib/Target/DirectX/DXILShaderFlags.h (+17-9)
  • (modified) llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp (+28-18)
diff --git a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
index 2c11373504e8c7..c7202cc04c26dc 100644
--- a/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
+++ b/llvm/lib/Target/DirectX/DXContainerGlobals.cpp
@@ -78,13 +78,18 @@ bool DXContainerGlobals::runOnModule(Module &M) {
 }
 
 GlobalVariable *DXContainerGlobals::getFeatureFlags(Module &M) {
-  const uint64_t FeatureFlags =
-      static_cast<uint64_t>(getAnalysis<ShaderFlagsAnalysisWrapper>()
-                                .getShaderFlags()
-                                .getFeatureFlags());
+  const DXILModuleShaderFlagsInfo &MSFI =
+      getAnalysis<ShaderFlagsAnalysisWrapper>().getShaderFlags();
+  // TODO: Feature flags mask is obtained as a collection of feature flags
+  // of the shader flags of all functions in the module. Need to verify
+  // and modify the computation of feature flags to be used.
+  uint64_t ConsolidatedFeatureFlags = 0;
+  for (const auto &FuncFlags : MSFI.FuncShaderFlagsMap) {
+    ConsolidatedFeatureFlags |= FuncFlags.second.getFeatureFlags();
+  }
 
   Constant *FeatureFlagsConstant =
-      ConstantInt::get(M.getContext(), APInt(64, FeatureFlags));
+      ConstantInt::get(M.getContext(), APInt(64, ConsolidatedFeatureFlags));
   return buildContainerGlobal(M, FeatureFlagsConstant, "dx.sfi0", "SFI0");
 }
 
diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
index 9fa137b4c025e1..8c590862008862 100644
--- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
+++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
@@ -20,33 +20,41 @@
 using namespace llvm;
 using namespace llvm::dxil;
 
-static void updateFlags(ComputedShaderFlags &Flags, const Instruction &I) {
+static void updateFlags(DXILModuleShaderFlagsInfo &MSFI, const Instruction &I) {
+  ComputedShaderFlags &FSF = MSFI.FuncShaderFlagsMap[I.getFunction()];
   Type *Ty = I.getType();
   if (Ty->isDoubleTy()) {
-    Flags.Doubles = true;
+    FSF.Doubles = true;
     switch (I.getOpcode()) {
     case Instruction::FDiv:
     case Instruction::UIToFP:
     case Instruction::SIToFP:
     case Instruction::FPToUI:
     case Instruction::FPToSI:
-      Flags.DX11_1_DoubleExtensions = true;
+      FSF.DX11_1_DoubleExtensions = true;
       break;
     }
   }
 }
 
-ComputedShaderFlags ComputedShaderFlags::computeFlags(Module &M) {
-  ComputedShaderFlags Flags;
-  for (const auto &F : M)
+static DXILModuleShaderFlagsInfo computeFlags(Module &M) {
+  DXILModuleShaderFlagsInfo MSFI;
+  for (const auto &F : M) {
+    if (F.isDeclaration())
+      continue;
+    if (!MSFI.FuncShaderFlagsMap.contains(&F)) {
+      ComputedShaderFlags CSF{};
+      MSFI.FuncShaderFlagsMap[&F] = CSF;
+    }
     for (const auto &BB : F)
       for (const auto &I : BB)
-        updateFlags(Flags, I);
-  return Flags;
+        updateFlags(MSFI, I);
+  }
+  return MSFI;
 }
 
 void ComputedShaderFlags::print(raw_ostream &OS) const {
-  uint64_t FlagVal = (uint64_t) * this;
+  uint64_t FlagVal = (uint64_t)*this;
   OS << formatv("; Shader Flags Value: {0:x8}\n;\n", FlagVal);
   if (FlagVal == 0)
     return;
@@ -65,15 +73,25 @@ void ComputedShaderFlags::print(raw_ostream &OS) const {
 
 AnalysisKey ShaderFlagsAnalysis::Key;
 
-ComputedShaderFlags ShaderFlagsAnalysis::run(Module &M,
-                                             ModuleAnalysisManager &AM) {
-  return ComputedShaderFlags::computeFlags(M);
+DXILModuleShaderFlagsInfo ShaderFlagsAnalysis::run(Module &M,
+                                                   ModuleAnalysisManager &AM) {
+  return computeFlags(M);
+}
+
+bool ShaderFlagsAnalysisWrapper::runOnModule(Module &M) {
+  MSFI = computeFlags(M);
+  return false;
 }
 
 PreservedAnalyses ShaderFlagsAnalysisPrinter::run(Module &M,
                                                   ModuleAnalysisManager &AM) {
-  ComputedShaderFlags Flags = AM.getResult<ShaderFlagsAnalysis>(M);
-  Flags.print(OS);
+  DXILModuleShaderFlagsInfo Flags = AM.getResult<ShaderFlagsAnalysis>(M);
+  OS << "; Shader Flags mask for Module:\n";
+  Flags.ModuleFlags.print(OS);
+  for (auto SF : Flags.FuncShaderFlagsMap) {
+    OS << "; Shader Flags mash for Function: " << SF.first->getName() << "\n";
+    SF.second.print(OS);
+  }
   return PreservedAnalyses::all();
 }
 
diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.h b/llvm/lib/Target/DirectX/DXILShaderFlags.h
index 1df7d27de13d3c..6f81ff74384d0c 100644
--- a/llvm/lib/Target/DirectX/DXILShaderFlags.h
+++ b/llvm/lib/Target/DirectX/DXILShaderFlags.h
@@ -14,6 +14,8 @@
 #ifndef LLVM_TARGET_DIRECTX_DXILSHADERFLAGS_H
 #define LLVM_TARGET_DIRECTX_DXILSHADERFLAGS_H
 
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/IR/Function.h"
 #include "llvm/IR/PassManager.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/Compiler.h"
@@ -60,11 +62,20 @@ struct ComputedShaderFlags {
     return FeatureFlags;
   }
 
-  static ComputedShaderFlags computeFlags(Module &M);
   void print(raw_ostream &OS = dbgs()) const;
   LLVM_DUMP_METHOD void dump() const { print(); }
 };
 
+using FunctionShaderFlagsMap =
+    SmallDenseMap<Function const *, ComputedShaderFlags>;
+struct DXILModuleShaderFlagsInfo {
+  // Shader Flag mask representing module-level properties
+  ComputedShaderFlags ModuleFlags;
+  // Map representing shader flag mask representing properties of each of the
+  // functions in the module
+  FunctionShaderFlagsMap FuncShaderFlagsMap;
+};
+
 class ShaderFlagsAnalysis : public AnalysisInfoMixin<ShaderFlagsAnalysis> {
   friend AnalysisInfoMixin<ShaderFlagsAnalysis>;
   static AnalysisKey Key;
@@ -72,9 +83,9 @@ class ShaderFlagsAnalysis : public AnalysisInfoMixin<ShaderFlagsAnalysis> {
 public:
   ShaderFlagsAnalysis() = default;
 
-  using Result = ComputedShaderFlags;
+  using Result = DXILModuleShaderFlagsInfo;
 
-  ComputedShaderFlags run(Module &M, ModuleAnalysisManager &AM);
+  DXILModuleShaderFlagsInfo run(Module &M, ModuleAnalysisManager &AM);
 };
 
 /// Printer pass for ShaderFlagsAnalysis results.
@@ -92,19 +103,16 @@ class ShaderFlagsAnalysisPrinter
 /// This is required because the passes that will depend on this are codegen
 /// passes which run through the legacy pass manager.
 class ShaderFlagsAnalysisWrapper : public ModulePass {
-  ComputedShaderFlags Flags;
+  DXILModuleShaderFlagsInfo MSFI;
 
 public:
   static char ID;
 
   ShaderFlagsAnalysisWrapper() : ModulePass(ID) {}
 
-  const ComputedShaderFlags &getShaderFlags() { return Flags; }
+  const DXILModuleShaderFlagsInfo &getShaderFlags() { return MSFI; }
 
-  bool runOnModule(Module &M) override {
-    Flags = ComputedShaderFlags::computeFlags(M);
-    return false;
-  }
+  bool runOnModule(Module &M) override;
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
diff --git a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
index be370e10df6943..2da4fe83a066c2 100644
--- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
+++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
@@ -286,11 +286,6 @@ static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD,
   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(EntryPropsTag::ShaderFlags, ShaderFlags, Ctx));
     Properties = MDNode::get(Ctx, MDVals);
@@ -302,7 +297,7 @@ static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD,
 
 static void translateMetadata(Module &M, const DXILResourceMap &DRM,
                               const Resources &MDResources,
-                              const ComputedShaderFlags &ShaderFlags,
+                              const DXILModuleShaderFlagsInfo &ShaderFlags,
                               const ModuleMetadataInfo &MMDI) {
   LLVMContext &Ctx = M.getContext();
   IRBuilder<> IRB(Ctx);
@@ -318,22 +313,37 @@ static void translateMetadata(Module &M, const DXILResourceMap &DRM,
   // See https://github.com/llvm/llvm-project/issues/57928
   MDTuple *Signatures = nullptr;
 
-  if (MMDI.ShaderProfile == Triple::EnvironmentType::Library)
+  if (MMDI.ShaderProfile == Triple::EnvironmentType::Library) {
+    // Create a consolidated shader flag mask of all functions in the library
+    // to be used as shader flags mask value associated with top-level library
+    // entry metadata.
+    uint64_t ConsolidatedMask = ShaderFlags.ModuleFlags;
+    for (const auto &FunFlags : ShaderFlags.FuncShaderFlagsMap) {
+      ConsolidatedMask |= FunFlags.second;
+    }
     EntryFnMDNodes.emplace_back(
-        emitTopLevelLibraryNode(M, ResourceMD, ShaderFlags));
-  else if (MMDI.EntryPropertyVec.size() > 1) {
+        emitTopLevelLibraryNode(M, ResourceMD, ConsolidatedMask));
+  } else if (MMDI.EntryPropertyVec.size() > 1) {
     M.getContext().diagnose(DiagnosticInfoTranslateMD(
         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;
+    auto FSFIt = ShaderFlags.FuncShaderFlagsMap.find(EntryProp.Entry);
+    if (FSFIt == ShaderFlags.FuncShaderFlagsMap.end()) {
+      M.getContext().diagnose(DiagnosticInfoTranslateMD(
+          M, "Shader Flags of Function '" + Twine(EntryProp.Entry->getName()) +
+                 "' not found"));
+    }
+    // If ShaderProfile is Library, mask is already consolidated in the
+    // top-level library node. Hence it is not emitted.
+    uint64_t EntryShaderFlags = 0;
+    if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) {
+      // TODO: Create a consolidated shader flag mask of all the entry
+      // functions and its callees. The following is correct only if
+      // (*FSIt).first has no call instructions.
+      EntryShaderFlags = (*FSFIt).second | ShaderFlags.ModuleFlags;
+    }
     if (MMDI.ShaderProfile != Triple::EnvironmentType::Library) {
       if (EntryProp.ShaderStage != MMDI.ShaderProfile) {
         M.getContext().diagnose(DiagnosticInfoTranslateMD(
@@ -361,7 +371,7 @@ PreservedAnalyses DXILTranslateMetadata::run(Module &M,
                                              ModuleAnalysisManager &MAM) {
   const DXILResourceMap &DRM = MAM.getResult<DXILResourceAnalysis>(M);
   const dxil::Resources &MDResources = MAM.getResult<DXILResourceMDAnalysis>(M);
-  const ComputedShaderFlags &ShaderFlags =
+  const DXILModuleShaderFlagsInfo &ShaderFlags =
       MAM.getResult<ShaderFlagsAnalysis>(M);
   const dxil::ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(M);
 
@@ -393,7 +403,7 @@ class DXILTranslateMetadataLegacy : public ModulePass {
         getAnalysis<DXILResourceWrapperPass>().getResourceMap();
     const dxil::Resources &MDResources =
         getAnalysis<DXILResourceMDWrapper>().getDXILResource();
-    const ComputedShaderFlags &ShaderFlags =
+    const DXILModuleShaderFlagsInfo &ShaderFlags =
         getAnalysis<ShaderFlagsAnalysisWrapper>().getShaderFlags();
     dxil::ModuleMetadataInfo MMDI =
         getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();

Copy link

github-actions bot commented Oct 18, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Currently, ShaderFlagsAnalysis pass represents various module-level
properties as well as function-level properties of a DXIL Module
using a single mask. However, separate flags to represent module-level
properties and function-level properties are needed for accurate computation
of shader flags mask, such as for entry function metadata creation.

This change introduces a structure that allows separate representation of

(a) shader flag mask to represent module properties
(b) a map of function to shader flag mask that represent function properties

instead of a single shader flag mask that represents module properties
and properties of all function. The result type of ShaderFlagsAnalysis
pass is changed to newly-defined structure type instead of a single shader
flags mask.

This seperation allows accurate computation of shader flags of an entry
function for use during its metadata generation (DXILTranslateMetadata pass)
and its feature flags in DX container globals construction (DXContainerGlobals
pass) based on the shader flags mask of functions called in entry function.
However, note that the change to implement such callee-based shader flags mask
computation is planned in a follow-on PR. Consequently, this PR changes shader
flag mask computation in DXILTranslateMetadata and DXContainerGlobals passes
to simply be a union of module flags and shader flags of all functions, thereby
retaining the existing effect of using a single shader flag mask.
@bharadwajy bharadwajy force-pushed the shader-flags/func-level-collection branch from cdfa0b5 to 397f70b Compare October 21, 2024 16:52
@bogner
Copy link
Contributor

bogner commented Oct 22, 2024

We should be able to write tests that the shader flags are correct per-function by running the analysis on some IR and printing the results, which we can then check.

pointers and corresponding shader flag masks. This follows the
recommendations in LLVM Programmer's Manual as the current usage
pattern has distinct phases of insertion of computed shader flags
followed by querying. Upon insertion, the Smallvector is sorted and
binary search is used for querying. Necessary comparison function of
pairs is also implemented.

Added a simple DiagnosticInfoShaderFlags for emitting diagnostics.

Added tests to verify shader flags masks collected per-function.
@bharadwajy bharadwajy changed the title [NFC][DirectX] Infrastructure to collect shader flags for each function [DirectX] Infrastructure to collect shader flags for each function Oct 28, 2024
@bharadwajy
Copy link
Contributor Author

We should be able to write tests that the shader flags are correct per-function by running the analysis on some IR and printing the results, which we can then check.

Added per-function test.

@bharadwajy bharadwajy requested a review from damyanp October 28, 2024 15:36
Copy link
Contributor

@damyanp damyanp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stopped reading this after seeing the string construction in the comparison function. I'm either missing something big about this change, which means I'd need to review again once I've been corrected, or this PR is going to be somewhat different anyway, so I'll wait until that's resolved before looking at it again.

@bharadwajy
Copy link
Contributor Author

I stopped reading this after seeing the string construction in the comparison function. I'm either missing something big about this change, which means I'd need to review again once I've been corrected, or this PR is going to be somewhat different anyway, so I'll wait until that's resolved before looking at it again.

Lexicographic sort of functions by their signatures seemed the appropriate. I looked at using FunctionComparator::compareSignature() but did not see its usage in the sources other than in tests, so wasn't sure if its usage is encouraged or not.

What are the concerns?

@damyanp
Copy link
Contributor

damyanp commented Oct 28, 2024

What are the concerns?

Have a think about how many strings will be built, and how many memory allocations there'll be, every time sort or lower_bound needs to compare two elements of the vector.

…nature

Non-empty Function names are unique in LLVM IR.

Update the expected test output accordingly
@bharadwajy
Copy link
Contributor Author

What are the concerns?

Have a think about how many strings will be built, and how many memory allocations there'll be, every time sort or lower_bound needs to compare two elements of the vector.

Changed to compare functions by their names instead of constructing a pseudo-signature. Non-empty Function names are unique in LLVM IR.

@bharadwajy bharadwajy requested a review from damyanp October 28, 2024 20:49
…unsorted vector

Other changes based on latest PR feedback
@bharadwajy bharadwajy requested a review from damyanp October 29, 2024 02:10
Comment on lines 14 to 18
; DXC-NEXT: Doubles: true
; DXC-NOT: {{[A-Za-z]+: +true}}
; DXC: DX11_1_DoubleExtensions: true
; DXC-NOT: {{[A-Za-z]+: +true}}
; DXC: NextUnusedBit: false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this print that we need this awkward CHECK-NOT of anything that's specifically "true" rather than just a CHECK-NEXT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The portion of the output being checked is as follows:

...
 - Name:            SFI0
    Size:            8
    Flags:
      Doubles:         true
      ComputeShadersPlusRawAndStructuredBuffers: false
      UAVsAtEveryStage: false
      Max64UAVs:       false
      MinimumPrecision: false
      DX11_1_DoubleExtensions: true
      DX11_1_ShaderExtensions: false
      LEVEL9ComparisonFiltering: false
      TiledResources:  false
      StencilRef:      false
      InnerCoverage:   false
      TypedUAVLoadAdditionalFormats: false
      ROVs:            false
      ViewportAndRTArrayIndexFromAnyShaderFeedingRasterizer: false
      WaveOps:         false
      Int64Ops:        false
      ViewID:          false
      Barycentrics:    false
      NativeLowPrecision: false
      ShadingRate:     false
      Raytracing_Tier_1_1: false
      SamplerFeedback: false
      AtomicInt64OnTypedResource: false
      AtomicInt64OnGroupShared: false
      DerivativesInMeshAndAmpShaders: false
      ResourceDescriptorHeapIndexing: false
      SamplerDescriptorHeapIndexing: false
      RESERVED:        false
      AtomicInt64OnHeapResource: false
      AdvancedTextureOps: false
      WriteableMSAATextures: false
      NextUnusedBit:   false
...

This test and the CHECK-NOT line in question (and all others) that already exist(s) appear to check for flags to be not true with only Doubles and DX11_1_DoubleExtensions that are expected to be true.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem like the place to check that all of the other flags are false. Just doing the two checks should be sufficient:

; CHECK: Doubles: true
; CHECK: DX11_1_DoubleExtensions: true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem like the place to check that all of the other flags are false. Just doing the two checks should be sufficient:

; CHECK: Doubles: true
; CHECK: DX11_1_DoubleExtensions: true

Changes made to limited checking to the two flags.

- Use CSF.Doubles directly
- Remove type-aliasies FuncShaderFlags*
- Make ModuleFlags and FunctionFlags private
- Delete DXILModuleShaderFlagsInfo::print()
- Delete check prefix DXC from test with a single run
- Get rid of compare functions
- Change order of expected output accordingly in double-extensions.ll
- Add extra comments for clarification
- Add back DiagnosticInfoShaderFlags
- Additional error checks
@bharadwajy bharadwajy requested a review from bogner October 31, 2024 23:32
@bharadwajy bharadwajy requested a review from damyanp November 4, 2024 18:12
Move the functionality of static void updateFlags(...) to private
method void DXILModuleShaderFlagsInfo::updateFuctionFlags(...) and
that of static DXILModuleShaderFlagsInfo computeFlags(const Module &M)
to public method bool DXILModuleShaderFlagsInfo::initialize(const Module &M).
@bharadwajy bharadwajy requested a review from damyanp November 6, 2024 19:51
Copy link
Contributor

@damyanp damyanp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is looking good. I think my remaining opens are really nits.

We should look for someone more expert in this area to approve though.

Delete DXILModuleShaderFlagsInfo::ModuleFlags and track module flags
in shader flags mask of each function.

Add private field DXILModuleShaderFlagsinfo::CombinedSFMask to
represent combined shader flags masks of all functions. Update the
value as it is computed per function.

Change DXILModuleShaderFlagsInfo::initialize(Module&) to constructor
@bharadwajy bharadwajy requested a review from damyanp November 15, 2024 21:54
@@ -120,16 +127,17 @@ class ShaderFlagsAnalysisPrinter
/// This is required because the passes that will depend on this are codegen
/// passes which run through the legacy pass manager.
class ShaderFlagsAnalysisWrapper : public ModulePass {
DXILModuleShaderFlagsInfo MSFI;
std::unique_ptr<DXILModuleShaderFlagsInfo> MSFI;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of interest, did you consider std::optional for this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

I also question if we need the optional. Why was the old pattern of the uninitialized flag structure a problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

I also question if we need the optional. Why was the old pattern of the uninitialized flag structure a problem?

Reverted to old pattern; changed to use initialize() instead of a constructor to avoid dynamic memory allocation about which concerns were expressed.

Copy link
Contributor

@damyanp damyanp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but would look for a review from someone with more domain knowledge than I have.

@damyanp damyanp self-requested a review November 15, 2024 23:24
@bharadwajy bharadwajy force-pushed the shader-flags/func-level-collection branch from be34fb3 to c6b3390 Compare November 18, 2024 13:59
uint64_t getModuleFlags() const {
uint64_t ModuleFlags = 0;
#define DXIL_MODULE_FLAG(DxilModuleBit, FlagName, Str) \
ModuleFlags |= FlagName ? getMask(DxilModuleBit) : 0ull;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this exact expansion inside the operator uint64_t definition above. Should we maybe refactor this so that one uses the other instead of duplicating the expansion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this exact expansion inside the operator uint64_t definition above. Should we maybe refactor this so that one uses the other instead of duplicating the expansion?

Refactored.

@@ -120,16 +127,17 @@ class ShaderFlagsAnalysisPrinter
/// This is required because the passes that will depend on this are codegen
/// passes which run through the legacy pass manager.
class ShaderFlagsAnalysisWrapper : public ModulePass {
DXILModuleShaderFlagsInfo MSFI;
std::unique_ptr<DXILModuleShaderFlagsInfo> MSFI;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

I also question if we need the optional. Why was the old pattern of the uninitialized flag structure a problem?

void print(raw_ostream &OS = dbgs()) const;
LLVM_DUMP_METHOD void dump() const { print(); }
};

struct DXILModuleShaderFlagsInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This class's full name is llvm::dxil::DXILModuleShaderFlagsInfo, that's a bit of a mouthful. I'm not really sure how much benefit we get from prefixing it with DXIL and suffixing it with Info.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This class's full name is llvm::dxil::DXILModuleShaderFlagsInfo, that's a bit of a mouthful. I'm not really sure how much benefit we get from prefixing it with DXIL and suffixing it with Info.

Changed to ModuleShaderFlags

.getFeatureFlags());
// TODO: Feature flags mask is obtained as a collection of feature flags
// of the shader flags of all functions in the module. Need to verify
// and modify the computation of feature flags to be used.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have an issue tracking this?

/// for Shader Flags Analysis pass
class DiagnosticInfoShaderFlags : public DiagnosticInfo {
private:
const Twine &Msg;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get really nervous when someone stores a Twine. This effectively means that you must create and destroy this object in a single expression, otherwise the Twine or its attached arguments can go out of scope and you have a memory error.

It seems to me like what you really need is an adapter that converts an llvm::Error to a DiagnosticInfo, so that you can just pass the Error object right through.

We should add a utility to llvm/Support/Error to facilitate that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted diagnostic. Error reporting simplified to use report_fatal_error() as the anticipated error conditions are not expected to be triggered during analysis of a well-formed module.

Comment on lines 120 to 123
if (Iter == FunctionFlags.end() || Iter->first != Func) {
return createStringError("Shader Flags information of Function '" +
Func->getName() + "' not found");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: https://llvm.org/docs/CodingStandards.html#don-t-use-braces-on-simple-single-statement-bodies-of-if-else-loop-statements

Suggested change
if (Iter == FunctionFlags.end() || Iter->first != Func) {
return createStringError("Shader Flags information of Function '" +
Func->getName() + "' not found");
}
if (Iter == FunctionFlags.end() || Iter->first != Func)
return createStringError("Shader Flags information of Function '" +
Func->getName() + "' not found");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to have this fail is if we've invalidated the analysis (and failed to tell the pass manager) or we're trying to use it wrong. This should just be an assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

PreservedAnalyses ShaderFlagsAnalysisPrinter::run(Module &M,
ModuleAnalysisManager &AM) {
ComputedShaderFlags Flags = AM.getResult<ShaderFlagsAnalysis>(M);
Flags.print(OS);
DXILModuleShaderFlagsInfo FlagsInfo = AM.getResult<ShaderFlagsAnalysis>(M);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could store the const & to avoid the copy here.

Comment on lines 147 to 150
if (Error E = SFMask.takeError()) {
M.getContext().diagnose(
DiagnosticInfoShaderFlags(M, toString(std::move(E))));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done through a call to llvm::handleAllErrors. Something like:

Suggested change
if (Error E = SFMask.takeError()) {
M.getContext().diagnose(
DiagnosticInfoShaderFlags(M, toString(std::move(E))));
}
if (!SFMask)
return handleAllErrors(std::move(E),
[&](std::unique_ptr<ErrorInfoBase> EIB) -> Error {
M.getContext().diagnose(errorToDiagnosticInfo(EIB);
return Error::success();
});

This handles arrays of errors so that your function can return more than one error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done through a call to llvm::handleAllErrors. Something like:

This handles arrays of errors so that your function can return more than one error.

Deleted this error-handling code as a result of the assertion added in getShaderFlagsmask().

for (const auto &F : M.getFunctionList()) {
if (F.isDeclaration())
continue;
ComputedShaderFlags CSF{};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: ComputedShaderFlags has a default constructor to zero itself out, the empty initializer list is unnecessary.

Suggested change
ComputedShaderFlags CSF{};
ComputedShaderFlags CSF;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: ComputedShaderFlags has a default constructor to zero itself out, the empty initializer list is unnecessary.

Changed.

Comment on lines 332 to 335
if (Error E = EntrySFMask.takeError()) {
M.getContext().diagnose(
DiagnosticInfoTranslateMD(M, toString(std::move(E))));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be a handleAllErrors call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also be a handleAllErrors call.

Deleted this error-handling code as a result of the assertion added in getShaderFlagsmask().

Copy link
Collaborator

@llvm-beanz llvm-beanz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I'm also just noticing. It doesn't look like any of the tests here actually trigger the errors. Should these even be errors? Should they instead be asserts?

Copy link
Contributor

@bogner bogner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking pretty close. As both Damyan and Chris pointed out, this isn't the appropriate kind of error handling here - the only way to hit the error is to break an invariant, which means we don't need user facing errors at all. A few other comments below.

Comment on lines 118 to 119
std::pair<Function const *, ComputedShaderFlags> V{Func, {}};
const auto Iter = llvm::lower_bound(FunctionFlags, V);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably simpler to use a comparator function that just compares against the pair's first rather than constructing a sentinel to compare against.

Also llvm style prefers const Function * to Function const *

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use comparator.

Comment on lines 120 to 123
if (Iter == FunctionFlags.end() || Iter->first != Func) {
return createStringError("Shader Flags information of Function '" +
Func->getName() + "' not found");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only way to have this fail is if we've invalidated the analysis (and failed to tell the pass manager) or we're trying to use it wrong. This should just be an assert.

Comment on lines 88 to 90
Expected<const ComputedShaderFlags &>
getShaderFlagsMask(const Function *) const;
const ComputedShaderFlags getCombinedFlags() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things here:

  1. getShaderFlagsMask should just return a const ComputedShaderFlags &, not an expected. The only way for this to fail if the analysis is in a valid state is if we call this with a function that isn't in the module somehow.
  2. getCombinedFlags should also return a reference - no need for a copy here.
  3. These names are inconsistent (FlagsMask vs Flags) even though they return the same type of thing. Maybe we should simplify a little and call them getFunctionFlags and getCombinedFlags

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes made per suggestions.

Comment on lines 93 to 95
/// Vector of Function-Shader Flag mask pairs representing properties of each
/// of the functions in the module. Shader Flags of each function represent
/// both module-level and function-level flags
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that this is a sorted vector.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that this is a sorted vector.

Updated comment accordingly.

Comment on lines 50 to 51
void DXILModuleShaderFlagsInfo::updateFunctionFlags(ComputedShaderFlags &CSF,
const Instruction &I) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a member function of DXILModuleShaderFlagsInfo and not just a static function? It doesn't use or change any state from the object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a member function of DXILModuleShaderFlagsInfo and not just a static function? It doesn't use or change any state from the object.

Changed it to a static function.

Comment on lines 16 to 19
; CHECK-NEXT: ; Note: shader requires additional functionality:
; CHECK-NEXT: ; Double-precision floating point
; CHECK-NEXT: ; Double-precision extensions for 11.1
; CHECK-NEXT: ; Note: extra DXIL module flags:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit ridiculous to print these notes after every function. Can't that be printed only for the module flags?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit ridiculous to print these notes after every function. Can't that be printed only for the module flags?

Changes made.

Delete DiagnosticInfoShaderFlags and flag failure to find shader flags mask
of a function as fatal error as it is not expected to be the case
for well-formed modules.

Restore ModuleFlags::initialize inplace of constructor

Rename DXILModuleShaderFlagsInfo as ModuleShaderFlags

Use getModuleFlags() in operator uint_64()
Print Module flags notes only once.

Delete extraneous checks in test

Declare updateFunctionFlags as static

Use comparator instead of constructing a fuction-mask pair to
search in getFunctionFlags()
Comment on lines 14 to 16
; CHECK-NEXT: Doubles: true
; CHECK-NOT: {{[A-Za-z]+: +true}}
; CHECK: DX11_1_DoubleExtensions: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
; CHECK-NEXT: Doubles: true
; CHECK-NOT: {{[A-Za-z]+: +true}}
; CHECK: DX11_1_DoubleExtensions: true
; CHECK: Doubles: true
; CHECK: DX11_1_DoubleExtensions: true

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

@@ -25,6 +25,7 @@
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include "llvm/Support/Error.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused include

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused include

Deleted.

Comment on lines 13 to 22
; CHECK-NEXT: ; Shader Flags for Module Functions
; CHECK-NEXT: ; Function test_fdiv_double : 0x00000044
; CHECK-NEXT: ;
; CHECK-NEXT: ; Function test_uitofp_i64 : 0x00000044
; CHECK-NEXT: ;
; CHECK-NEXT: ; Function test_sitofp_i64 : 0x00000044
; CHECK-NEXT: ;
; CHECK-NEXT: ; Function test_fptoui_i32 : 0x00000044
; CHECK-NEXT: ;
; CHECK-NEXT: ; Function test_fptosi_i64 : 0x00000044
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be easier to read/update this test if the checks for each function were by the function definition. I also think we can probably dispense of the CHECK-NEXT in that case and not bother checking the empty lines - if the checks are all by the function definitions it's obvious if we missed one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be easier to read/update this test if the checks for each function were by the function definition. I also think we can probably dispense of the CHECK-NEXT in that case and not bother checking the empty lines - if the checks are all by the function definitions it's obvious if we missed one.

Changes made.

@bharadwajy bharadwajy requested a review from bogner November 22, 2024 22:36
Comment on lines 14 to 15
; CHECK: Doubles: true
; CHECK: DX11_1_DoubleExtensions: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to have consistent whitespace across the check lines

@bharadwajy bharadwajy merged commit 96547de into llvm:main Nov 25, 2024
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[DirectX] Extend existing Module pass to collect shader flags per-function
5 participants