[HLSL] Add concepts for Structured buffers (#119643)
This PR adds concept validation to structured buffers, in the same way that it was done for typed buffers (like RWBuffer) in https://github.com/llvm/llvm-project/pull/116413. This PR should also be responsible for introducing rejection of 0 size elements for structured buffers. Fixes https://github.com/llvm/llvm-project/issues/117406
This commit is contained in:
parent
0517772b4a
commit
6a01ac7d06
@ -5118,7 +5118,7 @@ bool Type::isHLSLIntangibleType() const {
|
||||
|
||||
CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
|
||||
assert(RD != nullptr &&
|
||||
"all HLSL struct and classes should be CXXRecordDecl");
|
||||
"all HLSL structs and classes should be CXXRecordDecl");
|
||||
assert(RD->isCompleteDefinition() && "expecting complete type");
|
||||
return RD->isHLSLIntangible();
|
||||
}
|
||||
|
||||
@ -864,6 +864,10 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
|
||||
.addDefaultHandleConstructor();
|
||||
}
|
||||
|
||||
// This function is responsible for constructing the constraint expression for
|
||||
// this concept:
|
||||
// template<typename T> concept is_typed_resource_element_compatible =
|
||||
// __is_typed_resource_element_compatible<T>;
|
||||
static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
|
||||
TemplateTypeParmDecl *T) {
|
||||
ASTContext &Context = S.getASTContext();
|
||||
@ -885,8 +889,58 @@ static Expr *constructTypedBufferConstraintExpr(Sema &S, SourceLocation NameLoc,
|
||||
return TypedResExpr;
|
||||
}
|
||||
|
||||
static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
|
||||
NamespaceDecl *NSD) {
|
||||
// This function is responsible for constructing the constraint expression for
|
||||
// this concept:
|
||||
// template<typename T> concept is_structured_resource_element_compatible =
|
||||
// !__is_intangible<T> && sizeof(T) >= 1;
|
||||
static Expr *constructStructuredBufferConstraintExpr(Sema &S,
|
||||
SourceLocation NameLoc,
|
||||
TemplateTypeParmDecl *T) {
|
||||
ASTContext &Context = S.getASTContext();
|
||||
|
||||
// Obtain the QualType for 'bool'
|
||||
QualType BoolTy = Context.BoolTy;
|
||||
|
||||
// Create a QualType that points to this TemplateTypeParmDecl
|
||||
QualType TType = Context.getTypeDeclType(T);
|
||||
|
||||
// Create a TypeSourceInfo for the template type parameter 'T'
|
||||
TypeSourceInfo *TTypeSourceInfo =
|
||||
Context.getTrivialTypeSourceInfo(TType, NameLoc);
|
||||
|
||||
TypeTraitExpr *IsIntangibleExpr =
|
||||
TypeTraitExpr::Create(Context, BoolTy, NameLoc, UTT_IsIntangibleType,
|
||||
{TTypeSourceInfo}, NameLoc, true);
|
||||
|
||||
// negate IsIntangibleExpr
|
||||
UnaryOperator *NotIntangibleExpr = UnaryOperator::Create(
|
||||
Context, IsIntangibleExpr, UO_LNot, BoolTy, VK_LValue, OK_Ordinary,
|
||||
NameLoc, false, FPOptionsOverride());
|
||||
|
||||
// element types also may not be of 0 size
|
||||
UnaryExprOrTypeTraitExpr *SizeOfExpr = new (Context) UnaryExprOrTypeTraitExpr(
|
||||
UETT_SizeOf, TTypeSourceInfo, BoolTy, NameLoc, NameLoc);
|
||||
|
||||
// Create a BinaryOperator that checks if the size of the type is not equal to
|
||||
// 1 Empty structs have a size of 1 in HLSL, so we need to check for that
|
||||
IntegerLiteral *rhs = IntegerLiteral::Create(
|
||||
Context, llvm::APInt(Context.getTypeSize(Context.getSizeType()), 1, true),
|
||||
Context.getSizeType(), NameLoc);
|
||||
|
||||
BinaryOperator *SizeGEQOneExpr =
|
||||
BinaryOperator::Create(Context, SizeOfExpr, rhs, BO_GE, BoolTy, VK_LValue,
|
||||
OK_Ordinary, NameLoc, FPOptionsOverride());
|
||||
|
||||
// Combine the two constraints
|
||||
BinaryOperator *CombinedExpr = BinaryOperator::Create(
|
||||
Context, NotIntangibleExpr, SizeGEQOneExpr, BO_LAnd, BoolTy, VK_LValue,
|
||||
OK_Ordinary, NameLoc, FPOptionsOverride());
|
||||
|
||||
return CombinedExpr;
|
||||
}
|
||||
|
||||
static ConceptDecl *constructBufferConceptDecl(Sema &S, NamespaceDecl *NSD,
|
||||
bool isTypedBuffer) {
|
||||
ASTContext &Context = S.getASTContext();
|
||||
DeclContext *DC = NSD->getDeclContext();
|
||||
SourceLocation DeclLoc = SourceLocation();
|
||||
@ -907,9 +961,18 @@ static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
|
||||
TemplateParameterList *ConceptParams = TemplateParameterList::Create(
|
||||
Context, DeclLoc, DeclLoc, {T}, DeclLoc, nullptr);
|
||||
|
||||
DeclarationName DeclName = DeclarationName(
|
||||
&Context.Idents.get("__is_typed_resource_element_compatible"));
|
||||
Expr *ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
|
||||
DeclarationName DeclName;
|
||||
Expr *ConstraintExpr = nullptr;
|
||||
|
||||
if (isTypedBuffer) {
|
||||
DeclName = DeclarationName(
|
||||
&Context.Idents.get("__is_typed_resource_element_compatible"));
|
||||
ConstraintExpr = constructTypedBufferConstraintExpr(S, DeclLoc, T);
|
||||
} else {
|
||||
DeclName = DeclarationName(
|
||||
&Context.Idents.get("__is_structured_resource_element_compatible"));
|
||||
ConstraintExpr = constructStructuredBufferConstraintExpr(S, DeclLoc, T);
|
||||
}
|
||||
|
||||
// Create a ConceptDecl
|
||||
ConceptDecl *CD =
|
||||
@ -927,8 +990,10 @@ static ConceptDecl *constructTypedBufferConceptDecl(Sema &S,
|
||||
|
||||
void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
CXXRecordDecl *Decl;
|
||||
ConceptDecl *TypedBufferConcept =
|
||||
constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace);
|
||||
ConceptDecl *TypedBufferConcept = constructBufferConceptDecl(
|
||||
*SemaPtr, HLSLNamespace, /*isTypedBuffer*/ true);
|
||||
ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl(
|
||||
*SemaPtr, HLSLNamespace, /*isTypedBuffer*/ false);
|
||||
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
|
||||
.addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
@ -944,7 +1009,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
|
||||
Decl =
|
||||
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
|
||||
.addSimpleTemplateParams({"element_type"})
|
||||
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
||||
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
|
||||
@ -956,7 +1021,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
});
|
||||
|
||||
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer")
|
||||
.addSimpleTemplateParams({"element_type"})
|
||||
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
||||
setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, ResourceKind::RawBuffer,
|
||||
@ -966,7 +1031,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
});
|
||||
|
||||
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer")
|
||||
.addSimpleTemplateParams({"element_type"})
|
||||
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
||||
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
||||
@ -979,7 +1044,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
|
||||
Decl =
|
||||
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer")
|
||||
.addSimpleTemplateParams({"element_type"})
|
||||
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
||||
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
||||
@ -990,7 +1055,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
|
||||
Decl =
|
||||
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer")
|
||||
.addSimpleTemplateParams({"element_type"})
|
||||
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
||||
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
||||
@ -1001,7 +1066,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
|
||||
|
||||
Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace,
|
||||
"RasterizerOrderedStructuredBuffer")
|
||||
.addSimpleTemplateParams({"element_type"})
|
||||
.addSimpleTemplateParams({"element_type"}, StructuredBufferConcept)
|
||||
.finalizeForwardDeclaration();
|
||||
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
|
||||
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer,
|
||||
|
||||
@ -50,6 +50,14 @@
|
||||
|
||||
// EMPTY: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit [[RESOURCE]]
|
||||
// EMPTY-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
|
||||
// EMPTY-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_structured_resource_element_compatible'
|
||||
// EMPTY-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
|
||||
// EMPTY-NEXT: TemplateArgument type 'type-parameter-0-0'
|
||||
// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
|
||||
// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
|
||||
// EMPTY-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
|
||||
// EMPTY-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
|
||||
// EMPTY-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
|
||||
// EMPTY-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit <undeserialized declarations> class [[RESOURCE]]
|
||||
// EMPTY-NEXT: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
|
||||
@ -64,6 +72,14 @@ RESOURCE<float> Buffer;
|
||||
|
||||
// CHECK: ClassTemplateDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit [[RESOURCE]]
|
||||
// CHECK-NEXT: TemplateTypeParmDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> typename depth 0 index 0 element_type
|
||||
// CHECK-NEXT: ConceptSpecializationExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'bool' Concept 0x{{[0-9A-Fa-f]+}} '__is_structured_resource_element_compatible'
|
||||
// CHECK-NEXT: ImplicitConceptSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc>
|
||||
// CHECK-NEXT: TemplateArgument type 'type-parameter-0-0'
|
||||
// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'type-parameter-0-0' dependent depth 0 index 0
|
||||
// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} ''
|
||||
// CHECK-NEXT: TemplateArgument type 'element_type':'type-parameter-0-0'
|
||||
// CHECK-NEXT: TemplateTypeParmType 0x{{[0-9A-Fa-f]+}} 'element_type' dependent depth 0 index 0
|
||||
// CHECK-NEXT: TemplateTypeParm 0x{{[0-9A-Fa-f]+}} 'element_type'
|
||||
// CHECK-NEXT: CXXRecordDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> implicit class [[RESOURCE]] definition
|
||||
|
||||
// CHECK: FinalAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit final
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -ast-dump-filter=__is_structured_resource_element_compatible %s | FileCheck %s
|
||||
|
||||
// CHECK: ConceptDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> __is_structured_resource_element_compatible
|
||||
// CHECK: |-TemplateTypeParmDecl 0x{{[0-9a-f]+}} <<invalid sloc>> <invalid sloc> referenced typename depth 0 index 0 element_type
|
||||
// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '&&'
|
||||
// CHECK: |-UnaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue prefix '!' cannot overflow
|
||||
// CHECK: | `-TypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' __builtin_hlsl_is_intangible
|
||||
// CHECK: | `-TemplateTypeParmType 0x{{[0-9a-f]+}} 'element_type' dependent depth 0 index 0
|
||||
// CHECK: | `-TemplateTypeParm 0x{{[0-9a-f]+}} 'element_type'
|
||||
// CHECK: `-BinaryOperator 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' lvalue '>='
|
||||
// CHECK: |-UnaryExprOrTypeTraitExpr 0x{{[0-9a-f]+}} <<invalid sloc>> 'bool' sizeof 'element_type'
|
||||
// CHECK: `-IntegerLiteral 0x{{[0-9a-f]+}} <<invalid sloc>> 'unsigned long' 1
|
||||
|
||||
|
||||
StructuredBuffer<float> Buffer;
|
||||
@ -5,13 +5,25 @@ typedef vector<float, 3> float3;
|
||||
StructuredBuffer<float3> Buffer;
|
||||
|
||||
// expected-error@+2 {{class template 'StructuredBuffer' requires template arguments}}
|
||||
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> class StructuredBuffer {}}}
|
||||
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_structured_resource_element_compatible<element_type> class StructuredBuffer {}}}
|
||||
StructuredBuffer BufferErr1;
|
||||
|
||||
// expected-error@+2 {{too few template arguments for class template 'StructuredBuffer'}}
|
||||
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> class StructuredBuffer {}}}
|
||||
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_structured_resource_element_compatible<element_type> class StructuredBuffer {}}}
|
||||
StructuredBuffer<> BufferErr2;
|
||||
|
||||
// test elements of 0 size
|
||||
// expected-error@+3{{constraints not satisfied for class template 'StructuredBuffer' [with element_type = int[0]]}}
|
||||
// expected-note@*:*{{because 'int[0]' does not satisfy '__is_structured_resource_element_compatible'}}
|
||||
// expected-note@*:*{{because 'sizeof(int[0]) >= 1UL' (0 >= 1) evaluated to false}}
|
||||
StructuredBuffer<int[0]> BufferErr3;
|
||||
|
||||
// In C++, empty structs do have a size of 1. So should HLSL.
|
||||
// The concept will accept empty structs as element types, despite it being unintuitive.
|
||||
struct Empty {};
|
||||
StructuredBuffer<Empty> BufferErr4;
|
||||
|
||||
|
||||
[numthreads(1,1,1)]
|
||||
void main() {
|
||||
(void)Buffer.__handle; // expected-error {{'__handle' is a private member of 'hlsl::StructuredBuffer<vector<float, 3>>'}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user