-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[DXIL][Analysis] Collect Function properties in Metadata Analysis #105728
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
[DXIL][Analysis] Collect Function properties in Metadata Analysis #105728
Conversation
…adata Analysis - Add a map of function-function properties to the metadata information. - Add a structure to represent various function properties. Currently numthreads property of a compute shader entry function is represented.
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-backend-directx Author: S. Bharadwaj Yadavalli (bharadwajy) ChangesBasic infrastructure to collect Function properties in Metadata Analysis
Full diff: https://github.com/llvm/llvm-project/pull/105728.diff 5 Files Affected:
diff --git a/llvm/include/llvm/Analysis/DXILMetadataAnalysis.h b/llvm/include/llvm/Analysis/DXILMetadataAnalysis.h
index 0515139c01aee6..6195ac08b0e99c 100644
--- a/llvm/include/llvm/Analysis/DXILMetadataAnalysis.h
+++ b/llvm/include/llvm/Analysis/DXILMetadataAnalysis.h
@@ -9,6 +9,7 @@
#ifndef LLVM_ANALYSIS_DXILMETADATA_H
#define LLVM_ANALYSIS_DXILMETADATA_H
+#include "llvm/IR/Function.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/VersionTuple.h"
@@ -19,12 +20,16 @@ namespace llvm {
namespace dxil {
+struct FunctionProperties {
+ unsigned NumThreads[3];
+};
+
struct ModuleMetadataInfo {
VersionTuple DXILVersion{};
VersionTuple ShaderModelVersion{};
Triple::EnvironmentType ShaderStage = Triple::UnknownEnvironment;
VersionTuple ValidatorVersion{};
-
+ std::unordered_map<Function *, FunctionProperties> FunctionPropertyMap{};
void print(raw_ostream &OS) const;
};
diff --git a/llvm/lib/Analysis/DXILMetadataAnalysis.cpp b/llvm/lib/Analysis/DXILMetadataAnalysis.cpp
index 192e151565370d..39bd750d69649b 100644
--- a/llvm/lib/Analysis/DXILMetadataAnalysis.cpp
+++ b/llvm/lib/Analysis/DXILMetadataAnalysis.cpp
@@ -8,11 +8,16 @@
#include "llvm/Analysis/DXILMetadataAnalysis.h"
#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <memory>
+#include <utility>
#define DEBUG_TYPE "dxil-metadata-analysis"
@@ -33,6 +38,37 @@ static ModuleMetadataInfo collectMetadataInfo(Module &M) {
MMDAI.ValidatorVersion =
VersionTuple(MajorMD->getZExtValue(), MinorMD->getZExtValue());
}
+
+ if (MMDAI.ShaderStage == Triple::EnvironmentType::Compute) {
+ // For all HLSL Shader functions
+ for (auto &F : M.functions()) {
+ if (!F.hasFnAttribute("hlsl.shader"))
+ continue;
+
+ // Get numthreads attribute value
+ StringRef NumThreadsStr =
+ F.getFnAttribute("hlsl.numthreads").getValueAsString();
+ SmallVector<StringRef> NumThreadsVec;
+ NumThreadsStr.split(NumThreadsVec, ',');
+ if (NumThreadsVec.size() != 3) {
+ report_fatal_error(Twine(F.getName()) +
+ ": Invalid numthreads specified",
+ /* gen_crash_diag */ false);
+ }
+ FunctionProperties EFP;
+ auto Zip =
+ llvm::zip(NumThreadsVec, MutableArrayRef<unsigned>(EFP.NumThreads));
+ for (auto It : Zip) {
+ StringRef Str = std::get<0>(It);
+ APInt V;
+ assert(!Str.getAsInteger(10, V) &&
+ "Failed to parse numthreads components as integer values");
+ unsigned &Num = std::get<1>(It);
+ Num = V.getLimitedValue();
+ }
+ MMDAI.FunctionPropertyMap.emplace(std::make_pair(std::addressof(F), EFP));
+ }
+ }
return MMDAI;
}
@@ -42,6 +78,20 @@ void ModuleMetadataInfo::print(raw_ostream &OS) const {
OS << "Shader Stage : " << Triple::getEnvironmentTypeName(ShaderStage)
<< "\n";
OS << "Validator Version : " << ValidatorVersion.getAsString() << "\n";
+ for (auto MapItem : FunctionPropertyMap) {
+ MapItem.first->getReturnType()->print(OS, false, true);
+ OS << " " << MapItem.first->getName() << "(";
+ FunctionType *FT = MapItem.first->getFunctionType();
+ for (unsigned I = 0, Sz = FT->getNumParams(); I < Sz; ++I) {
+ if (I)
+ OS << ",";
+ FT->getParamType(I)->print(OS);
+ }
+ OS << ")\n";
+ OS << " NumThreads: " << MapItem.second.NumThreads[0] << ","
+ << MapItem.second.NumThreads[1] << "," << MapItem.second.NumThreads[2]
+ << "\n";
+ }
}
//===----------------------------------------------------------------------===//
diff --git a/llvm/test/CodeGen/DirectX/Metadata/dxilVer-1.8.ll b/llvm/test/CodeGen/DirectX/Metadata/dxilVer-1.8.ll
index 53edcde24b189f..d429b6534e4253 100644
--- a/llvm/test/CodeGen/DirectX/Metadata/dxilVer-1.8.ll
+++ b/llvm/test/CodeGen/DirectX/Metadata/dxilVer-1.8.ll
@@ -9,6 +9,8 @@ target triple = "dxil-pc-shadermodel6.8-compute"
; ANALYSIS-NEXT: DXIL Version : 1.8
; ANALYSIS-NEXT: Shader Stage : compute
; ANALYSIS-NEXT: Validator Version : 0
+; ANALYSIS-NEXT: void entry()
+; ANALYSIS-NEXT: NumThreads: 1,2,1
; ANALYSIS-EMPTY:
define void @entry() #0 {
diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs-val-ver-0.0.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs-val-ver-0.0.ll
index c9ea6c94e36519..b7e6d2fd3cf432 100644
--- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs-val-ver-0.0.ll
+++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs-val-ver-0.0.ll
@@ -19,5 +19,7 @@ attributes #0 = { noinline nounwind "exp-shader"="cs" "hlsl.numthreads"="1,2,1"
; ANALYSIS: Shader Model Version : 6.6
; ANALYSIS-NEXT: DXIL Version : 1.6
; ANALYSIS-NEXT: Shader Stage : compute
-; ANALYSIS-NEXT: Validator Version : 0
+; ANALYSIS-NEXT: Validator Version : 0.0
+; ANALYSIS-NEXT: void entry()
+; ANALYSIS-NEXT: NumThreads: 1,2,1
; ANALYSIS-EMPTY:
diff --git a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll
index f5e524562ac1e0..77b52ee3478abe 100644
--- a/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll
+++ b/llvm/test/CodeGen/DirectX/Metadata/shaderModel-cs.ll
@@ -11,6 +11,8 @@ target triple = "dxil-pc-shadermodel6.6-compute"
; ANALYSIS-NEXT: DXIL Version : 1.6
; ANALYSIS-NEXT: Shader Stage : compute
; ANALYSIS-NEXT: Validator Version : 0
+; ANALYSIS-NEXT: void entry()
+; ANALYSIS-NEXT: NumThreads: 1,2,1
; ANALYSIS-EMPTY:
define void @entry() #0 {
|
…ysis Pass - Record shader kind specified for entry function from hlsl.shader attribute - Record all entry functions irrespective of the target shader profile being compute - Update expected output of tests accordingly
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I didn't notice it when the pass itself went in, but we should really have some testing in llvm/test/Analysis/DXILMetadataAnalysis
, not just piggy backing on the DirectX backend tests.
struct ModuleMetadataInfo { | ||
VersionTuple DXILVersion{}; | ||
VersionTuple ShaderModelVersion{}; | ||
Triple::EnvironmentType ShaderStage = Triple::UnknownEnvironment; | ||
VersionTuple ValidatorVersion{}; | ||
|
||
std::unordered_map<Function *, FunctionProperties> FunctionPropertyMap{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need to look up functions in a map for the function properties, or do we just need to be able to iterate through them? It might be nice to wire up the metadata analysis into DXILTranslateMetadata before adding more properties so that we can see how they'll be used.
In any case, std::unordered_map
is very expensive as data structures go. Since this mapping is built up first and then queried later, we can probably just use a sorted vector instead of something heavyweight like this. There are some good tips on how to choose data structures for situations like this in the LLVM Programmer's Manual
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need to look up functions in a map for the function properties, or do we just need to be able to iterate through them? It might be nice to wire up the metadata analysis into DXILTranslateMetadata before adding more properties so that we can see how they'll be used.
Function lookup is employed in an implementation I have as a follow on PR that consumes this information in DXILTranslateMetadata
pass - see here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to MapVector
.
Move testing of DXILMetadata Analysis to llvm/test/Analysis/DXILMetadataAnalysis from llvm/test/CodeGen/DirectX/Metadata and add a new test
Moved testing of DXIL Metadata Analysis out of DirectX backend tests to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more questions and comments. We should have a test with multiple entry points as well.
struct ModuleMetadataInfo { | ||
VersionTuple DXILVersion{}; | ||
VersionTuple ShaderModelVersion{}; | ||
Triple::EnvironmentType ShaderStage = Triple::UnknownEnvironment; | ||
VersionTuple ValidatorVersion{}; | ||
|
||
MapVector<Function *, EntryProperties> EntryPropertyMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why a MapVector? Is the insertion order of these functions interesting to preserve for some reason, or is this just here so that it's easy to make the order that we print them in deterministic?
if (!llvm::to_integer(NumThreadsVec[0], EFP.NumThreadsX, 10)) | ||
assert(false && "Failed to parse X component of numthreads"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (x) assert(false)
is generally discouraged. It doesn't make much difference, but it's probably better
[[maybe_unused]] bool Success =
llvm::to_integer(NumThreadsVec[0], EFP.NumThreadsX, 10);
assert(Success&& "Failed to parse X component of numthreads");
if (!llvm::to_integer(NumThreadsVec[2], EFP.NumThreadsZ, 10)) | ||
assert(false && "Failed to parse Z component of numthreads"); | ||
} | ||
MMDAI.EntryPropertyMap.insert(std::make_pair(std::addressof(F), EFP)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use std::addressof(F)
here rather than just &F
? Generally you only need std::addressof
in certain template contexts
<< "\n"; | ||
OS << "Validator Version : " << ValidatorVersion.getAsString() << "\n"; | ||
for (auto MapItem : EntryPropertyMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use C++17 so you could use structured bindings to clean this up slightly if you want. In any case this should probably be const auto &
to avoid copying the items unnecessarily
I've already added one - |
Collect Entry properties in a SmallVector instead of in a Mapvector. Use structured binding declaration for printing analysis info.
@@ -9,6 +9,8 @@ | |||
#ifndef LLVM_ANALYSIS_DXILMETADATA_H | |||
#define LLVM_ANALYSIS_DXILMETADATA_H | |||
|
|||
#include "llvm/ADT/SmallVector.h" | |||
#include "llvm/IR/Function.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A forward declaration should be sufficient for Function
struct ModuleMetadataInfo { | ||
VersionTuple DXILVersion{}; | ||
VersionTuple ShaderModelVersion{}; | ||
Triple::EnvironmentType ShaderStage = Triple::UnknownEnvironment; | ||
VersionTuple ValidatorVersion{}; | ||
|
||
SmallVector<EntryProperties, 4> EntryPropertyVec{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless there's a specific argument for 4, it's better to use SmallVector<EntryProperties>
which will automatically deduce a reasonable size
#include <memory> | ||
#include <utility> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think <memory>
or <utility>
are used any more
NumThreadsStr.split(NumThreadsVec, ','); | ||
assert(NumThreadsVec.size() == 3 && "Invalid numthreads specified"); | ||
// Read in the three component values of numthreads | ||
bool Success = llvm::to_integer(NumThreadsVec[0], EFP.NumThreadsX, 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need a [[maybe_unused]]
or a (void)Success
so that this doesn't trigger warnings in no-asserts builds
<< "\n"; | ||
OS << "Validator Version : " << ValidatorVersion.getAsString() << "\n"; | ||
for (const auto [Ent, SS, NX, NY, NZ] : EntryPropertyVec) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be const auto &[Ent, SS, NX, NY, NZ]
to avoid a copy. This is also starting to get a bit unwieldy with this many members in the structured binding.
Also make sure to update the PR description - it still mentions a map. |
<< "\n"; | ||
OS << "Validator Version : " << ValidatorVersion.getAsString() << "\n"; | ||
for (const auto EP : EntryPropertyVec) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already pointed this out a couple of times (#105728 (comment), #105728 (comment)), but please don't copy the value here. It's inefficient, and a const reference will do just fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already pointed this out a couple of times (#105728 (comment), #105728 (comment)), but please don't copy the value here. It's inefficient, and a const reference will do just fine.
Changed.
…c52f95eba Local branch amd-gfx 10fc52f Merged main:1ace91f925ad87c3e5eb836ad58fdffe60c4aea6 into amd-gfx:a9dc02e5ed6f Remote branch main 8aa8c05 [DXIL][Analysis] Collect Function properties in Metadata Analysis (llvm#105728)
Basic infrastructure to collect Function properties in Metadata Analysis
SmallVector
of entry properties to the metadata information.numthreads
and shader kind properties of shader entry functions are represented.