-
Notifications
You must be signed in to change notification settings - Fork 822
[SPIR-V] Normalize inline SPIR-V for builtins #7974
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: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,10 +48,18 @@ | |
| #include "llvm/ADT/StringRef.h" | ||
| #include "llvm/Support/ErrorHandling.h" | ||
| #include "llvm/Support/raw_ostream.h" | ||
|
|
||
| #ifdef ENABLE_SPIRV_CODEGEN | ||
| // Enables functions like spv::BuiltInToString() | ||
| #define SPV_ENABLE_UTILITY_CODE | ||
| #include "spirv/unified1/spirv.hpp11" | ||
| #endif | ||
|
|
||
| #include <algorithm> | ||
| #include <array> | ||
| #include <bitset> | ||
| #include <float.h> | ||
| #include <optional> | ||
|
|
||
| enum ArBasicKind { | ||
| AR_BASIC_BOOL, | ||
|
|
@@ -14593,6 +14601,162 @@ void ValidateDispatchGridValues(DiagnosticsEngine &Diags, | |
| << A.getName() << A.getRange(); | ||
| } | ||
|
|
||
| void hlsl::NormalizeInlineSPIRVAttributes(Sema &S, Decl *D) { | ||
| #ifdef ENABLE_SPIRV_CODEGEN | ||
| // Collecting the values that can be set across multiple attributes. | ||
| std::optional<std::pair<spv::StorageClass, SourceRange>> StorageClass; | ||
| std::optional<std::pair<unsigned, SourceRange>> Location; | ||
| std::optional<std::pair<spv::BuiltIn, SourceRange>> BuiltIn; | ||
|
|
||
| // Vector collecting all the other attributes. | ||
| clang::AttrVec NewAttrs; | ||
|
|
||
| for (auto *A : D->attrs()) { | ||
| if (auto *DA = dyn_cast<VKDecorateExtAttr>(A)) { | ||
| spv::Decoration Decoration = spv::Decoration(DA->getDecorate()); | ||
| if (Decoration == spv::Decoration::Location) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The inline spir-v is suppose to be pass through decorations. We need to be careful we do not set precedent that the compiler will start looking into the specifics of the inline SPIR-V. It might make sense to have the compiler look into some things. I know we look into pointer types in the SPIR-V backend end to add types to the type manager. It might be reasonable on so other situations. In this case, you are checking the validity a location decoration. I guess I can ask the question, why just this one and not others? We have loads of decorations that can be added in for official ways. Do you plan on trying to verify all of them? If not where do you draw the line?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The line I want to draw is at the functional overlap between this low-level attribute and a higher-level attribute like
Hence why I only "validate" only the Location and BuiltIn decoration here: only looking at things we created a high-level attribute for. |
||
| if (DA->literals_size() != 1) { | ||
| S.Diags.Report( | ||
| A->getLocation(), | ||
| diag:: | ||
| err_hlsl_spv_inline_invalid_decoration_single_operand_missing) | ||
| << "Location"; | ||
| return; | ||
| } | ||
| unsigned Index = *DA->literals_begin(); | ||
| if (Location.has_value() && Location->first != Index) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_location_redefinition) | ||
| << DA << Location->first; | ||
| return; | ||
| } | ||
| Location = {Index, DA->getLocation()}; | ||
| } else if (Decoration == spv::Decoration::BuiltIn) { | ||
| if (DA->literals_size() != 1) { | ||
| S.Diags.Report( | ||
| A->getLocation(), | ||
| diag:: | ||
| err_hlsl_spv_inline_invalid_decoration_single_operand_missing) | ||
| << "BuiltIn"; | ||
| return; | ||
| } | ||
| spv::BuiltIn ID = spv::BuiltIn(*DA->literals_begin()); | ||
| if (BuiltIn.has_value() && ID != BuiltIn->first) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_builtin_redefinition) | ||
| << DA << spv::BuiltInToString(BuiltIn->first); | ||
| return; | ||
| } | ||
| BuiltIn = {ID, DA->getLocation()}; | ||
| } else { | ||
| NewAttrs.push_back(A); | ||
| } | ||
| } else if (auto *SCA = dyn_cast<VKStorageClassExtAttr>(A)) { | ||
| spv::StorageClass SC = spv::StorageClass(SCA->getStclass()); | ||
| if (StorageClass.has_value() && StorageClass->first != SC) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_storage_class_redefinition) | ||
| << SCA << spv::StorageClassToString(StorageClass->first); | ||
| return; | ||
| } | ||
| StorageClass = {SC, SCA->getLocation()}; | ||
| } else if (auto *LA = dyn_cast<VKLocationAttr>(A)) { | ||
| if (Location.has_value() && | ||
| Location->first != static_cast<unsigned>(LA->getNumber())) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_location_redefinition) | ||
| << LA << Location->first; | ||
| return; | ||
| } | ||
| Location = {LA->getNumber(), LA->getLocation()}; | ||
| } else if (auto *BA = dyn_cast<VKExtBuiltinOutputAttr>(A)) { | ||
| if (StorageClass.has_value() && | ||
| StorageClass->first != spv::StorageClass::Output) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_storage_class_redefinition) | ||
| << BA << spv::StorageClassToString(StorageClass->first); | ||
| return; | ||
| } | ||
| StorageClass = {spv::StorageClass::Output, BA->getLocation()}; | ||
|
|
||
| spv::BuiltIn ID = spv::BuiltIn(BA->getBuiltInID()); | ||
| if (BuiltIn.has_value() && ID != BuiltIn->first) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_builtin_redefinition) | ||
| << BA << spv::BuiltInToString(BuiltIn->first); | ||
| return; | ||
| } | ||
| BuiltIn = {ID, BA->getLocation()}; | ||
|
|
||
| } else if (auto *BA = dyn_cast<VKExtBuiltinInputAttr>(A)) { | ||
| if (StorageClass.has_value() && | ||
| StorageClass->first != spv::StorageClass::Input) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_storage_class_redefinition) | ||
| << BA << spv::StorageClassToString(StorageClass->first); | ||
| return; | ||
| } | ||
| StorageClass = {spv::StorageClass::Input, BA->getLocation()}; | ||
|
|
||
| spv::BuiltIn ID = spv::BuiltIn(BA->getBuiltInID()); | ||
| if (BuiltIn.has_value() && ID != BuiltIn->first) { | ||
| S.Diags.Report(A->getLocation(), | ||
| diag::err_hlsl_spv_inline_builtin_redefinition) | ||
| << BA << spv::BuiltInToString(BuiltIn->first); | ||
| return; | ||
| } | ||
| BuiltIn = {ID, BA->getLocation()}; | ||
| } else { | ||
| NewAttrs.push_back(A); | ||
| } | ||
| } | ||
|
|
||
| if (BuiltIn.has_value()) { | ||
| // Location should not be set if a BuiltIn is requested. | ||
| if (Location.has_value()) { | ||
| S.Diags.Report(D->getLocation(), | ||
| diag::err_hlsl_spv_inline_builtin_incompatible) | ||
| << "Location" << spv::BuiltInToString(BuiltIn->first); | ||
| return; | ||
| } | ||
|
|
||
| // BuiltIn requires either Input or Output storage class. | ||
| if (StorageClass.has_value() && | ||
| StorageClass->first == spv::StorageClass::Input) | ||
| NewAttrs.push_back(new (S.Context) VKExtBuiltinInputAttr( | ||
| BuiltIn->second, S.Context, static_cast<unsigned>(BuiltIn->first), | ||
| 0)); | ||
| else if (StorageClass.has_value() && | ||
| StorageClass->first == spv::StorageClass::Output) | ||
| NewAttrs.push_back(new (S.Context) VKExtBuiltinOutputAttr( | ||
| BuiltIn->second, S.Context, static_cast<unsigned>(BuiltIn->first), | ||
| 0)); | ||
| else if (isa<ParmVarDecl>(D) || isa<FunctionDecl>(D)) { | ||
| unsigned BuiltInID = static_cast<unsigned>(BuiltIn->first); | ||
| NewAttrs.push_back(new (S.Context) VKDecorateExtAttr( | ||
| BuiltIn->second, S.Context, /* BuiltIn */ 11, &BuiltInID, 1, 0)); | ||
| } else | ||
| S.Diags.Report(D->getLocation(), | ||
| diag::err_hlsl_spv_inline_builtin_incompatible) | ||
| << std::string("StorageClass ") + | ||
| spv::StorageClassToString(StorageClass->first) | ||
| << spv::BuiltInToString(BuiltIn->first); | ||
| } else { | ||
| if (Location.has_value()) | ||
| NewAttrs.push_back(new (S.Context) VKLocationAttr( | ||
| Location->second, S.Context, static_cast<unsigned>(Location->first), | ||
| 0)); | ||
| if (StorageClass.has_value()) | ||
| NewAttrs.push_back(new (S.Context) VKStorageClassExtAttr( | ||
| StorageClass->second, S.Context, | ||
| static_cast<unsigned>(StorageClass->first), 0)); | ||
| } | ||
|
|
||
| D->dropAttrs(); | ||
| D->setAttrs(NewAttrs); | ||
| #endif // ENABLE_SPIRV_CODEGEN | ||
| } | ||
|
|
||
| void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A, | ||
| bool &Handled) { | ||
| DXASSERT_NOMSG(D != nullptr); | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,8 @@ | |
|
|
||
| // CHECK-DAG: OpCapability RuntimeDescriptorArray | ||
| // CHECK-DAG: OpCapability RayQueryKHR | ||
| // CHECK-DAG: OpDecorate %MyScene DescriptorSet 3 | ||
| // CHECK-DAG: OpDecorate %MyScene Binding 2 | ||
|
|
||
| using A = vk::SpirvOpaqueType</* OpTypeAccelerationStructureKHR */ 5341>; | ||
| // CHECK: %[[name:[^ ]+]] = OpTypeAccelerationStructureKHR | ||
|
|
@@ -12,6 +14,8 @@ using RA [[vk::ext_capability(/* RuntimeDescriptorArray */ 5302)]] = vk::SpirvOp | |
| // CHECK: %[[ptr:[^ ]+]] = OpTypePointer UniformConstant %[[rarr]] | ||
| // CHECK: %MyScene = OpVariable %[[ptr]] UniformConstant | ||
| [[vk::ext_storage_class(0)]] | ||
| [[vk::ext_decorate(/* Binding */ 33, 2)]] | ||
| [[vk::ext_decorate(/* DescriptorSet */ 34, 3)]] | ||
|
Comment on lines
+17
to
+18
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I can tell, this works with We blindly add these decorations to the global variable, and we do all other processing (including adding a set and binding decoration) normally. These are suppose to be pass throughs. If the user gets an invalid module because they now have multiple set and binding decorations, that is what they asked for. This type of interaction where we will look into a decoration in inline spir-v and allow it to interact with the other behaviour of the compiler is generally not what is asked for. As in another comment, I can see some amount of this being allowed. For example, if the storage class is specifically set, then do not add the variable to the global cbuffer makes sense. In that case, there is no other way to stop something for going into the cbuffer. We did look into the spirv types for pointers because HLSL codegen leads to pointer type being generated, but they cannot be explicitly named. In this case, we can explicitly set the set and binding using
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It does, but as you asked in the other PR, it's not "allowed". I understood in the other PR why you wanted to restrict |
||
| RA MyScene; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| [[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] | ||
| [[vk::ext_storage_class(/* UniformConstant */ 0)]] | ||
| // expected-error@+1{{StorageClass UniformConstant incompatible with the BuiltIn WorkgroupId}} | ||
| static uint3 input; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| // expected-error@+1{{'ext_builtin_input' cannot be used on a variable already associated to another storage class Output}} | ||
| [[vk::ext_builtin_input(/* NumWorkgroups */ 24)]] | ||
| [[vk::ext_builtin_output(/* NumWorkgroups */ 24)]] | ||
| static uint3 invalid; | ||
|
|
||
| void main() { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| [[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] | ||
| [[vk::ext_builtin_input(/* WorkgroupId */ 26)]] | ||
| [[vk::ext_decorate(/* Location */ 30, 0)]] | ||
| // expected-error@+1{{Location incompatible with the BuiltIn WorkgroupId}} | ||
| static uint3 input; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| // expected-error@+1{{decoration BuiltIn requires 1 operand}} | ||
| [[vk::ext_decorate(/* BuiltIn */ 11)]] | ||
| static uint3 input; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| // expected-error@+1{{'ext_decorate' cannot be used on a variable already associated with the built-in LocalInvocationId}} | ||
| [[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] | ||
| [[vk::ext_decorate(/* BuiltIn */ 11, /* LocalInvocationId */ 27)]] | ||
| [[vk::ext_storage_class(/* Input */ 1)]] | ||
| static uint3 invalid; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| // expected-error@+1{{'ext_builtin_input' cannot be used on a variable already associated with the built-in WorkgroupId}} | ||
| [[vk::ext_builtin_input(/* NumWorkgroups */ 24)]] | ||
| [[vk::ext_builtin_input(/* WorkgroupId */ 26)]] | ||
| static uint3 invalid; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s | ||
|
|
||
| [[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] | ||
| [[vk::ext_builtin_input(/* WorkgroupId */ 26)]] | ||
| static uint3 input; | ||
|
|
||
| // CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector<unsigned int, 3>' static | ||
| // CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} <line:3:3> 26 | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // RUN: %dxc -T cs_6_9 -E main -spirv %s -verify | ||
|
|
||
| // expected-error@+1{{'ext_storage_class' cannot be used on a variable already associated to another storage class Input}} | ||
| [[vk::ext_storage_class(/* Output */ 3)]] | ||
| [[vk::ext_builtin_input(/* WorkgroupId */ 26)]] | ||
| static uint3 input; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s | ||
|
|
||
| [[vk::ext_storage_class(/* Input */ 1)]] | ||
| [[vk::ext_builtin_input(/* WorkgroupId */ 26)]] | ||
| static uint3 input; | ||
| // CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector<unsigned int, 3>' static | ||
| // CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} <line:4:3> 26 | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s | ||
|
|
||
| [[vk::ext_builtin_input(/* WorkgroupId */ 26)]] | ||
| [[vk::ext_storage_class(/* Input */ 1)]] | ||
| static uint3 input; | ||
| // CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector<unsigned int, 3>' static | ||
| // CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} <line:3:3> 26 | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify | ||
|
|
||
| // expected-error@+1{{'ext_builtin_output' cannot be used on a variable already associated with the built-in WorkgroupId}} | ||
| [[vk::ext_builtin_output(/* NumWorkgroups */ 24)]] | ||
| [[vk::ext_builtin_output(/* WorkgroupId */ 26)]] | ||
| static uint3 invalid; | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s | ||
|
|
||
| [[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] | ||
| [[vk::ext_builtin_output(/* WorkgroupId */ 26)]] | ||
| static uint3 input; | ||
|
|
||
| // CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector<unsigned int, 3>' static | ||
| // CHECK-NEXT: VKExtBuiltinOutputAttr 0x{{.+}} <line:3:3> 26 | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| // RUN: %dxc -T vs_6_9 -E main -spirv %s -verify | ||
|
|
||
| // expected-error@+1{{'ext_storage_class' cannot be used on a variable already associated to another storage class Output}} | ||
| [[vk::ext_storage_class(/* Input */ 1)]] | ||
| [[vk::ext_builtin_output(/* Position */ 0)]] | ||
| static float4 output; | ||
|
|
||
| void main() { | ||
| } | ||
|
|
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 function if very long. Can you split it up into multiple function to improve readability?