-
Notifications
You must be signed in to change notification settings - Fork 13.7k
[HLSL] Add descriptor table metadata parsing #142492
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
base: users/joaosaffran/143201
Are you sure you want to change the base?
[HLSL] Add descriptor table metadata parsing #142492
Conversation
if (std::optional<uint32_t> Val = extractMdIntValue(RangeDescriptorNode, 1)) | ||
Range.NumDescriptors = *Val; | ||
else | ||
return reportError(Ctx, "Invalid value for Number of Descriptor in Range"); |
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 could probably have a function like reportInvalidTypeError
for all of these. Similar to reportValueError
but with a message that will differentiate between the two.
Something like "Parameter: ... expected metadata node of type ... but got ..." I am not sure if you can retrieve the type of an MDNode
?
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.
Have addressed this in a separate branch, will send a follow-up PR
@@ -391,14 +567,12 @@ PreservedAnalyses RootSignatureAnalysisPrinter::run(Module &M, | |||
OS << "Definition for '" << F.getName() << "':\n"; | |||
|
|||
// start root signature header | |||
Space++; |
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.
These don't seem like a related change? I guess it is to conform to use explicit idents everywhere instead of incremeting/decrementing?
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.
Yeah, it is just to add consistency in the idents
|
||
if (Version == 2 && | ||
Type != llvm::to_underlying(dxbc::DescriptorRangeType::Sampler)) { | ||
switch (Flags) { |
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.
Personally, I find this switch statement really difficult to follow along and verify it is doing what we want.
I am pretty sure it is implementing the logic from here.
Maybe a templated isFlagSet
function and an if-else chain might help?
Or a comment describing/linking to the docs to help.
diff
Outdated
@@ -0,0 +1,34 @@ | |||
diff --git a/llvm/include/llvm/BinaryFormat/DXContainer.h b/llvm/include/llvm/BinaryFormat/DXContainer.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.
Accidentally included this file?
@@ -98,6 +98,17 @@ DESCRIPTOR_RANGE_FLAG(16, DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS) | |||
#undef DESCRIPTOR_RANGE_FLAG | |||
#endif // DESCRIPTOR_RANGE_FLAG | |||
|
|||
// DESCRIPTOR_RANGE(value, name). | |||
#ifdef DESCRIPTOR_RANGE |
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.
nit: the other ifdef blocks have a new line after the ifdef.
nit: I suggest naming to constants for improved readability. It is pretty easy to infer in this case but I think its a good habit. Up to you though. Refers to: llvm/lib/Target/DirectX/DXILRootSignature.cpp:326 in 72ca44b. [](commit_id = 72ca44b, deletion_comment = False) |
|
||
static bool verifyDescriptorRangeFlag(uint32_t Version, uint32_t Type, | ||
uint32_t Flags) { | ||
bool IsSampler = |
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.
bool IsSampler = | |
const bool IsSampler = |
@@ -391,14 +565,12 @@ PreservedAnalyses RootSignatureAnalysisPrinter::run(Module &M, | |||
OS << "Definition for '" << F.getName() << "':\n"; | |||
|
|||
// start root signature header | |||
Space++; | |||
OS << indent(Space) << "Flags: " << format_hex(RS.Flags, 8) << "\n"; |
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.
given that space is now always 0 is it still needed?
I think you can remove the calls that are effectively 'indent(0)'
case llvm::to_underlying(dxbc::RootParameterType::DescriptorTable): { | ||
const mcdxbc::DescriptorTable &Table = | ||
RS.ParametersContainer.getDescriptorTable(Loc); | ||
OS << indent(Space + 2) << "NumRanges: " << Table.Ranges.size() << "\n"; |
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.
nit: Suggest avoiding 'magic numbers' and instead use named constants. I assume the 2 is intended to be a tab? Could use 'const uint8_t TabInSpaces = 1'.
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.
Some minor suggestions from me. But the rest looks good.
Header.ParameterType = | ||
llvm::to_underlying(dxbc::RootParameterType::DescriptorTable); | ||
|
||
for (unsigned int I = 2; I < DescriptorTableNode->getNumOperands(); I++) { |
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 calculate NumOperands
earlier, probably best to store that and reuse it here instead of calling getNumOperands
each time through the loop.
See https://llvm.org/docs/CodingStandards.html#don-t-evaluate-end-every-time-through-a-loop
if (Version == 1) { | ||
if (IsSampler) { | ||
return Flags == 0; | ||
} | ||
return Flags == | ||
llvm::to_underlying(dxbc::DescriptorRangeFlag::DESCRIPTORS_VOLATILE); | ||
} |
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.
This doesn't make a lot of sense - version 1 doesn't have a flags field, so why are we verifying it? Furthermore, if this is meaningful to do, it doesn't match the spec in https://github.com/llvm/wg-hlsl/blob/main/proposals/0002-root-signature-in-clang.md#all-the-values-should-be-legal, which states that samplers must have the DESCRIPTORS_VOLATILE flag and SRVs/CBVs/UAVs must have the DATA_VOLATILE flag.
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.
The metadata representation, for descriptor tables, always contains a flag values, so the spec expect me to verify that. I will fix the values though.
return Flags == 0 || | ||
isFlagSet(Flags, dxbc::DescriptorRangeFlag::DESCRIPTORS_VOLATILE) || | ||
isFlagSet(Flags, dxbc::DescriptorRangeFlag::DATA_VOLATILE) || | ||
isFlagSet(Flags, dxbc::DescriptorRangeFlag::DATA_STATIC) || | ||
isFlagSet( | ||
Flags, | ||
dxbc::DescriptorRangeFlag::DATA_STATIC_WHILE_SET_AT_EXECUTE) || | ||
isFlagSet(Flags, | ||
dxbc::DescriptorRangeFlag:: | ||
DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS) || | ||
isFlagSet(Flags, dxbc::DescriptorRangeFlag::DESCRIPTORS_VOLATILE, | ||
dxbc::DescriptorRangeFlag::DATA_VOLATILE) || | ||
isFlagSet( | ||
Flags, dxbc::DescriptorRangeFlag::DESCRIPTORS_VOLATILE, | ||
dxbc::DescriptorRangeFlag::DATA_STATIC_WHILE_SET_AT_EXECUTE) || | ||
isFlagSet(Flags, | ||
dxbc::DescriptorRangeFlag:: | ||
DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS, | ||
dxbc::DescriptorRangeFlag::DATA_VOLATILE) || | ||
isFlagSet(Flags, | ||
dxbc::DescriptorRangeFlag:: | ||
DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS, | ||
dxbc::DescriptorRangeFlag::DATA_STATIC) || | ||
isFlagSet( | ||
Flags, | ||
dxbc::DescriptorRangeFlag:: | ||
DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS, | ||
dxbc::DescriptorRangeFlag::DATA_STATIC_WHILE_SET_AT_EXECUTE); |
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 find this extremely hard to follow. I think it'd make things quite a bit simpler if we broke this down based on which DESCRIPTORS_
behaviours are set, and then check the DATA_
bits based on that.
That is, if DESCRIPTORS_VOLATILE
is set, then the DATA_ flags can't contain DATA_STATIC
, but for DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS
or the default, the three DATA_*
values are all fine. We'll also need to check that the DATA_*
values are mutually exclusive presumably.
This also all becomes a little bit more readable if we (1) use BitmaskEnum.h so we can avoid a bunch of to_underlying
calls:
// Earlier in DXContainer.h:
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
// Then define DescriptorRangeFlag like so:
enum class DescriptorRangeFlag : uint32_t {
#define DESCRIPTOR_RANGE_FLAG(Num, Val) Val = Num,
#include "DXContainerConstants.def"
LLVM_MARK_AS_BITMASK_ENUM(DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS)
};
and then (2), define a local alias for dxbc::DescriptorRangeFlag
to shorten things up.
Putting it all together, we get something like:
static bool verifyDescriptorRangeFlag(uint32_t Version, uint32_t Type,
uint32_t FlagsVal) {
using FlagT = dxbc::DescriptorRangeFlag;
FlagT Flags = FlagT(FlagsVal);
const bool IsSampler =
(Type == llvm::to_underlying(dxbc::DescriptorRangeType::Sampler));
// The data-specific flags are mutually exclusive.
FlagT DataFlags = FlagT::DATA_VOLATILE | FlagT::DATA_STATIC |
FlagT::DATA_STATIC_WHILE_SET_AT_EXECUTE;
if (popcount(llvm::to_underlying(Flags & DataFlags)) > 1)
return false;
// For volatile descriptors, DATA_STATIC is never valid.
if ((Flags & FlagT::DESCRIPTORS_VOLATILE) == FlagT::DESCRIPTORS_VOLATILE) {
FlagT Mask = FlagT::DESCRIPTORS_VOLATILE;
if (!IsSampler) {
Mask |= FlagT::DATA_VOLATILE;
Mask |= FlagT::DATA_STATIC_WHILE_SET_AT_EXECUTE;
}
return (Flags & ~Mask) == FlagT::NONE;
}
// For default (static) or "STATIC_KEEPING_BUFFER_BOUNDS_CHECKS" descriptors,
// the other data-specific flags may all be set.
FlagT Mask = FlagT::DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS;
if (!IsSampler) {
Mask |= FlagT::DATA_VOLATILE;
Mask |= FlagT::DATA_STATIC;
Mask |= FlagT::DATA_STATIC_WHILE_SET_AT_EXECUTE;
}
return (Flags & ~Mask) == FlagT::NONE;
}
Note that I don't think any of the current tests check for the exclusivity of DATA_ flags.
; DXC-NEXT: BaseShaderRegister: 1 | ||
; DXC-NEXT: RegisterSpace: 10 | ||
; DXC-NEXT: OffsetInDescriptorsFromTableStart: 5 | ||
; DXC-NEXT: DESCRIPTORS_VOLATILE: true |
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.
Are the DESCRIPTORS_VOLATILE and DATA_VOLATILE values here correct? Might want to get this change in after #143201 to avoid conflicts.
enum class DescriptorRangeFlag : uint32_t { | ||
#include "DXContainerConstants.def" | ||
|
||
LLVM_MARK_AS_BITMASK_ENUM(DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS) |
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.
review note: the value used in the macro is required to be the highest value
DESCRIPTOR_RANGE(4, ERROR) | ||
DESCRIPTOR_RANGE(0, SRV) | ||
DESCRIPTOR_RANGE(1, UAV) | ||
DESCRIPTOR_RANGE(2, CBV) | ||
DESCRIPTOR_RANGE(3, Sampler) | ||
DESCRIPTOR_RANGE(0, NONE) | ||
#undef DESCRIPTOR_RANGE |
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 assume this is an artifact of not rebasing onto #143201?
Otherwise, the values here seem very odd.
if (popcount(llvm::to_underlying(Flags & DataFlags)) == 1) | ||
return true; |
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 this is correct. It seems we would skip further validation.
If Flags = DESCRIPTORS_VOLATILE | DATA_STATIC
, then this will return early as valid.
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 this check is needed at all - once we've assured that at most one of these flags is set the logic below should handle the other cases.
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.
This test fails llvm/test/CodeGen/DirectX/ContainerData/RootSignature-DescriptorTable-AllValidFlagCombinations.ll
, because the DATA_*
are allowed to be set without setting any DESCRIPTOR_*
flag, so for example the metadata below errors with: error: Invalid value for DescriptorFlag: 2
, even it is expected to be valid according to the spec: https://github.com/llvm/wg-hlsl/blob/main/proposals/0002-root-signature-in-clang.md#all-the-things-validated-in-sema.
!12 = !{ !"CBV", i32 5, i32 1, i32 7, i32 5, i32 2 }
.Case("UAV", llvm::to_underlying(dxbc::DescriptorRangeType::UAV)) | ||
.Case("Sampler", | ||
llvm::to_underlying(dxbc::DescriptorRangeType::Sampler)) | ||
.Default(llvm::to_underlying(dxbc::DescriptorRangeType::ERROR)); |
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.
This doesn't look right:
- I don't think we want an "ERROR" value in the DescriptorRangeType enum
- Shouldn't we report an error here?
if (popcount(llvm::to_underlying(Flags & DataFlags)) == 1) | ||
return true; |
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 this check is needed at all - once we've assured that at most one of these flags is set the logic below should handle the other cases.
|
||
const bool IsSampler = | ||
(Type == llvm::to_underlying(dxbc::DescriptorRangeType::Sampler)); | ||
|
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.
In #142492 (comment) you pointed out that we do want the (simple) validation for version 1, but I don't see that happening any more. Shouldn't we be doing that here?
Closes: #126640