[clang] Implement resource binding type prefix mismatch diagnostic infrastructure (PR #97103)
Joshua Batista via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 22 15:31:03 PDT 2024
https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/97103
>From 3af2a39250f751021ef1756ad09994e1b02ddc72 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 28 Jun 2024 12:40:56 -0700
Subject: [PATCH 01/43] update tests, update code
---
clang/include/clang/Basic/Attr.td | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 5 +-
clang/include/clang/Sema/SemaHLSL.h | 2 +
clang/lib/Sema/HLSLExternalSemaSource.cpp | 23 +-
clang/lib/Sema/SemaHLSL.cpp | 231 +++++++++++++++++-
.../ast-dump-comment-cbuffe-tbufferr.hlsl | 2 +
clang/test/AST/HLSL/cbuffer_tbuffer.hlsl | 6 +-
clang/test/AST/HLSL/packoffset.hlsl | 1 +
clang/test/AST/HLSL/pch_hlsl_buffer.hlsl | 2 +
.../test/AST/HLSL/resource_binding_attr.hlsl | 6 +-
.../SemaHLSL/resource_binding_attr_error.hlsl | 21 +-
.../resource_binding_attr_error_mismatch.hlsl | 74 ++++++
12 files changed, 344 insertions(+), 31 deletions(-)
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 10a9d9e899e007..cf3fff94711f16 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4529,7 +4529,7 @@ def HLSLSV_GroupIndex: HLSLAnnotationAttr {
def HLSLResourceBinding: InheritableAttr {
let Spellings = [HLSLAnnotation<"register">];
- let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar]>;
+ let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar], ErrorDiag>;
let LangOpts = [HLSL];
let Args = [StringArgument<"Slot">, StringArgument<"Space", 1>];
let Documentation = [HLSLResourceBindingDocs];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c710c3360be7da..6f9655f0fe90f5 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12342,7 +12342,10 @@ def err_hlsl_missing_semantic_annotation : Error<
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
-def err_hlsl_unsupported_register_type : Error<"invalid resource class specifier '%0' used; expected 'b', 's', 't', or 'u'">;
+def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
+def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
+def err_hlsl_unsupported_register_prefix : Error<"invalid resource class specifier '%0' used; expected 't', 'u', 'b', or 's'">;
+def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index d60cb2a57d4918..13e9f7324345d6 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -22,6 +22,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaBase.h"
+#include "clang/Sema/Sema.h"
#include <initializer_list>
namespace clang {
@@ -31,6 +32,7 @@ class SemaHLSL : public SemaBase {
public:
SemaHLSL(Sema &S);
+ HLSLResourceAttr *mergeHLSLResourceAttr(bool CBuffer);
Decl *ActOnStartBuffer(Scope *BufferScope, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *Ident, SourceLocation IdentLoc,
SourceLocation LBrace);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 89a0e391920cc6..14845a96b5297b 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -490,9 +490,8 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
}
/// Set up common members and attributes for buffer types
-static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
- ResourceClass RC, ResourceKind RK,
- bool IsROV) {
+static BuiltinTypeDeclBuilder setupBufferHandle(CXXRecordDecl *Decl, Sema &S,
+ ResourceClass RC) {
return BuiltinTypeDeclBuilder(Decl)
.addHandleMember(RC, RK, IsROV)
.addDefaultHandleConstructor(S, RC);
@@ -500,12 +499,15 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
CXXRecordDecl *Decl;
- Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
- .addSimpleTemplateParams(*SemaPtr, {"element_type"})
- .Record;
+ Decl =
+ BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
+ .addSimpleTemplateParams(*SemaPtr, {"element_type"})
+ .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
+ /*IsROV=*/false)
+ .Record;
+
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
- setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
- ResourceKind::TypedBuffer, /*IsROV=*/false)
+ setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
.addArraySubscriptOperators()
.completeDefinition();
});
@@ -513,10 +515,11 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
.addSimpleTemplateParams(*SemaPtr, {"element_type"})
+ .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
+ /*IsROV=*/true)
.Record;
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
- setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
- ResourceKind::TypedBuffer, /*IsROV=*/true)
+ setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
.addArraySubscriptOperators()
.completeDefinition();
});
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e3e926465e799e..25a399fb6b5840 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -29,6 +29,25 @@ using namespace clang;
SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
+HLSLResourceAttr *SemaHLSL::mergeHLSLResourceAttr(bool CBuffer) {
+ // cbuffer case
+ if (CBuffer) {
+ HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+ getASTContext(), llvm::hlsl::ResourceClass::CBuffer,
+ llvm::hlsl::ResourceKind::CBuffer,
+ /*IsROV=*/false);
+ return attr;
+ }
+ // tbuffer case
+ else {
+ HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+ getASTContext(), llvm::hlsl::ResourceClass::SRV,
+ llvm::hlsl::ResourceKind::TBuffer,
+ /*IsROV=*/false);
+ return attr;
+ }
+}
+
Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *Ident,
SourceLocation IdentLoc,
@@ -38,6 +57,10 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
HLSLBufferDecl *Result = HLSLBufferDecl::Create(
getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
+ HLSLResourceAttr *NewAttr = mergeHLSLResourceAttr(CBuffer);
+ if (NewAttr)
+ Result->addAttr(NewAttr);
+
SemaRef.PushOnScopeChains(Result, BufferScope);
SemaRef.PushDeclContext(BufferScope, Result);
@@ -459,7 +482,205 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
D->addAttr(HLSLResourceClassAttr::Create(getASTContext(), RC, ArgLoc));
}
+struct register_binding_flags {
+ bool resource = false;
+ bool udt = false;
+ bool other = false;
+ bool basic = false;
+
+ bool srv = false;
+ bool uav = false;
+ bool cbv = false;
+ bool sampler = false;
+
+ bool contains_numeric = false;
+ bool default_globals = false;
+};
+
+bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
+ if (!decl)
+ return false;
+
+ // Traverse up the parent contexts
+ const DeclContext *context = decl->getDeclContext();
+ while (context) {
+ if (isa<HLSLBufferDecl>(context)) {
+ return true;
+ }
+ context = context->getParent();
+ }
+
+ return false;
+}
+
+const HLSLResourceAttr *
+getHLSLResourceAttrFromVarDecl(VarDecl *SamplerUAVOrSRV) {
+ const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+ if (!Ty)
+ llvm_unreachable("Resource class must have an element type.");
+
+ if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+ /* QualType QT = SamplerUAVOrSRV->getType();
+ PrintingPolicy PP = S.getPrintingPolicy();
+ std::string typestr = QualType::getAsString(QT.split(), PP);
+
+ S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
+ << typestr;
+ return; */
+ return nullptr;
+ }
+
+ const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+ if (!TheRecordDecl)
+ llvm_unreachable("Resource class should have a resource type declaration.");
+
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
+ TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ return Attr;
+}
+
+void traverseType(QualType T, register_binding_flags &r) {
+ if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
+ r.contains_numeric = true;
+ return;
+ } else if (const RecordType *RT = T->getAs<RecordType>()) {
+ RecordDecl *SubRD = RT->getDecl();
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
+ auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+ switch (DeclResourceClass) {
+ case llvm::hlsl::ResourceClass::SRV: {
+ r.srv = true;
+ break;
+ }
+ case llvm::hlsl::ResourceClass::UAV: {
+ r.uav = true;
+ break;
+ }
+ case llvm::hlsl::ResourceClass::CBuffer: {
+ r.cbv = true;
+ break;
+ }
+ case llvm::hlsl::ResourceClass::Sampler: {
+ r.sampler = true;
+ break;
+ }
+ }
+ }
+
+ else if (SubRD->isCompleteDefinition()) {
+ for (auto Field : SubRD->fields()) {
+ QualType T = Field->getType();
+ traverseType(T, r);
+ }
+ }
+ }
+}
+
+void setResourceClassFlagsFromRecordDecl(register_binding_flags &r,
+ const RecordDecl *RD) {
+ if (!RD)
+ return;
+
+ if (RD->isCompleteDefinition()) {
+ for (auto Field : RD->fields()) {
+ QualType T = Field->getType();
+ traverseType(T, r);
+ }
+ }
+}
+
+register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
+ register_binding_flags r;
+ if (!isDeclaredWithinCOrTBuffer(D)) {
+ // make sure the type is a basic / numeric type
+ if (VarDecl *v = dyn_cast<VarDecl>(D)) {
+ QualType t = v->getType();
+ // a numeric variable will inevitably end up in $Globals buffer
+ if (t->isIntegralType(S.getASTContext()) || t->isFloatingType())
+ r.default_globals = true;
+ }
+ }
+ // Cbuffers and Tbuffers are HLSLBufferDecl types
+ HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+ // Samplers, UAVs, and SRVs are VarDecl types
+ VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+
+ if (CBufferOrTBuffer) {
+ r.resource = true;
+ if (CBufferOrTBuffer->isCBuffer())
+ r.cbv = true;
+ else
+ r.srv = true;
+ } else if (SamplerUAVOrSRV) {
+ const HLSLResourceAttr *res_attr =
+ getHLSLResourceAttrFromVarDecl(SamplerUAVOrSRV);
+ if (res_attr) {
+ llvm::hlsl::ResourceClass DeclResourceClass =
+ res_attr->getResourceClass();
+ r.resource = true;
+ switch (DeclResourceClass) {
+ case llvm::hlsl::ResourceClass::SRV: {
+ r.srv = true;
+ break;
+ }
+ case llvm::hlsl::ResourceClass::UAV: {
+ r.uav = true;
+ break;
+ }
+ case llvm::hlsl::ResourceClass::CBuffer: {
+ r.cbv = true;
+ break;
+ }
+ case llvm::hlsl::ResourceClass::Sampler: {
+ r.sampler = true;
+ break;
+ }
+ }
+ } else {
+ if (SamplerUAVOrSRV->getType()->isBuiltinType())
+ r.basic = true;
+ else if (SamplerUAVOrSRV->getType()->isAggregateType()) {
+ r.udt = true;
+ QualType VarType = SamplerUAVOrSRV->getType();
+ if (const RecordType *RT = VarType->getAs<RecordType>()) {
+ const RecordDecl *RD = RT->getDecl();
+ // recurse through members, set appropriate resource class flags.
+ setResourceClassFlagsFromRecordDecl(r, RD);
+ }
+ } else
+ r.other = true;
+ }
+ } else {
+ llvm_unreachable("unknown decl type");
+ }
+ return r;
+}
+
+static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
+ Decl *D, StringRef &Slot) {
+
+ register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
+ // Samplers, UAVs, and SRVs are VarDecl types
+ VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+ // Cbuffers and Tbuffers are HLSLBufferDecl types
+ HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+ if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
+ return;
+
+ // TODO: emit diagnostic code based on the flags set in f.
+}
+
void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
+ if (dyn_cast<VarDecl>(D)) {
+ if (SemaRef.RequireCompleteType(D->getBeginLoc(), cast<ValueDecl>(D)->getType(),
+ diag::err_incomplete_type))
+ return;
+ }
StringRef Space = "space0";
StringRef Slot = "";
@@ -492,13 +713,15 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
// Validate.
if (!Slot.empty()) {
switch (Slot[0]) {
+ case 't':
case 'u':
case 'b':
case 's':
- case 't':
+ case 'c':
+ case 'i':
break;
default:
- Diag(ArgLoc, diag::err_hlsl_unsupported_register_type)
+ Diag(ArgLoc, diag::err_hlsl_unsupported_register_prefix)
<< Slot.substr(0, 1);
return;
}
@@ -522,8 +745,8 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
return;
}
- // FIXME: check reg type match decl. Issue
- // https://github.com/llvm/llvm-project/issues/57886.
+ DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, D, Slot);
+
HLSLResourceBindingAttr *NewAttr =
HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
if (NewAttr)
diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
index a98dc0f4ce4312..727d505471bcb7 100644
--- a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
+++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
@@ -38,12 +38,14 @@ tbuffer B {
}
// AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
// AST-NEXT:FullComment {{.*}}<line:10:4, col:17>
// AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
// AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
// AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
// AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
// AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
// AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
// AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
// AST-NEXT: `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
index 7204dcd16e0a92..a67688d510ea64 100644
--- a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
+++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
@@ -1,12 +1,14 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:5:9 cbuffer CB
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
cbuffer CB {
float a;
}
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:11:9 tbuffer TB
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
tbuffer TB {
float b;
diff --git a/clang/test/AST/HLSL/packoffset.hlsl b/clang/test/AST/HLSL/packoffset.hlsl
index 060288c2f7f76c..1dc99620c55c40 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,6 +4,7 @@
// CHECK: HLSLBufferDecl {{.*}} cbuffer A
cbuffer A
{
+ // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> line:5:9 cbuffer A
// CHECK-NEXT: VarDecl {{.*}} A1 'float4'
// CHECK-NEXT: HLSLPackOffsetAttr {{.*}} 0 0
float4 A1 : packoffset(c);
diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
index e9a6ea1a16312c..e264241e62e924 100644
--- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
+++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
@@ -17,8 +17,10 @@ float foo() {
}
// Make sure cbuffer/tbuffer works for PCH.
// CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported <undeserialized declarations> cbuffer A
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
// CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] <line:8:3, col:9> col:9 imported used a 'float'
// CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} <line:11:1, line:13:1> line:11:9 imported <undeserialized declarations> tbuffer B
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
// CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] <line:12:3, col:9> col:9 imported used b 'float'
// CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} <line:15:1, line:17:1> line:15:7 imported foo 'float ()'
// CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} <col:13, line:17:1>
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index 71900f2dbda550..9752494f5adc9b 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -1,13 +1,15 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer CBuffer
// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "b3" "space2"
// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
cbuffer CB : register(b3, space2) {
float a;
}
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV TBuffer
// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "t2" "space1"
// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
tbuffer TB : register(t2, space1) {
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 2f8aa098db701a..58a1f3f1f64d75 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-// expected-error at +1 {{invalid resource class specifier 'c' used; expected 'b', 's', 't', or 'u'}}
+// FIXME: emit a diagnostic because float doesn't match the 'c' register type
float a : register(c0, space1);
-// expected-error at +1 {{invalid resource class specifier 'i' used; expected 'b', 's', 't', or 'u'}}
+// FIXME: emit a diagnostic because cbuffer doesn't match the 'i' register type
cbuffer b : register(i0) {
}
@@ -33,28 +33,27 @@ cbuffer C : register(b 2) {}
// expected-error at +1 {{wrong argument format for hlsl attribute, use space3 instead}}
cbuffer D : register(b 2, space 3) {}
-// expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
static RWBuffer<float> U : register(u5);
void foo() {
- // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+ // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<float> U : register(u3);
}
void foo2() {
- // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+ // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
extern RWBuffer<float> U2 : register(u5);
}
-// FIXME: expect-error once fix https://github.com/llvm/llvm-project/issues/57886.
+
+// FIXME: emit a diagnostic because float doesn't match the 'u' register type
float b : register(u0, space1);
-// expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
void bar(RWBuffer<float> U : register(u3)) {
}
-struct S {
- // FIXME: generate better error when support semantic on struct field.
- // See https://github.com/llvm/llvm-project/issues/57889.
- // expected-warning at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+struct S {
+ // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<float> U : register(u3);
};
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
new file mode 100644
index 00000000000000..b47171f8784365
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+
+// expected-error at +1 {{invalid register name prefix 'b' for register resource type 'RWBuffer' (expected 'u')}}
+RWBuffer<int> a : register(b2, space1);
+
+// expected-error at +1 {{invalid register name prefix 't' for register resource type 'RWBuffer' (expected 'u')}}
+RWBuffer<int> b : register(t2, space1);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
+// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCube <float> t8 : register(b8);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
+
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
+// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2 : register(T2);
+
+// expected-error at +1 {{invalid register name prefix 's' for register resource type 'cbuffer' (expected 'b')}}
+cbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
+// Can this type just be Sampler instead of SamplerState?
+// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
+
+// expected-error at +1 {{invalid register name prefix 's' for register resource type 'tbuffer' (expected 't')}}
+tbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
+
+
+// empty binding prefix cases:
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> c: register();
+
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> d: register("");
>From 9119de0d6cc99b1a11440c049417789cbfd19cf4 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 28 Jun 2024 16:18:43 -0700
Subject: [PATCH 02/43] remove mismatch test, will be applied in step 2 PR.
update packoffset
---
clang/test/AST/HLSL/packoffset.hlsl | 2 +-
.../resource_binding_attr_error_mismatch.hlsl | 74 -------------------
2 files changed, 1 insertion(+), 75 deletions(-)
delete mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
diff --git a/clang/test/AST/HLSL/packoffset.hlsl b/clang/test/AST/HLSL/packoffset.hlsl
index 1dc99620c55c40..9deb63caa500a1 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,7 +4,7 @@
// CHECK: HLSLBufferDecl {{.*}} cbuffer A
cbuffer A
{
- // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> line:5:9 cbuffer A
+ // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
// CHECK-NEXT: VarDecl {{.*}} A1 'float4'
// CHECK-NEXT: HLSLPackOffsetAttr {{.*}} 0 0
float4 A1 : packoffset(c);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
deleted file mode 100644
index b47171f8784365..00000000000000
--- a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
+++ /dev/null
@@ -1,74 +0,0 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-
-
-// expected-error at +1 {{invalid register name prefix 'b' for register resource type 'RWBuffer' (expected 'u')}}
-RWBuffer<int> a : register(b2, space1);
-
-// expected-error at +1 {{invalid register name prefix 't' for register resource type 'RWBuffer' (expected 'u')}}
-RWBuffer<int> b : register(t2, space1);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
-// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
-// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCube <float> t8 : register(b8);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
-
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
-// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
-// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
-// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2 : register(T2);
-
-// expected-error at +1 {{invalid register name prefix 's' for register resource type 'cbuffer' (expected 'b')}}
-cbuffer f : register(s2, space1) {}
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
-// Can this type just be Sampler instead of SamplerState?
-// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
-
-// expected-error at +1 {{invalid register name prefix 's' for register resource type 'tbuffer' (expected 't')}}
-tbuffer f : register(s2, space1) {}
-
-// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
-// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
-// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
-
-
-// empty binding prefix cases:
-// expected-error at +1 {{expected identifier}}
-RWBuffer<int> c: register();
-
-// expected-error at +1 {{expected identifier}}
-RWBuffer<int> d: register("");
>From 77090ad8a0ed14a3b63da520d092a954d37ce4c7 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 28 Jun 2024 16:29:20 -0700
Subject: [PATCH 03/43] clang-format
---
clang/include/clang/Sema/SemaHLSL.h | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 11 ++++++-----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 13e9f7324345d6..d80aaff4b528b2 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -21,8 +21,8 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Scope.h"
-#include "clang/Sema/SemaBase.h"
#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaBase.h"
#include <initializer_list>
namespace clang {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 25a399fb6b5840..0318190ffc31b7 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -568,7 +568,7 @@ void traverseType(QualType T, register_binding_flags &r) {
case llvm::hlsl::ResourceClass::Sampler: {
r.sampler = true;
break;
- }
+ }
}
}
@@ -639,7 +639,7 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
case llvm::hlsl::ResourceClass::Sampler: {
r.sampler = true;
break;
- }
+ }
}
} else {
if (SamplerUAVOrSRV->getType()->isBuiltinType())
@@ -676,9 +676,10 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
- if (dyn_cast<VarDecl>(D)) {
- if (SemaRef.RequireCompleteType(D->getBeginLoc(), cast<ValueDecl>(D)->getType(),
- diag::err_incomplete_type))
+ if (dyn_cast<VarDecl>(D)) {
+ if (SemaRef.RequireCompleteType(D->getBeginLoc(),
+ cast<ValueDecl>(D)->getType(),
+ diag::err_incomplete_type))
return;
}
StringRef Space = "space0";
>From 32e2ea6345a0b07822e26d55574b704aaaae03cd Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 1 Jul 2024 10:36:02 -0700
Subject: [PATCH 04/43] remove unnecessary header
---
clang/include/clang/Sema/SemaHLSL.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index d80aaff4b528b2..99ff3ec2531a0d 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -21,7 +21,6 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Scope.h"
-#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaBase.h"
#include <initializer_list>
>From ee92ca6d703ba5b8036b09df022700565967b62e Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 1 Jul 2024 17:55:03 -0700
Subject: [PATCH 05/43] full implementation
---
.../clang/Basic/DiagnosticSemaKinds.td | 10 ++
clang/lib/Sema/SemaHLSL.cpp | 162 +++++++++++++++++-
2 files changed, 170 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6f9655f0fe90f5..0fb44a10592bd2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12342,6 +12342,16 @@ def err_hlsl_missing_semantic_annotation : Error<
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
+def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}1' on variable of type '%0'">;
+def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
+def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
+def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
+def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: %0 and %0">;
+def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">;
+def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">;
+def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">;
+def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">;
+def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
def err_hlsl_unsupported_register_prefix : Error<"invalid resource class specifier '%0' used; expected 't', 'u', 'b', or 's'">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 0318190ffc31b7..6aa727210a7dc9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -661,10 +661,26 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
return r;
}
+static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
+ StringRef &Slot) {
+ std::set<std::string> s; // store unique register type + numbers
+ for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
+
+ if (HLSLResourceBindingAttr *attr =
+ dyn_cast<HLSLResourceBindingAttr>(*it)) {
+ std::string regInfo(Slot);
+ auto p = s.insert(regInfo);
+ if (!p.second) {
+ S.Diag(attr->getLoc(), diag::err_hlsl_conflicting_register_annotations)
+ << Slot.substr(0, 1);
+ }
+ }
+ }
+}
+
static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
Decl *D, StringRef &Slot) {
- register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
// Samplers, UAVs, and SRVs are VarDecl types
VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
// Cbuffers and Tbuffers are HLSLBufferDecl types
@@ -672,7 +688,149 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
return;
- // TODO: emit diagnostic code based on the flags set in f.
+ register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
+ assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 && "only one resource analysis result should be expected");
+
+ // get the variable type
+ std::string typestr;
+ if (SamplerUAVOrSRV) {
+ QualType QT = SamplerUAVOrSRV->getType();
+ PrintingPolicy PP = S.getPrintingPolicy();
+ typestr = QualType::getAsString(QT.split(), PP);
+ } else
+ typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
+
+
+ // first, if "other" is set, emit an error
+ if (f.other) {
+ S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
+ << Slot << typestr;
+ }
+
+ // next, if multiple register annotations exist, check that none conflict.
+ ValidateMultipleRegisterAnnotations(S, D, Slot);
+
+ // next, if resource is set, make sure the register type in the register annotation
+ // is compatible with the variable's resource type.
+ if (f.resource) {
+ VarDecl *VD = dyn_cast<VarDecl>(D);
+
+ const HLSLResourceAttr *res_attr =
+ getHLSLResourceAttrFromVarDecl(VD);
+ assert(res_attr && "any decl that set the resource flag on analysis should have a resource attribute attached.");
+ llvm::hlsl::ResourceClass DeclResourceClass = res_attr->getResourceClass();
+ switch (DeclResourceClass) {
+ case llvm::hlsl::ResourceClass::SRV: {
+ if (!f.srv) {
+ S.Diag(
+ res_attr->getLoc(), diag::err_hlsl_mismatching_register_type_and_resource_type)
+ << typestr << Slot[0] << 0 /*srv*/;
+ }
+ break;
+ }
+ case llvm::hlsl::ResourceClass::UAV: {
+ if (!f.uav) {
+ S.Diag(res_attr->getLoc(),
+ diag::err_hlsl_mismatching_register_type_and_resource_type)
+ << typestr << Slot[0] << 1 /*uav*/;
+ }
+ break;
+ }
+ case llvm::hlsl::ResourceClass::CBuffer: {
+ if (!f.cbv) {
+ S.Diag(res_attr->getLoc(),
+ diag::err_hlsl_mismatching_register_type_and_resource_type)
+ << typestr << Slot[0] << 2 /*cbv*/;
+ }
+ break;
+ }
+ case llvm::hlsl::ResourceClass::Sampler: {
+ if (!f.sampler) {
+ S.Diag(res_attr->getLoc(),
+ diag::err_hlsl_mismatching_register_type_and_resource_type)
+ << typestr << Slot[0] << 3 /*sampler*/;
+ }
+ break;
+ }
+ }
+ }
+
+ // next, handle diagnostics for when the "basic" flag is set,
+ // including the legacy "i" and "b" register types.
+ if (f.basic) {
+ if (f.default_globals) {
+ if (Slot[0] == 'b')
+ S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
+ if (Slot[0] == 'i')
+ S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+ }
+
+ else if (Slot[0] == 'c') {
+ if (!f.default_globals){
+ S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
+ }
+ }
+ else if (Slot[0] == 't')
+ S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type) << 0 << typestr;
+ else if (Slot[0] == 'u')
+ S.Diag(ArgLoc,
+ diag::err_hlsl_mismatching_register_type_and_variable_type)
+ << 1 << typestr;
+ else if (Slot[0] == 's')
+ S.Diag(ArgLoc,
+ diag::err_hlsl_mismatching_register_type_and_variable_type)
+ << 3 << typestr;
+ // any other register type should emit err_hlsl_unsupported_register_type_and_variable_type
+ else {
+ S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
+ << Slot[0] << typestr;
+ }
+ }
+
+ // finally, we handle the udt case
+
+ if (f.udt) {
+ for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
+ if (HLSLResourceBindingAttr *attr =
+ dyn_cast<HLSLResourceBindingAttr>(*it)) {
+ llvm::StringRef registerTypeSlotRef = attr->getSlot();
+ std::string registerTypeSlot(
+ registerTypeSlotRef);
+ if (registerTypeSlot[0] == 't') {
+ if (!f.srv) {
+ S.Diag(attr->getLoc(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "t"
+ << 0;
+ }
+ } else if (registerTypeSlot[0] == 'u') {
+ if (!f.uav) {
+ S.Diag(attr->getLoc(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "u" << 1;
+ }
+ } else if (registerTypeSlot[0] == 'b') {
+ if (!f.cbv) {
+ S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "b" << 2;
+ }
+ } else if (registerTypeSlot[0] == 's') {
+ if (!f.sampler) {
+ S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "s" << 3;
+ }
+ } else if (registerTypeSlot[0] == 'c') {
+ if (!f.srv)
+ S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
+ }
+ else {
+ S.Diag(attr->getLoc(),
+ diag::err_hlsl_unsupported_register_type_and_variable_type)
+ << registerTypeSlot[0] << typestr;
+ }
+ }
+ }
+ }
}
void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
>From 6fa8415ea6217962f644dd8e3601f475e8e28b7f Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 1 Jul 2024 17:55:33 -0700
Subject: [PATCH 06/43] clang format
---
clang/lib/Sema/SemaHLSL.cpp | 74 ++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 38 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6aa727210a7dc9..ba738bb502a3eb 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -689,7 +689,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
return;
register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
- assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 && "only one resource analysis result should be expected");
+ assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 &&
+ "only one resource analysis result should be expected");
// get the variable type
std::string typestr;
@@ -697,33 +698,32 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
QualType QT = SamplerUAVOrSRV->getType();
PrintingPolicy PP = S.getPrintingPolicy();
typestr = QualType::getAsString(QT.split(), PP);
- } else
+ } else
typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
-
// first, if "other" is set, emit an error
- if (f.other) {
+ if (f.other) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< Slot << typestr;
}
// next, if multiple register annotations exist, check that none conflict.
ValidateMultipleRegisterAnnotations(S, D, Slot);
-
- // next, if resource is set, make sure the register type in the register annotation
- // is compatible with the variable's resource type.
+
+ // next, if resource is set, make sure the register type in the register
+ // annotation is compatible with the variable's resource type.
if (f.resource) {
VarDecl *VD = dyn_cast<VarDecl>(D);
- const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromVarDecl(VD);
- assert(res_attr && "any decl that set the resource flag on analysis should have a resource attribute attached.");
+ const HLSLResourceAttr *res_attr = getHLSLResourceAttrFromVarDecl(VD);
+ assert(res_attr && "any decl that set the resource flag on analysis should "
+ "have a resource attribute attached.");
llvm::hlsl::ResourceClass DeclResourceClass = res_attr->getResourceClass();
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV: {
if (!f.srv) {
- S.Diag(
- res_attr->getLoc(), diag::err_hlsl_mismatching_register_type_and_resource_type)
+ S.Diag(res_attr->getLoc(),
+ diag::err_hlsl_mismatching_register_type_and_resource_type)
<< typestr << Slot[0] << 0 /*srv*/;
}
break;
@@ -754,54 +754,51 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
}
}
-
+
// next, handle diagnostics for when the "basic" flag is set,
// including the legacy "i" and "b" register types.
if (f.basic) {
if (f.default_globals) {
if (Slot[0] == 'b')
- S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
+ S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
if (Slot[0] == 'i')
- S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+ S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
}
else if (Slot[0] == 'c') {
- if (!f.default_globals){
+ if (!f.default_globals) {
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
- }
- }
- else if (Slot[0] == 't')
- S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type) << 0 << typestr;
+ }
+ } else if (Slot[0] == 't')
+ S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
+ << 0 << typestr;
else if (Slot[0] == 'u')
- S.Diag(ArgLoc,
- diag::err_hlsl_mismatching_register_type_and_variable_type)
+ S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 1 << typestr;
else if (Slot[0] == 's')
- S.Diag(ArgLoc,
- diag::err_hlsl_mismatching_register_type_and_variable_type)
+ S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 3 << typestr;
- // any other register type should emit err_hlsl_unsupported_register_type_and_variable_type
+ // any other register type should emit
+ // err_hlsl_unsupported_register_type_and_variable_type
else {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< Slot[0] << typestr;
- }
+ }
}
// finally, we handle the udt case
-
+
if (f.udt) {
for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
llvm::StringRef registerTypeSlotRef = attr->getSlot();
- std::string registerTypeSlot(
- registerTypeSlotRef);
- if (registerTypeSlot[0] == 't') {
+ std::string registerTypeSlot(registerTypeSlotRef);
+ if (registerTypeSlot[0] == 't') {
if (!f.srv) {
S.Diag(attr->getLoc(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "t"
- << 0;
+ << typestr << "t" << 0;
}
} else if (registerTypeSlot[0] == 'u') {
if (!f.uav) {
@@ -811,26 +808,27 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
} else if (registerTypeSlot[0] == 'b') {
if (!f.cbv) {
- S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+ S.Diag(attr->getLoc(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
<< typestr << "b" << 2;
}
} else if (registerTypeSlot[0] == 's') {
if (!f.sampler) {
- S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_resource_type_member)
+ S.Diag(attr->getLoc(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
<< typestr << "s" << 3;
}
} else if (registerTypeSlot[0] == 'c') {
if (!f.srv)
- S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
- }
- else {
+ S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
+ } else {
S.Diag(attr->getLoc(),
diag::err_hlsl_unsupported_register_type_and_variable_type)
<< registerTypeSlot[0] << typestr;
}
}
}
- }
+ }
}
void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
>From 6cdb101736b845dfc602f660488f1920968db54c Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 2 Jul 2024 16:41:39 -0700
Subject: [PATCH 07/43] fix mismatching register type diagnostic logic
---
clang/lib/Sema/SemaHLSL.cpp | 113 ++++++++++--------
.../SemaHLSL/resource_binding_attr_error.hlsl | 7 +-
.../resource_binding_attr_error_mismatch.hlsl | 74 ++++++++++++
3 files changed, 142 insertions(+), 52 deletions(-)
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ba738bb502a3eb..38bdf72c7e3cea 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -514,31 +514,39 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
}
const HLSLResourceAttr *
-getHLSLResourceAttrFromVarDecl(VarDecl *SamplerUAVOrSRV) {
- const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
- if (!Ty)
- llvm_unreachable("Resource class must have an element type.");
+getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
+ HLSLBufferDecl *CBufferOrTBuffer) {
- if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
- /* QualType QT = SamplerUAVOrSRV->getType();
- PrintingPolicy PP = S.getPrintingPolicy();
- std::string typestr = QualType::getAsString(QT.split(), PP);
-
- S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
- << typestr;
- return; */
- return nullptr;
- }
+ if (SamplerUAVOrSRV) {
+ const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+ if (!Ty)
+ llvm_unreachable("Resource class must have an element type.");
+
+ if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+ /* QualType QT = SamplerUAVOrSRV->getType();
+ PrintingPolicy PP = S.getPrintingPolicy();
+ std::string typestr = QualType::getAsString(QT.split(), PP);
+
+ S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
+ << typestr;
+ return; */
+ return nullptr;
+ }
- const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
- if (!TheRecordDecl)
- llvm_unreachable("Resource class should have a resource type declaration.");
+ const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+ if (!TheRecordDecl)
+ llvm_unreachable(
+ "Resource class should have a resource type declaration.");
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
- TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
- TheRecordDecl = TheRecordDecl->getCanonicalDecl();
- const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
- return Attr;
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
+ TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ return Attr;
+ } else if (CBufferOrTBuffer) {
+ const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
+ return Attr;
+ }
}
void traverseType(QualType T, register_binding_flags &r) {
@@ -618,7 +626,7 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
r.srv = true;
} else if (SamplerUAVOrSRV) {
const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromVarDecl(SamplerUAVOrSRV);
+ getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
if (res_attr) {
llvm::hlsl::ResourceClass DeclResourceClass =
res_attr->getResourceClass();
@@ -685,8 +693,12 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
// Cbuffers and Tbuffers are HLSLBufferDecl types
HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+
+ // exactly one of these two types should be set
if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
return;
+ if (SamplerUAVOrSRV && CBufferOrTBuffer)
+ return;
register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 &&
@@ -701,6 +713,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
} else
typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
+ std::string registerType(Slot.substr(0, 1));
+
// first, if "other" is set, emit an error
if (f.other) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
@@ -713,42 +727,43 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
if (f.resource) {
- VarDecl *VD = dyn_cast<VarDecl>(D);
-
- const HLSLResourceAttr *res_attr = getHLSLResourceAttrFromVarDecl(VD);
+ const HLSLResourceAttr *res_attr =
+ getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
assert(res_attr && "any decl that set the resource flag on analysis should "
"have a resource attribute attached.");
- llvm::hlsl::ResourceClass DeclResourceClass = res_attr->getResourceClass();
+ const llvm::hlsl::ResourceClass DeclResourceClass =
+ res_attr->getResourceClass();
+
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV: {
- if (!f.srv) {
- S.Diag(res_attr->getLoc(),
+ if (registerType != "t") {
+ S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << Slot[0] << 0 /*srv*/;
+ << typestr << registerType << 0 /*srv*/;
}
break;
}
case llvm::hlsl::ResourceClass::UAV: {
- if (!f.uav) {
- S.Diag(res_attr->getLoc(),
+ if (registerType != "u") {
+ S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << Slot[0] << 1 /*uav*/;
+ << typestr << registerType << 1 /*uav*/;
}
break;
}
case llvm::hlsl::ResourceClass::CBuffer: {
- if (!f.cbv) {
- S.Diag(res_attr->getLoc(),
+ if (registerType != "b") {
+ S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << Slot[0] << 2 /*cbv*/;
+ << typestr << registerType << 2 /*cbv*/;
}
break;
}
case llvm::hlsl::ResourceClass::Sampler: {
- if (!f.sampler) {
- S.Diag(res_attr->getLoc(),
+ if (registerType != "s") {
+ S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << Slot[0] << 3 /*sampler*/;
+ << typestr << registerType << 3 /*sampler*/;
}
break;
}
@@ -769,18 +784,18 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
if (!f.default_globals) {
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
}
- } else if (Slot[0] == 't')
+ } else if (Slot[0] == 't') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 0 << typestr;
- else if (Slot[0] == 'u')
+ } else if (Slot[0] == 'u') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 1 << typestr;
- else if (Slot[0] == 's')
+ } else if (Slot[0] == 's') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 3 << typestr;
// any other register type should emit
// err_hlsl_unsupported_register_type_and_variable_type
- else {
+ } else {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< Slot[0] << typestr;
}
@@ -794,37 +809,37 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
dyn_cast<HLSLResourceBindingAttr>(*it)) {
llvm::StringRef registerTypeSlotRef = attr->getSlot();
std::string registerTypeSlot(registerTypeSlotRef);
- if (registerTypeSlot[0] == 't') {
+ if (registerTypeSlot.front() == 't') {
if (!f.srv) {
S.Diag(attr->getLoc(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< typestr << "t" << 0;
}
- } else if (registerTypeSlot[0] == 'u') {
+ } else if (registerTypeSlot.front() == 'u') {
if (!f.uav) {
S.Diag(attr->getLoc(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< typestr << "u" << 1;
}
- } else if (registerTypeSlot[0] == 'b') {
+ } else if (registerTypeSlot.front() == 'b') {
if (!f.cbv) {
S.Diag(attr->getLoc(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< typestr << "b" << 2;
}
- } else if (registerTypeSlot[0] == 's') {
+ } else if (registerTypeSlot.front() == 's') {
if (!f.sampler) {
S.Diag(attr->getLoc(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< typestr << "s" << 3;
}
- } else if (registerTypeSlot[0] == 'c') {
+ } else if (registerTypeSlot.front() == 'c') {
if (!f.srv)
S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
} else {
S.Diag(attr->getLoc(),
diag::err_hlsl_unsupported_register_type_and_variable_type)
- << registerTypeSlot[0] << typestr;
+ << registerTypeSlot.front() << typestr;
}
}
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 58a1f3f1f64d75..ce81574a796082 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -1,9 +1,10 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-// FIXME: emit a diagnostic because float doesn't match the 'c' register type
-float a : register(c0, space1);
+// valid, The register keyword in this statement isn't binding a resource, rather it is
+// specifying a constant register binding offset within the $Globals cbuffer, which is legacy behavior from DX9.
+float a : register(c0);
-// FIXME: emit a diagnostic because cbuffer doesn't match the 'i' register type
+// expected-error at +1 {{cbv type 'cbuffer' requires register type 'b', but register type 'i' was used}}
cbuffer b : register(i0) {
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
new file mode 100644
index 00000000000000..54e852483f832b
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+
+// expected-error at +1 {{uav type 'RWBuffer<int>' requires register type 'u', but register type 'b' was used}}
+RWBuffer<int> a : register(b2, space1);
+
+// expected-error at +1 {{uav type 'RWBuffer<int>' requires register type 'u', but register type 't' was used}}
+RWBuffer<int> b : register(t2, space1);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
+// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCube <float> t8 : register(b8);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
+
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
+// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
+// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
+// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
+// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2 : register(T2);
+
+// expected-error at +1 {{cbv type 'cbuffer' requires register type 'b', but register type 's' was used}}
+cbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
+// Can this type just be Sampler instead of SamplerState?
+// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
+
+// expected-error at +1 {{srv type 'tbuffer' requires register type 't', but register type 's' was used}}
+tbuffer f : register(s2, space1) {}
+
+// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
+
+// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
+// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
+
+
+// empty binding prefix cases:
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> c: register();
+
+// expected-error at +1 {{expected identifier}}
+RWBuffer<int> d: register("");
>From be6ab253c5a15eeed43b11f40d07c8b07ba795ed Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 2 Jul 2024 18:09:06 -0700
Subject: [PATCH 08/43] add udt tests
---
clang/lib/Sema/SemaHLSL.cpp | 133 ++++++++++--------
...resource_binding_attr_error_resource.hlsl} | 2 +
.../resource_binding_attr_error_udt.hlsl | 89 ++++++++++++
3 files changed, 163 insertions(+), 61 deletions(-)
rename clang/test/SemaHLSL/{resource_binding_attr_error_mismatch.hlsl => resource_binding_attr_error_resource.hlsl} (94%)
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 38bdf72c7e3cea..53bd69166a67f9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -513,40 +513,47 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
return false;
}
+const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
+ const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+ if (!Ty)
+ llvm_unreachable("Resource class must have an element type.");
+
+ if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+ /* QualType QT = SamplerUAVOrSRV->getType();
+ PrintingPolicy PP = S.getPrintingPolicy();
+ std::string typestr = QualType::getAsString(QT.split(), PP);
+
+ S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
+ << typestr;
+ return; */
+ return nullptr;
+ }
+
+ const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+ if (!TheRecordDecl)
+ llvm_unreachable("Resource class should have a resource type declaration.");
+
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
+ TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+ return TheRecordDecl;
+}
+
const HLSLResourceAttr *
getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
HLSLBufferDecl *CBufferOrTBuffer) {
if (SamplerUAVOrSRV) {
- const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
- if (!Ty)
- llvm_unreachable("Resource class must have an element type.");
-
- if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
- /* QualType QT = SamplerUAVOrSRV->getType();
- PrintingPolicy PP = S.getPrintingPolicy();
- std::string typestr = QualType::getAsString(QT.split(), PP);
-
- S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
- << typestr;
- return; */
- return nullptr;
- }
-
- const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
- if (!TheRecordDecl)
- llvm_unreachable(
- "Resource class should have a resource type declaration.");
-
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
- TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
- TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+ const CXXRecordDecl *TheRecordDecl =
+ getRecordDeclFromVarDecl(SamplerUAVOrSRV);
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
return Attr;
} else if (CBufferOrTBuffer) {
const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
return Attr;
}
+ llvm_unreachable("one of the two conditions should be true.");
+ return nullptr;
}
void traverseType(QualType T, register_binding_flags &r) {
@@ -802,46 +809,50 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
// finally, we handle the udt case
-
if (f.udt) {
- for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
- if (HLSLResourceBindingAttr *attr =
- dyn_cast<HLSLResourceBindingAttr>(*it)) {
- llvm::StringRef registerTypeSlotRef = attr->getSlot();
- std::string registerTypeSlot(registerTypeSlotRef);
- if (registerTypeSlot.front() == 't') {
- if (!f.srv) {
- S.Diag(attr->getLoc(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "t" << 0;
- }
- } else if (registerTypeSlot.front() == 'u') {
- if (!f.uav) {
- S.Diag(attr->getLoc(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "u" << 1;
- }
- } else if (registerTypeSlot.front() == 'b') {
- if (!f.cbv) {
- S.Diag(attr->getLoc(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "b" << 2;
- }
- } else if (registerTypeSlot.front() == 's') {
- if (!f.sampler) {
- S.Diag(attr->getLoc(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "s" << 3;
- }
- } else if (registerTypeSlot.front() == 'c') {
- if (!f.srv)
- S.Diag(attr->getLoc(), diag::warn_hlsl_UDT_missing_basic_type);
- } else {
- S.Diag(attr->getLoc(),
- diag::err_hlsl_unsupported_register_type_and_variable_type)
- << registerTypeSlot.front() << typestr;
- }
+ switch (Slot[0]) {
+ case 't': {
+ if (!f.srv) {
+ S.Diag(D->getLocation(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "t" << 0;
+ }
+ break;
+ }
+ case 'u': {
+ if (!f.uav) {
+ S.Diag(D->getLocation(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "u" << 1;
+ }
+ break;
+ }
+ case 'b': {
+ if (!f.cbv) {
+ S.Diag(D->getLocation(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "b" << 2;
}
+ break;
+ }
+ case 's': {
+ if (!f.sampler) {
+ S.Diag(D->getLocation(),
+ diag::warn_hlsl_UDT_missing_resource_type_member)
+ << typestr << "s" << 3;
+ }
+ break;
+ }
+ case 'c': {
+ if (!f.srv)
+ S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
+ break;
+ }
+ default: {
+ S.Diag(D->getLocation(),
+ diag::err_hlsl_unsupported_register_type_and_variable_type)
+ << Slot.front() << typestr;
+ }
}
}
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
similarity index 94%
rename from clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
rename to clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index 54e852483f832b..2be78b44314993 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_mismatch.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+// This test validates the diagnostics that are emitted when a variable with a "resource" type
+// is bound to a register using the register annotation
// expected-error at +1 {{uav type 'RWBuffer<int>' requires register type 'u', but register type 'b' was used}}
RWBuffer<int> a : register(b2, space1);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
new file mode 100644
index 00000000000000..2f4d20b40a0fd3
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+// TODO: Implement "Buffer"
+struct Eg1 {
+ float f;
+ // Buffer<float> Buf;
+ RWBuffer<float> RWBuf;
+ };
+Eg1 e1 : /* register(t0) :*/ register(u0);
+// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0
+
+
+struct Eg2 {
+ float f;
+ // Buffer<float> Buf;
+ RWBuffer<float> RWBuf;
+ RWBuffer<float> RWBuf2;
+ };
+Eg2 e2 : /* register(t0) :*/ register(u0);
+// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0.
+// RWBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
+
+/*
+struct Eg3 {
+ float f;
+ // Buffer<float> Buf;
+ };
+Eg3 e3 : register(t0) : register(u0);
+// Valid: Buf gets bound to t0. Buf will also be bound to u0.
+*/
+
+struct Eg4 {
+ struct Bar {
+ RWBuffer<int> a;
+ };
+ Bar b;
+};
+Eg4 e4 : register(u0);
+// Valid: Bar, the struct within Eg4, has a valid resource that can be bound to t0.
+
+/* Light up this test when SamplerState is implemented
+struct Eg5 {
+ SamplerState s[3];
+};
+
+Eg5 e5 : register(s5);
+// Valid: the first sampler state object within Eg5's s is bound to slot 5
+*/
+
+struct Eg6 {
+ float f;
+};
+// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg6 e6 : register(t0);
+
+struct Eg7 {
+ struct Bar {
+ float f;
+ };
+ Bar b;
+};
+// expected-warning at +1{{variable of type 'Eg7' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg7 e7 : register(t0);
+
+struct Eg8 {
+ RWBuffer<int> a;
+};
+// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
+Eg8 e8 : register(c0);
+
+
+struct Eg9{
+ // expected-error at +1{{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+ RWBuffer<int> a : register(u9);
+};
+
+Eg9 e9;
+
+
+/* Light up this test when Texture2D is implemented
+template<typename R>
+struct Eg10 {
+ R b;
+};
+// expecting warning: {{variable of type 'Eg10' bound to register type 'u' does not contain a matching 'uav' resource}}
+Eg10<Texture2D> t : register(u0);
+
+// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
+*/
>From 5280128dbf6cfdbf0635835f6be4fde9a400147f Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 2 Jul 2024 19:33:44 -0700
Subject: [PATCH 09/43] add warnings to warning-flags.c file, do a nullptr
check
---
clang/lib/Sema/SemaHLSL.cpp | 2 ++
clang/test/Misc/warning-flags.c | 5 +++++
2 files changed, 7 insertions(+)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 53bd69166a67f9..0319749352a636 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -546,6 +546,8 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
if (SamplerUAVOrSRV) {
const CXXRecordDecl *TheRecordDecl =
getRecordDeclFromVarDecl(SamplerUAVOrSRV);
+ if (!TheRecordDecl)
+ return nullptr;
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
return Attr;
} else if (CBufferOrTBuffer) {
diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c
index cdbe1e95cba965..cdbb05b6d32a4b 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -53,6 +53,11 @@ CHECK-NEXT: warn_fe_backend_unsupported
CHECK-NEXT: warn_fe_cc_log_diagnostics_failure
CHECK-NEXT: warn_fe_cc_print_header_failure
CHECK-NEXT: warn_fe_macro_contains_embedded_newline
+CHECK-NEXT: warn_hlsl_UDT_missing_basic_type
+CHECK-NEXT: warn_hlsl_UDT_missing_resource_type_member
+CHECK-NEXT: warn_hlsl_deprecated_register_type_b
+CHECK-NEXT: warn_hlsl_deprecated_register_type_i
+CHECK-NEXT: warn_hlsl_register_type_c_not_in_global_scope
CHECK-NEXT: warn_ignoring_ftabstop_value
CHECK-NEXT: warn_implements_nscopying
CHECK-NEXT: warn_incompatible_qualified_id
>From c160e6db2780920526822a66cb59f90b0e54883d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 3 Jul 2024 01:51:15 -0700
Subject: [PATCH 10/43] clang format
---
clang/lib/Sema/SemaHLSL.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 0319749352a636..ca78c24954a89d 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -682,10 +682,9 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
StringRef &Slot) {
std::set<std::string> s; // store unique register type + numbers
for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
-
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
- std::string regInfo(Slot);
+ std::string regInfo(Slot);
auto p = s.insert(regInfo);
if (!p.second) {
S.Diag(attr->getLoc(), diag::err_hlsl_conflicting_register_annotations)
@@ -802,8 +801,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
} else if (Slot[0] == 's') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 3 << typestr;
- // any other register type should emit
- // err_hlsl_unsupported_register_type_and_variable_type
+ // any other register type should emit
+ // err_hlsl_unsupported_register_type_and_variable_type
} else {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< Slot[0] << typestr;
>From 856a1fbc1cfd1cebe7adddfea0ccdf461eb58096 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 5 Jul 2024 13:58:25 -0700
Subject: [PATCH 11/43] allow multiple reg annotations, complete udt tests
---
.../clang/Basic/DiagnosticSemaKinds.td | 3 +-
clang/include/clang/Parse/Parser.h | 4 +-
clang/lib/Parse/ParseDecl.cpp | 3 +-
clang/lib/Sema/SemaHLSL.cpp | 38 +++++++++++++++----
.../resource_binding_attr_error_udt.hlsl | 17 ++++++++-
5 files changed, 53 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0fb44a10592bd2..8055a92f0a9953 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12346,7 +12346,7 @@ def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported res
def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
-def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: %0 and %0">;
+def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register numbers detected for register type '%0'">;
def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">;
def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">;
def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">;
@@ -12354,7 +12354,6 @@ def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' b
def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
-def err_hlsl_unsupported_register_prefix : Error<"invalid resource class specifier '%0' used; expected 't', 'u', 'b', or 's'">;
def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 99a0b0200fa06f..a7513069ff5da0 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3021,7 +3021,7 @@ class Parser : public CodeCompletionHandler {
SemaCodeCompletion::AttributeCompletion::None,
const IdentifierInfo *EnclosingScope = nullptr);
- void MaybeParseHLSLAnnotations(Declarator &D,
+ bool MaybeParseHLSLAnnotations(Declarator &D,
SourceLocation *EndLoc = nullptr,
bool CouldBeBitField = false) {
assert(getLangOpts().HLSL && "MaybeParseHLSLAnnotations is for HLSL only");
@@ -3029,7 +3029,9 @@ class Parser : public CodeCompletionHandler {
ParsedAttributes Attrs(AttrFactory);
ParseHLSLAnnotations(Attrs, EndLoc, CouldBeBitField);
D.takeAttributes(Attrs);
+ return true;
}
+ return false;
}
void MaybeParseHLSLAnnotations(ParsedAttributes &Attrs,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index a8a9d3f3f5b088..9cad3bb4e6c8a2 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2326,7 +2326,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
}
if (getLangOpts().HLSL)
- MaybeParseHLSLAnnotations(D);
+ while (MaybeParseHLSLAnnotations(D))
+ ;
if (Tok.is(tok::kw_requires))
ParseTrailingRequiresClause(D);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ca78c24954a89d..71eafed3527a67 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -680,18 +680,38 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
StringRef &Slot) {
- std::set<std::string> s; // store unique register type + numbers
+ // make sure that there are no register annotations applied to the decl
+ // with the same register type but different numbers
+ std::unordered_map<char, std::set<char>>
+ s; // store unique register type + numbers
+ std::set<char> starting_set = {Slot[1]};
+ s.insert(std::make_pair(Slot[0], starting_set));
for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
- std::string regInfo(Slot);
- auto p = s.insert(regInfo);
- if (!p.second) {
- S.Diag(attr->getLoc(), diag::err_hlsl_conflicting_register_annotations)
- << Slot.substr(0, 1);
+ std::string otherSlot(attr->getSlot().data());
+
+ // insert into hash map
+ if (s.find(otherSlot[0]) != s.end()) {
+ // if the register type is already in the map, insert the number
+ // into the set (if it's not already there
+ s[otherSlot[0]].insert(otherSlot[1]);
+ } else {
+ // if the register type is not in the map, insert it with the number
+ std::set<char> otherSet;
+ otherSet.insert(otherSlot[1]);
+ s.insert(std::make_pair(otherSlot[0], otherSet));
}
}
}
+
+ for (auto regType : s) {
+ if (regType.second.size() > 1) {
+ std::string regTypeStr(1, regType.first);
+ S.Diag(D->getLocation(), diag::err_hlsl_conflicting_register_annotations)
+ << regTypeStr;
+ }
+ }
}
static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
@@ -727,6 +747,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
if (f.other) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< Slot << typestr;
+ return;
}
// next, if multiple register annotations exist, check that none conflict.
@@ -776,6 +797,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
break;
}
}
+ return;
}
// next, handle diagnostics for when the "basic" flag is set,
@@ -807,6 +829,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< Slot[0] << typestr;
}
+ return;
}
// finally, we handle the udt case
@@ -855,6 +878,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
<< Slot.front() << typestr;
}
}
+ return;
}
}
@@ -905,7 +929,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
case 'i':
break;
default:
- Diag(ArgLoc, diag::err_hlsl_unsupported_register_prefix)
+ Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_resource_type)
<< Slot.substr(0, 1);
return;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 2f4d20b40a0fd3..28a747b965b226 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -83,7 +83,22 @@ struct Eg10 {
R b;
};
// expecting warning: {{variable of type 'Eg10' bound to register type 'u' does not contain a matching 'uav' resource}}
-Eg10<Texture2D> t : register(u0);
+Eg10<Texture2D> e10 : register(u0);
// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
*/
+
+struct Eg11{
+ RWBuffer<int> a;
+ RWBuffer<int> b;
+};
+
+// expected-error at +1{{conflicting register annotations: multiple register numbers detected for register type 'u'}}
+Eg11 e11 : register(u9) : register(u10);
+
+struct Eg12{
+ RWBuffer<int> a;
+};
+
+// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
+Eg12 e12 : register(c9);
\ No newline at end of file
>From 13bad9a372e9f8db31f3f63e75a397147dd1ac4c Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 5 Jul 2024 17:01:17 -0700
Subject: [PATCH 12/43] add other test, xfail it
---
clang/include/clang/Basic/DiagnosticGroups.td | 3 ++
.../clang/Basic/DiagnosticSemaKinds.td | 8 ++---
clang/lib/Sema/SemaHLSL.cpp | 6 ++--
.../resource_binding_attr_error_basic.hlsl | 31 +++++++++++++++++++
.../resource_binding_attr_error_other.hlsl | 9 ++++++
5 files changed, 50 insertions(+), 7 deletions(-)
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 19c3f1e0433496..0702efd36366e7 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1547,6 +1547,9 @@ def DXILValidation : DiagGroup<"dxil-validation">;
// Warning for HLSL API availability
def HLSLAvailability : DiagGroup<"hlsl-availability">;
+// Warnings for legacy binding behavior
+def DisallowLegacyBindingRules : DiagGroup<"disallow-legacy-binding-rules">;
+
// Warnings and notes related to const_var_decl_type attribute checks
def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8055a92f0a9953..b308b77aeafd3a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12342,14 +12342,14 @@ def err_hlsl_missing_semantic_annotation : Error<
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
-def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}1' on variable of type '%0'">;
+def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}0' on variable of type '%1'">;
def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register numbers detected for register type '%0'">;
-def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">;
-def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">;
-def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">;
+def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">;
def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 71eafed3527a67..e0da71ec58e535 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -810,7 +810,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
}
- else if (Slot[0] == 'c') {
+ if (Slot[0] == 'c') {
if (!f.default_globals) {
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
}
@@ -825,9 +825,9 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
<< 3 << typestr;
// any other register type should emit
// err_hlsl_unsupported_register_type_and_variable_type
- } else {
+ } else if (!f.default_globals){
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot[0] << typestr;
+ << registerType << typestr;
}
return;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
new file mode 100644
index 00000000000000..64cff79274eddd
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
+
+// expected-error at +1{{unsupported resource register binding 't' on variable of type 'float'}}
+float f1 : register(t9);
+
+// expected-error at +1{{deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding}}
+float f2 : register(b9);
+
+// expected-error at +1{{deprecated legacy int constant register binding 'i' used}}
+float f3 : register(i9);
+
+
+cbuffer g_cbuffer1 {
+// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
+ float f4 : register(c2);
+};
+
+tbuffer g_tbuffer1 {
+// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
+ float f5 : register(c2);
+};
+
+tbuffer g_cbuffer2 {
+// expected-error at +1{{register binding type 'float' not supported for variable of type 'b'}}
+ float f6 : register(b2);
+};
+
+tbuffer g_tbuffer2 {
+// expected-error at +1{{register binding type 'float' not supported for variable of type 'i'}}
+ float f7 : register(i2);
+};
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl
new file mode 100644
index 00000000000000..4c9e9a6b44c928
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_other.hlsl
@@ -0,0 +1,9 @@
+// RUN: not %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s | FileCheck %s
+
+// XFAIL: *
+// This expectedly fails because RayQuery is an unsupported type.
+// When it becomes supported, we should expect an error due to
+// the variable type being classified as "other", and according
+// to the spec, err_hlsl_unsupported_register_type_and_variable_type
+// should be emitted.
+RayQuery<0> r1: register(t0);
>From a43e2f3c84fac947753ea530512f31689d0db1c9 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Sun, 7 Jul 2024 00:26:38 -0700
Subject: [PATCH 13/43] fix warning flag errs
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++--
clang/lib/Sema/SemaHLSL.cpp | 2 +-
clang/test/Misc/warning-flags.c | 5 -----
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 2 +-
4 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b308b77aeafd3a..293c7b895790f2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12350,8 +12350,8 @@ def err_hlsl_conflicting_register_annotations: Error<"conflicting register annot
def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">;
-def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">;
+def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">, InGroup<DisallowLegacyBindingRules>;
+def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">, InGroup<DisallowLegacyBindingRules>;
def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index e0da71ec58e535..d853568261c836 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -825,7 +825,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
<< 3 << typestr;
// any other register type should emit
// err_hlsl_unsupported_register_type_and_variable_type
- } else if (!f.default_globals){
+ } else if (!f.default_globals) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
<< registerType << typestr;
}
diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c
index cdbb05b6d32a4b..cdbe1e95cba965 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -53,11 +53,6 @@ CHECK-NEXT: warn_fe_backend_unsupported
CHECK-NEXT: warn_fe_cc_log_diagnostics_failure
CHECK-NEXT: warn_fe_cc_print_header_failure
CHECK-NEXT: warn_fe_macro_contains_embedded_newline
-CHECK-NEXT: warn_hlsl_UDT_missing_basic_type
-CHECK-NEXT: warn_hlsl_UDT_missing_resource_type_member
-CHECK-NEXT: warn_hlsl_deprecated_register_type_b
-CHECK-NEXT: warn_hlsl_deprecated_register_type_i
-CHECK-NEXT: warn_hlsl_register_type_c_not_in_global_scope
CHECK-NEXT: warn_ignoring_ftabstop_value
CHECK-NEXT: warn_implements_nscopying
CHECK-NEXT: warn_incompatible_qualified_id
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index ce81574a796082..95de3ad55af54f 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -46,7 +46,7 @@ void foo2() {
extern RWBuffer<float> U2 : register(u5);
}
-// FIXME: emit a diagnostic because float doesn't match the 'u' register type
+// expected-error at +1 {{unsupported resource register binding 'u' on variable of type 'float'}}
float b : register(u0, space1);
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
>From 3ccd16552f7bde48c725615451464283217dd160 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 10:21:25 -0700
Subject: [PATCH 14/43] clean up unused diags, remove comment
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 ---
clang/lib/Sema/SemaHLSL.cpp | 7 -------
2 files changed, 10 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 293c7b895790f2..a6bbbb16932e1f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12352,9 +12352,6 @@ def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constan
def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">, InGroup<DisallowLegacyBindingRules>;
def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">, InGroup<DisallowLegacyBindingRules>;
-def err_hlsl_mismatching_register_resource_type_and_name: Error<"invalid register name prefix '%0' for register resource type '%1' (expected %select{'t'|'u'|'b'|'s'}2)">;
-def err_hlsl_mismatching_register_builtin_type_and_name: Error<"invalid register name prefix '%0' for '%1' (expected %2)">;
-def err_hlsl_unsupported_register_resource_type : Error<"invalid resource '%0' used">;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d853568261c836..27961cfec0ddaf 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -519,13 +519,6 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
llvm_unreachable("Resource class must have an element type.");
if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
- /* QualType QT = SamplerUAVOrSRV->getType();
- PrintingPolicy PP = S.getPrintingPolicy();
- std::string typestr = QualType::getAsString(QT.split(), PP);
-
- S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_resource_type)
- << typestr;
- return; */
return nullptr;
}
>From 05b289bf8b251d5edc2a96f60a13255cad99afc3 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 17:32:54 -0700
Subject: [PATCH 15/43] address Damyan
---
clang/lib/Sema/SemaHLSL.cpp | 214 +++++++++---------
.../resource_binding_attr_error_udt.hlsl | 2 +-
2 files changed, 106 insertions(+), 110 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 27961cfec0ddaf..9c37573cc80fd5 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -29,37 +29,30 @@ using namespace clang;
SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}
-HLSLResourceAttr *SemaHLSL::mergeHLSLResourceAttr(bool CBuffer) {
- // cbuffer case
+Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
+ SourceLocation KwLoc, IdentifierInfo *Ident,
+ SourceLocation IdentLoc,
+ SourceLocation LBrace) {
+ // For anonymous namespace, take the location of the left brace.
+ DeclContext *LexicalParent = SemaRef.getCurLexicalContext();
+ HLSLBufferDecl *Result = HLSLBufferDecl::Create(
+ getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
+
+ HLSLResourceAttr *NewAttr;
if (CBuffer) {
- HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+ NewAttr = HLSLResourceAttr::CreateImplicit(
getASTContext(), llvm::hlsl::ResourceClass::CBuffer,
llvm::hlsl::ResourceKind::CBuffer,
/*IsROV=*/false);
- return attr;
}
// tbuffer case
else {
- HLSLResourceAttr *attr = HLSLResourceAttr::CreateImplicit(
+ NewAttr = HLSLResourceAttr::CreateImplicit(
getASTContext(), llvm::hlsl::ResourceClass::SRV,
llvm::hlsl::ResourceKind::TBuffer,
/*IsROV=*/false);
- return attr;
}
-}
-
-Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
- SourceLocation KwLoc, IdentifierInfo *Ident,
- SourceLocation IdentLoc,
- SourceLocation LBrace) {
- // For anonymous namespace, take the location of the left brace.
- DeclContext *LexicalParent = SemaRef.getCurLexicalContext();
- HLSLBufferDecl *Result = HLSLBufferDecl::Create(
- getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
-
- HLSLResourceAttr *NewAttr = mergeHLSLResourceAttr(CBuffer);
- if (NewAttr)
- Result->addAttr(NewAttr);
+ Result->addAttr(NewAttr);
SemaRef.PushOnScopeChains(Result, BufferScope);
SemaRef.PushDeclContext(BufferScope, Result);
@@ -482,19 +475,19 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
D->addAttr(HLSLResourceClassAttr::Create(getASTContext(), RC, ArgLoc));
}
-struct register_binding_flags {
- bool resource = false;
- bool udt = false;
- bool other = false;
- bool basic = false;
+struct RegisterBindingFlags {
+ bool Resource = false;
+ bool Udt = false;
+ bool Other = false;
+ bool Basic = false;
- bool srv = false;
- bool uav = false;
- bool cbv = false;
- bool sampler = false;
+ bool Srv = false;
+ bool Uav = false;
+ bool Cbv = false;
+ bool Sampler = false;
- bool contains_numeric = false;
- bool default_globals = false;
+ bool ContainsNumeric = false;
+ bool DefaultGlobals = false;
};
bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
@@ -513,8 +506,8 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
return false;
}
-const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
- const Type *Ty = SamplerUAVOrSRV->getType()->getPointeeOrArrayElementType();
+const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
+ const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
if (!Ty)
llvm_unreachable("Resource class must have an element type.");
@@ -533,12 +526,11 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *SamplerUAVOrSRV) {
}
const HLSLResourceAttr *
-getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
+getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
HLSLBufferDecl *CBufferOrTBuffer) {
- if (SamplerUAVOrSRV) {
- const CXXRecordDecl *TheRecordDecl =
- getRecordDeclFromVarDecl(SamplerUAVOrSRV);
+ if (VD) {
+ const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
if (!TheRecordDecl)
return nullptr;
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
@@ -551,9 +543,9 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *SamplerUAVOrSRV,
return nullptr;
}
-void traverseType(QualType T, register_binding_flags &r) {
+void traverseType(QualType T, RegisterBindingFlags &r) {
if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
- r.contains_numeric = true;
+ r.ContainsNumeric = true;
return;
} else if (const RecordType *RT = T->getAs<RecordType>()) {
RecordDecl *SubRD = RT->getDecl();
@@ -564,19 +556,19 @@ void traverseType(QualType T, register_binding_flags &r) {
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV: {
- r.srv = true;
+ r.Srv = true;
break;
}
case llvm::hlsl::ResourceClass::UAV: {
- r.uav = true;
+ r.Uav = true;
break;
}
case llvm::hlsl::ResourceClass::CBuffer: {
- r.cbv = true;
+ r.Cbv = true;
break;
}
case llvm::hlsl::ResourceClass::Sampler: {
- r.sampler = true;
+ r.Sampler = true;
break;
}
}
@@ -591,7 +583,7 @@ void traverseType(QualType T, register_binding_flags &r) {
}
}
-void setResourceClassFlagsFromRecordDecl(register_binding_flags &r,
+void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &r,
const RecordDecl *RD) {
if (!RD)
return;
@@ -604,66 +596,66 @@ void setResourceClassFlagsFromRecordDecl(register_binding_flags &r,
}
}
-register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
- register_binding_flags r;
+RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
+ RegisterBindingFlags r;
if (!isDeclaredWithinCOrTBuffer(D)) {
// make sure the type is a basic / numeric type
if (VarDecl *v = dyn_cast<VarDecl>(D)) {
QualType t = v->getType();
// a numeric variable will inevitably end up in $Globals buffer
if (t->isIntegralType(S.getASTContext()) || t->isFloatingType())
- r.default_globals = true;
+ r.DefaultGlobals = true;
}
}
// Cbuffers and Tbuffers are HLSLBufferDecl types
HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
// Samplers, UAVs, and SRVs are VarDecl types
- VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+ VarDecl *VD = dyn_cast<VarDecl>(D);
if (CBufferOrTBuffer) {
- r.resource = true;
+ r.Resource = true;
if (CBufferOrTBuffer->isCBuffer())
- r.cbv = true;
+ r.Cbv = true;
else
- r.srv = true;
- } else if (SamplerUAVOrSRV) {
+ r.Srv = true;
+ } else if (VD) {
const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
+ getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
if (res_attr) {
llvm::hlsl::ResourceClass DeclResourceClass =
res_attr->getResourceClass();
- r.resource = true;
+ r.Resource = true;
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV: {
- r.srv = true;
+ r.Srv = true;
break;
}
case llvm::hlsl::ResourceClass::UAV: {
- r.uav = true;
+ r.Uav = true;
break;
}
case llvm::hlsl::ResourceClass::CBuffer: {
- r.cbv = true;
+ r.Cbv = true;
break;
}
case llvm::hlsl::ResourceClass::Sampler: {
- r.sampler = true;
+ r.Sampler = true;
break;
}
}
} else {
- if (SamplerUAVOrSRV->getType()->isBuiltinType())
- r.basic = true;
- else if (SamplerUAVOrSRV->getType()->isAggregateType()) {
- r.udt = true;
- QualType VarType = SamplerUAVOrSRV->getType();
+ if (VD->getType()->isBuiltinType())
+ r.Basic = true;
+ else if (VD->getType()->isAggregateType()) {
+ r.Udt = true;
+ QualType VarType = VD->getType();
if (const RecordType *RT = VarType->getAs<RecordType>()) {
const RecordDecl *RD = RT->getDecl();
// recurse through members, set appropriate resource class flags.
setResourceClassFlagsFromRecordDecl(r, RD);
}
} else
- r.other = true;
+ r.Other = true;
}
} else {
llvm_unreachable("unknown decl type");
@@ -707,39 +699,43 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
}
}
+std::string getHLSLResourceTypeStr(Sema &S, Decl *D) {
+ VarDecl *VD = dyn_cast<VarDecl>(D);
+ HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+
+ if (VD) {
+ QualType QT = VD->getType();
+ PrintingPolicy PP = S.getPrintingPolicy();
+ return QualType::getAsString(QT.split(), PP);
+ } else {
+ return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
+ }
+}
+
static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
Decl *D, StringRef &Slot) {
// Samplers, UAVs, and SRVs are VarDecl types
- VarDecl *SamplerUAVOrSRV = dyn_cast<VarDecl>(D);
+ VarDecl *VD = dyn_cast<VarDecl>(D);
// Cbuffers and Tbuffers are HLSLBufferDecl types
HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
// exactly one of these two types should be set
- if (!SamplerUAVOrSRV && !CBufferOrTBuffer)
+ if (!VD && !CBufferOrTBuffer)
return;
- if (SamplerUAVOrSRV && CBufferOrTBuffer)
+ if (VD && CBufferOrTBuffer)
return;
- register_binding_flags f = HLSLFillRegisterBindingFlags(S, D);
- assert((int)f.other + (int)f.resource + (int)f.basic + (int)f.udt == 1 &&
+ RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
+ assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.Udt == 1 &&
"only one resource analysis result should be expected");
- // get the variable type
- std::string typestr;
- if (SamplerUAVOrSRV) {
- QualType QT = SamplerUAVOrSRV->getType();
- PrintingPolicy PP = S.getPrintingPolicy();
- typestr = QualType::getAsString(QT.split(), PP);
- } else
- typestr = CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
-
std::string registerType(Slot.substr(0, 1));
// first, if "other" is set, emit an error
- if (f.other) {
+ if (f.Other) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot << typestr;
+ << Slot << getHLSLResourceTypeStr(S, D);
return;
}
@@ -748,9 +744,9 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
- if (f.resource) {
+ if (f.Resource) {
const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromEitherDecl(SamplerUAVOrSRV, CBufferOrTBuffer);
+ getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
assert(res_attr && "any decl that set the resource flag on analysis should "
"have a resource attribute attached.");
const llvm::hlsl::ResourceClass DeclResourceClass =
@@ -758,34 +754,34 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV: {
- if (registerType != "t") {
+ if (Slot[0] != 't') {
S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << registerType << 0 /*srv*/;
+ << getHLSLResourceTypeStr(S, D) << registerType << 0 /*srv*/;
}
break;
}
case llvm::hlsl::ResourceClass::UAV: {
- if (registerType != "u") {
+ if (Slot[0] != 'u') {
S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << registerType << 1 /*uav*/;
+ << getHLSLResourceTypeStr(S, D) << registerType << 1 /*uav*/;
}
break;
}
case llvm::hlsl::ResourceClass::CBuffer: {
- if (registerType != "b") {
+ if (Slot[0] != 'b') {
S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << registerType << 2 /*cbv*/;
+ << getHLSLResourceTypeStr(S, D) << registerType << 2 /*cbv*/;
}
break;
}
case llvm::hlsl::ResourceClass::Sampler: {
- if (registerType != "s") {
+ if (Slot[0] != 's') {
S.Diag(D->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << typestr << registerType << 3 /*sampler*/;
+ << getHLSLResourceTypeStr(S, D) << registerType << 3 /*sampler*/;
}
break;
}
@@ -795,8 +791,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// next, handle diagnostics for when the "basic" flag is set,
// including the legacy "i" and "b" register types.
- if (f.basic) {
- if (f.default_globals) {
+ if (f.Basic) {
+ if (f.DefaultGlobals) {
if (Slot[0] == 'b')
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
if (Slot[0] == 'i')
@@ -804,71 +800,71 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
if (Slot[0] == 'c') {
- if (!f.default_globals) {
+ if (!f.DefaultGlobals) {
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
}
} else if (Slot[0] == 't') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 0 << typestr;
+ << 0 << getHLSLResourceTypeStr(S, D);
} else if (Slot[0] == 'u') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 1 << typestr;
+ << 1 << getHLSLResourceTypeStr(S, D);
} else if (Slot[0] == 's') {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 3 << typestr;
+ << 3 << getHLSLResourceTypeStr(S, D);
// any other register type should emit
// err_hlsl_unsupported_register_type_and_variable_type
- } else if (!f.default_globals) {
+ } else if (!f.DefaultGlobals) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << registerType << typestr;
+ << registerType << getHLSLResourceTypeStr(S, D);
}
return;
}
// finally, we handle the udt case
- if (f.udt) {
+ if (f.Udt) {
switch (Slot[0]) {
case 't': {
- if (!f.srv) {
+ if (!f.Srv) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "t" << 0;
+ << getHLSLResourceTypeStr(S, D) << "t" << 0;
}
break;
}
case 'u': {
- if (!f.uav) {
+ if (!f.Uav) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "u" << 1;
+ << getHLSLResourceTypeStr(S, D) << "u" << 1;
}
break;
}
case 'b': {
- if (!f.cbv) {
+ if (!f.Cbv) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "b" << 2;
+ << getHLSLResourceTypeStr(S, D) << "b" << 2;
}
break;
}
case 's': {
- if (!f.sampler) {
+ if (!f.Sampler) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << typestr << "s" << 3;
+ << getHLSLResourceTypeStr(S, D) << "s" << 3;
}
break;
}
case 'c': {
- if (!f.srv)
+ if (!f.Srv)
S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
break;
}
default: {
S.Diag(D->getLocation(),
diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot.front() << typestr;
+ << Slot.front() << getHLSLResourceTypeStr(S, D);
}
}
return;
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 28a747b965b226..73708535f8d8f3 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -95,7 +95,7 @@ struct Eg11{
// expected-error at +1{{conflicting register annotations: multiple register numbers detected for register type 'u'}}
Eg11 e11 : register(u9) : register(u10);
-
+Eg11 e11a : register(u9, space0) : register(u9, space1);
struct Eg12{
RWBuffer<int> a;
};
>From 22cb1e2990b819853b75c419d7e05383fea2dedd Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 19:04:53 -0700
Subject: [PATCH 16/43] fix some asserts
---
clang/lib/Sema/SemaHLSL.cpp | 13 +++++--------
.../SemaHLSL/resource_binding_attr_error_udt.hlsl | 4 +---
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9c37573cc80fd5..636d9bbeba234c 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -508,16 +508,15 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
- if (!Ty)
- llvm_unreachable("Resource class must have an element type.");
+ assert(Ty && "Resource class must have an element type.");
if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
return nullptr;
}
const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
- if (!TheRecordDecl)
- llvm_unreachable("Resource class should have a resource type declaration.");
+ assert(TheRecordDecl &&
+ "Resource class should have a resource type declaration.");
if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
@@ -721,10 +720,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
// exactly one of these two types should be set
- if (!VD && !CBufferOrTBuffer)
- return;
- if (VD && CBufferOrTBuffer)
- return;
+ assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
+ "either VD or CBufferOrTBuffer should be set");
RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.Udt == 1 &&
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 73708535f8d8f3..080db7d87a6814 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -75,8 +75,6 @@ struct Eg9{
};
Eg9 e9;
-
-
/* Light up this test when Texture2D is implemented
template<typename R>
struct Eg10 {
@@ -86,7 +84,7 @@ struct Eg10 {
Eg10<Texture2D> e10 : register(u0);
// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
-*/
+*/
struct Eg11{
RWBuffer<int> a;
>From 843a2ed25cf2ca3695856166ed7e3575e176fbac Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 8 Jul 2024 19:08:07 -0700
Subject: [PATCH 17/43] add test for infeasible reg type
---
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 95de3ad55af54f..bd2e25f0db24e8 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -58,3 +58,6 @@ struct S {
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<float> U : register(u3);
};
+
+// expected-error at +1 {{invalid register type 'z' used; expected 't', 'u', 'b', or 's'}}
+RWBuffer<float> U3 : register(z5);
\ No newline at end of file
>From 36253318bab53366f9a9a594c47b7aff81d59e8a Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 9 Jul 2024 16:10:18 -0700
Subject: [PATCH 18/43] update tests, change multiple annotation diagnostic
behavior, rename some variables
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 178 +++++++++---------
.../resource_binding_attr_error_udt.hlsl | 3 +-
3 files changed, 91 insertions(+), 92 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a6bbbb16932e1f..1a9c1fdbe7c35d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12346,7 +12346,7 @@ def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported res
def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
-def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register numbers detected for register type '%0'">;
+def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register annotations detected for register type '%0'">;
def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 636d9bbeba234c..24c3c4bb9c4c82 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -38,21 +38,12 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
HLSLBufferDecl *Result = HLSLBufferDecl::Create(
getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
- HLSLResourceAttr *NewAttr;
- if (CBuffer) {
- NewAttr = HLSLResourceAttr::CreateImplicit(
- getASTContext(), llvm::hlsl::ResourceClass::CBuffer,
- llvm::hlsl::ResourceKind::CBuffer,
- /*IsROV=*/false);
- }
- // tbuffer case
- else {
- NewAttr = HLSLResourceAttr::CreateImplicit(
- getASTContext(), llvm::hlsl::ResourceClass::SRV,
- llvm::hlsl::ResourceKind::TBuffer,
- /*IsROV=*/false);
- }
- Result->addAttr(NewAttr);
+ auto RC = CBuffer ? llvm::hlsl::ResourceClass::CBuffer
+ : llvm::hlsl::ResourceClass::SRV;
+ auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
+ : llvm::hlsl::ResourceKind::TBuffer;
+ Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RC, RK,
+ /*IsROV=*/false));
SemaRef.PushOnScopeChains(Result, BufferScope);
SemaRef.PushDeclContext(BufferScope, Result);
@@ -477,13 +468,13 @@ void SemaHLSL::handleResourceClassAttr(Decl *D, const ParsedAttr &AL) {
struct RegisterBindingFlags {
bool Resource = false;
- bool Udt = false;
+ bool UDT = false;
bool Other = false;
bool Basic = false;
- bool Srv = false;
- bool Uav = false;
- bool Cbv = false;
+ bool SRV = false;
+ bool UAV = false;
+ bool CBV = false;
bool Sampler = false;
bool ContainsNumeric = false;
@@ -510,9 +501,8 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
assert(Ty && "Resource class must have an element type.");
- if (const BuiltinType *BTy = dyn_cast<BuiltinType>(Ty)) {
+ if (const auto *BTy = dyn_cast<BuiltinType>(Ty))
return nullptr;
- }
const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
assert(TheRecordDecl &&
@@ -546,38 +536,37 @@ void traverseType(QualType T, RegisterBindingFlags &r) {
if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
r.ContainsNumeric = true;
return;
- } else if (const RecordType *RT = T->getAs<RecordType>()) {
- RecordDecl *SubRD = RT->getDecl();
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
- auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
- TheRecordDecl = TheRecordDecl->getCanonicalDecl();
- const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
- llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
- switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV: {
- r.Srv = true;
- break;
- }
- case llvm::hlsl::ResourceClass::UAV: {
- r.Uav = true;
- break;
- }
- case llvm::hlsl::ResourceClass::CBuffer: {
- r.Cbv = true;
- break;
- }
- case llvm::hlsl::ResourceClass::Sampler: {
- r.Sampler = true;
- break;
- }
- }
+ }
+ const RecordType *RT = T->getAs<RecordType>();
+ if (!RT)
+ return;
+
+ RecordDecl *SubRD = RT->getDecl();
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
+ auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheRecordDecl = TheRecordDecl->getCanonicalDecl();
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+ switch (DeclResourceClass) {
+ case llvm::hlsl::ResourceClass::SRV:
+ r.SRV = true;
+ break;
+ case llvm::hlsl::ResourceClass::UAV:
+ r.UAV = true;
+ break;
+ case llvm::hlsl::ResourceClass::CBuffer:
+ r.CBV = true;
+ break;
+ case llvm::hlsl::ResourceClass::Sampler:
+ r.Sampler = true;
+ break;
}
+ }
- else if (SubRD->isCompleteDefinition()) {
- for (auto Field : SubRD->fields()) {
- QualType T = Field->getType();
- traverseType(T, r);
- }
+ else if (SubRD->isCompleteDefinition()) {
+ for (auto Field : SubRD->fields()) {
+ QualType T = Field->getType();
+ traverseType(T, r);
}
}
}
@@ -611,12 +600,15 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
// Samplers, UAVs, and SRVs are VarDecl types
VarDecl *VD = dyn_cast<VarDecl>(D);
+ assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
+ "either VD or CBufferOrTBuffer should be set");
+
if (CBufferOrTBuffer) {
r.Resource = true;
if (CBufferOrTBuffer->isCBuffer())
- r.Cbv = true;
+ r.CBV = true;
else
- r.Srv = true;
+ r.SRV = true;
} else if (VD) {
const HLSLResourceAttr *res_attr =
getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
@@ -626,15 +618,15 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
r.Resource = true;
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV: {
- r.Srv = true;
+ r.SRV = true;
break;
}
case llvm::hlsl::ResourceClass::UAV: {
- r.Uav = true;
+ r.UAV = true;
break;
}
case llvm::hlsl::ResourceClass::CBuffer: {
- r.Cbv = true;
+ r.CBV = true;
break;
}
case llvm::hlsl::ResourceClass::Sampler: {
@@ -646,7 +638,7 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
if (VD->getType()->isBuiltinType())
r.Basic = true;
else if (VD->getType()->isAggregateType()) {
- r.Udt = true;
+ r.UDT = true;
QualType VarType = VD->getType();
if (const RecordType *RT = VarType->getAs<RecordType>()) {
const RecordDecl *RD = RT->getDecl();
@@ -656,46 +648,52 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
} else
r.Other = true;
}
- } else {
- llvm_unreachable("unknown decl type");
}
return r;
}
+int getRegisterTypeIndex(StringRef Slot) {
+ switch (Slot[0]) {
+ case 't':
+ return 0;
+ case 'u':
+ return 1;
+ case 'b':
+ return 2;
+ case 's':
+ return 3;
+ case 'c':
+ return 4;
+ case 'i':
+ return 5;
+ default:
+ llvm_unreachable("invalid register type");
+ }
+}
+
static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
StringRef &Slot) {
- // make sure that there are no register annotations applied to the decl
- // with the same register type but different numbers
- std::unordered_map<char, std::set<char>>
- s; // store unique register type + numbers
- std::set<char> starting_set = {Slot[1]};
- s.insert(std::make_pair(Slot[0], starting_set));
+ // make sure that there are no tworegister annotations
+ // applied to the decl with the same register type
+ bool registerTypesDetectedCount[6];
+ for (int i = 0; i < 6; i++)
+ registerTypesDetectedCount[i] = false;
+ registerTypesDetectedCount[getRegisterTypeIndex(Slot)] = true;
+
for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
std::string otherSlot(attr->getSlot().data());
-
- // insert into hash map
- if (s.find(otherSlot[0]) != s.end()) {
- // if the register type is already in the map, insert the number
- // into the set (if it's not already there
- s[otherSlot[0]].insert(otherSlot[1]);
- } else {
- // if the register type is not in the map, insert it with the number
- std::set<char> otherSet;
- otherSet.insert(otherSlot[1]);
- s.insert(std::make_pair(otherSlot[0], otherSet));
+ int registerTypeIndex = getRegisterTypeIndex(otherSlot);
+ if (registerTypesDetectedCount[registerTypeIndex])
+ S.Diag(D->getLocation(),
+ diag::err_hlsl_conflicting_register_annotations)
+ << otherSlot.substr(0, 1);
+ else {
+ registerTypesDetectedCount[registerTypeIndex] = true;
}
}
}
-
- for (auto regType : s) {
- if (regType.second.size() > 1) {
- std::string regTypeStr(1, regType.first);
- S.Diag(D->getLocation(), diag::err_hlsl_conflicting_register_annotations)
- << regTypeStr;
- }
- }
}
std::string getHLSLResourceTypeStr(Sema &S, Decl *D) {
@@ -724,7 +722,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
"either VD or CBufferOrTBuffer should be set");
RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
- assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.Udt == 1 &&
+ assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.UDT == 1 &&
"only one resource analysis result should be expected");
std::string registerType(Slot.substr(0, 1));
@@ -819,10 +817,10 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
// finally, we handle the udt case
- if (f.Udt) {
+ if (f.UDT) {
switch (Slot[0]) {
case 't': {
- if (!f.Srv) {
+ if (!f.SRV) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< getHLSLResourceTypeStr(S, D) << "t" << 0;
@@ -830,7 +828,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
break;
}
case 'u': {
- if (!f.Uav) {
+ if (!f.UAV) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< getHLSLResourceTypeStr(S, D) << "u" << 1;
@@ -838,7 +836,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
break;
}
case 'b': {
- if (!f.Cbv) {
+ if (!f.CBV) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
<< getHLSLResourceTypeStr(S, D) << "b" << 2;
@@ -854,7 +852,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
break;
}
case 'c': {
- if (!f.Srv)
+ if (!f.ContainsNumeric)
S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
break;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 080db7d87a6814..9c93224add0772 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -91,8 +91,9 @@ struct Eg11{
RWBuffer<int> b;
};
-// expected-error at +1{{conflicting register annotations: multiple register numbers detected for register type 'u'}}
+// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
Eg11 e11 : register(u9) : register(u10);
+// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
Eg11 e11a : register(u9, space0) : register(u9, space1);
struct Eg12{
RWBuffer<int> a;
>From e9121f91fcc52bd02677b60b1bbb34e52b712fa5 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 9 Jul 2024 17:28:03 -0700
Subject: [PATCH 19/43] make register types case insensitive, improve array
initializatoin
---
clang/lib/Sema/SemaHLSL.cpp | 52 ++++++++++++-------
.../resource_binding_attr_error_basic.hlsl | 4 +-
2 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 24c3c4bb9c4c82..d257df5b052a69 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -487,11 +487,8 @@ bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
// Traverse up the parent contexts
const DeclContext *context = decl->getDeclContext();
- while (context) {
- if (isa<HLSLBufferDecl>(context)) {
- return true;
- }
- context = context->getParent();
+ if (isa<HLSLBufferDecl>(context)) {
+ return true;
}
return false;
@@ -655,16 +652,22 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
int getRegisterTypeIndex(StringRef Slot) {
switch (Slot[0]) {
case 't':
+ case 'T':
return 0;
case 'u':
+ case 'U':
return 1;
case 'b':
+ case 'B ':
return 2;
case 's':
+ case 'S':
return 3;
case 'c':
+ case 'C':
return 4;
case 'i':
+ case 'I':
return 5;
default:
llvm_unreachable("invalid register type");
@@ -675,22 +678,20 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
StringRef &Slot) {
// make sure that there are no tworegister annotations
// applied to the decl with the same register type
- bool registerTypesDetectedCount[6];
- for (int i = 0; i < 6; i++)
- registerTypesDetectedCount[i] = false;
- registerTypesDetectedCount[getRegisterTypeIndex(Slot)] = true;
+ bool RegisterTypesDetected[6] = {false};
+ RegisterTypesDetected[getRegisterTypeIndex(Slot)] = true;
for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
- std::string otherSlot(attr->getSlot().data());
- int registerTypeIndex = getRegisterTypeIndex(otherSlot);
- if (registerTypesDetectedCount[registerTypeIndex])
+
+ int registerTypeIndex = getRegisterTypeIndex(attr->getSlot());
+ if (RegisterTypesDetected[registerTypeIndex]) {
S.Diag(D->getLocation(),
diag::err_hlsl_conflicting_register_annotations)
- << otherSlot.substr(0, 1);
- else {
- registerTypesDetectedCount[registerTypeIndex] = true;
+ << attr->getSlot().substr(0, 1);
+ } else {
+ RegisterTypesDetected[registerTypeIndex] = true;
}
}
}
@@ -819,7 +820,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// finally, we handle the udt case
if (f.UDT) {
switch (Slot[0]) {
- case 't': {
+ case 't':
+ case 'T': {
if (!f.SRV) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -827,7 +829,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 'u': {
+ case 'u':
+ case 'U': {
if (!f.UAV) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -835,7 +838,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 'b': {
+ case 'b':
+ case 'B': {
if (!f.CBV) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -843,7 +847,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 's': {
+ case 's':
+ case 'S': {
if (!f.Sampler) {
S.Diag(D->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -851,7 +856,8 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 'c': {
+ case 'c':
+ case 'C': {
if (!f.ContainsNumeric)
S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
break;
@@ -906,11 +912,17 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
if (!Slot.empty()) {
switch (Slot[0]) {
case 't':
+ case 'T':
case 'u':
+ case 'U':
case 'b':
+ case 'B':
case 's':
+ case 'S':
case 'c':
+ case 'C':
case 'i':
+ case 'I':
break;
default:
Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_resource_type)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index 64cff79274eddd..787fd54a3258f9 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -20,7 +20,7 @@ tbuffer g_tbuffer1 {
float f5 : register(c2);
};
-tbuffer g_cbuffer2 {
+cbuffer g_cbuffer2 {
// expected-error at +1{{register binding type 'float' not supported for variable of type 'b'}}
float f6 : register(b2);
};
@@ -28,4 +28,4 @@ tbuffer g_cbuffer2 {
tbuffer g_tbuffer2 {
// expected-error at +1{{register binding type 'float' not supported for variable of type 'i'}}
float f7 : register(i2);
-};
\ No newline at end of file
+};
>From 6914992869444b97c30690d00a6cb5ef472b56ee Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 10 Jul 2024 12:25:32 -0700
Subject: [PATCH 20/43] variable name changes, add diag group silence test,
centralize slot interpretation
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 309 +++++++++---------
.../resource_binding_attr_error_basic.hlsl | 4 +-
...urce_binding_attr_error_silence_diags.hlsl | 26 ++
4 files changed, 179 insertions(+), 162 deletions(-)
create mode 100644 clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a9c1fdbe7c35d..d78e1e073b3f7e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12343,7 +12343,7 @@ def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}0' on variable of type '%1'">;
-def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%1' not supported for variable of type '%0'">;
+def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%0' not supported for variable of type '%1'">;
def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register annotations detected for register type '%0'">;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d257df5b052a69..effc3b0b1eca49 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -481,12 +481,12 @@ struct RegisterBindingFlags {
bool DefaultGlobals = false;
};
-bool isDeclaredWithinCOrTBuffer(const Decl *decl) {
- if (!decl)
+bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
+ if (!TheDecl)
return false;
// Traverse up the parent contexts
- const DeclContext *context = decl->getDeclContext();
+ const DeclContext *context = TheDecl->getDeclContext();
if (isa<HLSLBufferDecl>(context)) {
return true;
}
@@ -498,7 +498,7 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
assert(Ty && "Resource class must have an element type.");
- if (const auto *BTy = dyn_cast<BuiltinType>(Ty))
+ if (const auto *TheBuiltinTy = dyn_cast<BuiltinType>(Ty))
return nullptr;
const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
@@ -529,46 +529,45 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
return nullptr;
}
-void traverseType(QualType T, RegisterBindingFlags &r) {
- if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
- r.ContainsNumeric = true;
+void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
+ if (TheQualTy->isIntegralOrEnumerationType() || TheQualTy->isFloatingType()) {
+ Flags.ContainsNumeric = true;
return;
}
- const RecordType *RT = T->getAs<RecordType>();
- if (!RT)
+ const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>();
+ if (!TheRecordTy)
return;
- RecordDecl *SubRD = RT->getDecl();
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRD)) {
+ RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
TheRecordDecl = TheRecordDecl->getCanonicalDecl();
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
- r.SRV = true;
+ Flags.SRV = true;
break;
case llvm::hlsl::ResourceClass::UAV:
- r.UAV = true;
+ Flags.UAV = true;
break;
case llvm::hlsl::ResourceClass::CBuffer:
- r.CBV = true;
+ Flags.CBV = true;
break;
case llvm::hlsl::ResourceClass::Sampler:
- r.Sampler = true;
+ Flags.Sampler = true;
break;
}
}
- else if (SubRD->isCompleteDefinition()) {
- for (auto Field : SubRD->fields()) {
- QualType T = Field->getType();
- traverseType(T, r);
+ else if (SubRecordDecl->isCompleteDefinition()) {
+ for (auto Field : SubRecordDecl->fields()) {
+ traverseType(Field->getType(), Flags);
}
}
}
-void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &r,
+void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &Flags,
const RecordDecl *RD) {
if (!RD)
return;
@@ -576,77 +575,77 @@ void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &r,
if (RD->isCompleteDefinition()) {
for (auto Field : RD->fields()) {
QualType T = Field->getType();
- traverseType(T, r);
+ traverseType(T, Flags);
}
}
}
-RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
- RegisterBindingFlags r;
- if (!isDeclaredWithinCOrTBuffer(D)) {
- // make sure the type is a basic / numeric type
- if (VarDecl *v = dyn_cast<VarDecl>(D)) {
- QualType t = v->getType();
- // a numeric variable will inevitably end up in $Globals buffer
- if (t->isIntegralType(S.getASTContext()) || t->isFloatingType())
- r.DefaultGlobals = true;
- }
- }
+RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
+
// Cbuffers and Tbuffers are HLSLBufferDecl types
- HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+ HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
// Samplers, UAVs, and SRVs are VarDecl types
- VarDecl *VD = dyn_cast<VarDecl>(D);
+ VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
- assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
+ assert(((TheVarDecl && !CBufferOrTBuffer) ||
+ (!TheVarDecl && CBufferOrTBuffer)) &&
"either VD or CBufferOrTBuffer should be set");
+ RegisterBindingFlags Flags;
+ if (!isDeclaredWithinCOrTBuffer(TheDecl)) {
+ // make sure the type is a basic / numeric type
+ if (TheVarDecl) {
+ QualType TheQualTy = TheVarDecl->getType();
+ // a numeric variable will inevitably end up in $Globals buffer
+ if (TheQualTy->isIntegralType(S.getASTContext()) ||
+ TheQualTy->isFloatingType())
+ Flags.DefaultGlobals = true;
+ }
+ }
+
if (CBufferOrTBuffer) {
- r.Resource = true;
+ Flags.Resource = true;
if (CBufferOrTBuffer->isCBuffer())
- r.CBV = true;
+ Flags.CBV = true;
else
- r.SRV = true;
- } else if (VD) {
+ Flags.SRV = true;
+ } else if (TheVarDecl) {
const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
+ getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
if (res_attr) {
llvm::hlsl::ResourceClass DeclResourceClass =
res_attr->getResourceClass();
- r.Resource = true;
+ Flags.Resource = true;
switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV: {
- r.SRV = true;
+ case llvm::hlsl::ResourceClass::SRV:
+ Flags.SRV = true;
break;
- }
- case llvm::hlsl::ResourceClass::UAV: {
- r.UAV = true;
+ case llvm::hlsl::ResourceClass::UAV:
+ Flags.UAV = true;
break;
- }
- case llvm::hlsl::ResourceClass::CBuffer: {
- r.CBV = true;
+ case llvm::hlsl::ResourceClass::CBuffer:
+ Flags.CBV = true;
break;
- }
- case llvm::hlsl::ResourceClass::Sampler: {
- r.Sampler = true;
+ case llvm::hlsl::ResourceClass::Sampler:
+ Flags.Sampler = true;
break;
}
- }
} else {
- if (VD->getType()->isBuiltinType())
- r.Basic = true;
- else if (VD->getType()->isAggregateType()) {
- r.UDT = true;
- QualType VarType = VD->getType();
- if (const RecordType *RT = VarType->getAs<RecordType>()) {
- const RecordDecl *RD = RT->getDecl();
+ if (TheVarDecl->getType()->isBuiltinType())
+ Flags.Basic = true;
+ else if (TheVarDecl->getType()->isAggregateType()) {
+ Flags.UDT = true;
+ QualType TheQualTy = TheVarDecl->getType();
+ if (const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>()) {
+ const RecordDecl *TheRecordDecl = TheRecordTy->getDecl();
// recurse through members, set appropriate resource class flags.
- setResourceClassFlagsFromRecordDecl(r, RD);
+ setResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
}
} else
- r.Other = true;
+ Flags.Other = true;
}
}
- return r;
+ return Flags;
}
int getRegisterTypeIndex(StringRef Slot) {
@@ -674,20 +673,20 @@ int getRegisterTypeIndex(StringRef Slot) {
}
}
-static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
+static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
StringRef &Slot) {
// make sure that there are no tworegister annotations
// applied to the decl with the same register type
bool RegisterTypesDetected[6] = {false};
RegisterTypesDetected[getRegisterTypeIndex(Slot)] = true;
- for (auto it = D->attr_begin(); it != D->attr_end(); ++it) {
+ for (auto it = TheDecl->attr_begin(); it != TheDecl->attr_end(); ++it) {
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
int registerTypeIndex = getRegisterTypeIndex(attr->getSlot());
if (RegisterTypesDetected[registerTypeIndex]) {
- S.Diag(D->getLocation(),
+ S.Diag(TheDecl->getLocation(),
diag::err_hlsl_conflicting_register_annotations)
<< attr->getSlot().substr(0, 1);
} else {
@@ -697,185 +696,177 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *D,
}
}
-std::string getHLSLResourceTypeStr(Sema &S, Decl *D) {
- VarDecl *VD = dyn_cast<VarDecl>(D);
- HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
+ VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
+ HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
- if (VD) {
- QualType QT = VD->getType();
+ if (TheVarDecl) {
+ QualType TheQualTy = TheVarDecl->getType();
PrintingPolicy PP = S.getPrintingPolicy();
- return QualType::getAsString(QT.split(), PP);
+ return QualType::getAsString(TheQualTy.split(), PP);
} else {
return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
}
}
static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
- Decl *D, StringRef &Slot) {
+ Decl *TheDecl, StringRef &Slot) {
// Samplers, UAVs, and SRVs are VarDecl types
- VarDecl *VD = dyn_cast<VarDecl>(D);
+ VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
// Cbuffers and Tbuffers are HLSLBufferDecl types
- HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D);
+ HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
// exactly one of these two types should be set
- assert(((VD && !CBufferOrTBuffer) || (!VD && CBufferOrTBuffer)) &&
- "either VD or CBufferOrTBuffer should be set");
-
- RegisterBindingFlags f = HLSLFillRegisterBindingFlags(S, D);
- assert((int)f.Other + (int)f.Resource + (int)f.Basic + (int)f.UDT == 1 &&
+ assert(((TheVarDecl && !CBufferOrTBuffer) ||
+ (!TheVarDecl && CBufferOrTBuffer)) &&
+ "either TheVarDecl or CBufferOrTBuffer should be set");
+
+ RegisterBindingFlags Flags = HLSLFillRegisterBindingFlags(S, TheDecl);
+ assert((int)Flags.Other + (int)Flags.Resource + (int)Flags.Basic +
+ (int)Flags.UDT ==
+ 1 &&
"only one resource analysis result should be expected");
- std::string registerType(Slot.substr(0, 1));
-
// first, if "other" is set, emit an error
- if (f.Other) {
+ if (Flags.Other) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot << getHLSLResourceTypeStr(S, D);
+ << Slot << getHLSLResourceTypeStr(S, TheDecl);
return;
}
// next, if multiple register annotations exist, check that none conflict.
- ValidateMultipleRegisterAnnotations(S, D, Slot);
+ ValidateMultipleRegisterAnnotations(S, TheDecl, Slot);
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
- if (f.Resource) {
+ if (Flags.Resource) {
const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromEitherDecl(VD, CBufferOrTBuffer);
+ getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
assert(res_attr && "any decl that set the resource flag on analysis should "
"have a resource attribute attached.");
const llvm::hlsl::ResourceClass DeclResourceClass =
res_attr->getResourceClass();
switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV: {
- if (Slot[0] != 't') {
- S.Diag(D->getLocation(),
+ case llvm::hlsl::ResourceClass::SRV:
+ if (getRegisterTypeIndex(Slot) != 0)
+ S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, D) << registerType << 0 /*srv*/;
- }
+ << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+ << 0 /*srv*/;
break;
- }
- case llvm::hlsl::ResourceClass::UAV: {
- if (Slot[0] != 'u') {
- S.Diag(D->getLocation(),
+ case llvm::hlsl::ResourceClass::UAV:
+ if (getRegisterTypeIndex(Slot) != 1)
+ S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, D) << registerType << 1 /*uav*/;
- }
+ << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+ << 1 /*uav*/;
break;
- }
- case llvm::hlsl::ResourceClass::CBuffer: {
- if (Slot[0] != 'b') {
- S.Diag(D->getLocation(),
+ case llvm::hlsl::ResourceClass::CBuffer:
+ if (getRegisterTypeIndex(Slot) != 2)
+ S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, D) << registerType << 2 /*cbv*/;
- }
+ << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+ << 2 /*cbv*/;
break;
- }
- case llvm::hlsl::ResourceClass::Sampler: {
- if (Slot[0] != 's') {
- S.Diag(D->getLocation(),
+ case llvm::hlsl::ResourceClass::Sampler:
+ if (getRegisterTypeIndex(Slot) != 3)
+ S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, D) << registerType << 3 /*sampler*/;
- }
+ << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
+ << 3 /*sampler*/;
break;
}
- }
return;
}
// next, handle diagnostics for when the "basic" flag is set,
// including the legacy "i" and "b" register types.
- if (f.Basic) {
- if (f.DefaultGlobals) {
- if (Slot[0] == 'b')
+ if (Flags.Basic) {
+ if (Flags.DefaultGlobals) {
+ if (getRegisterTypeIndex(Slot) == 2)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
- if (Slot[0] == 'i')
+ if (getRegisterTypeIndex(Slot) == 5)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
}
- if (Slot[0] == 'c') {
- if (!f.DefaultGlobals) {
+ if (getRegisterTypeIndex(Slot) == 4) {
+ if (!Flags.DefaultGlobals) {
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
}
- } else if (Slot[0] == 't') {
+ } else if (getRegisterTypeIndex(Slot) == 0) {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 0 << getHLSLResourceTypeStr(S, D);
- } else if (Slot[0] == 'u') {
+ << 0 << getHLSLResourceTypeStr(S, TheDecl);
+ } else if (getRegisterTypeIndex(Slot) == 1) {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 1 << getHLSLResourceTypeStr(S, D);
- } else if (Slot[0] == 's') {
+ << 1 << getHLSLResourceTypeStr(S, TheDecl);
+ } else if (getRegisterTypeIndex(Slot) == 3) {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 3 << getHLSLResourceTypeStr(S, D);
+ << 3 << getHLSLResourceTypeStr(S, TheDecl);
// any other register type should emit
// err_hlsl_unsupported_register_type_and_variable_type
- } else if (!f.DefaultGlobals) {
+ } else if (!Flags.DefaultGlobals) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << registerType << getHLSLResourceTypeStr(S, D);
+ << Slot.substr(0, 1) << getHLSLResourceTypeStr(S, TheDecl);
}
return;
}
// finally, we handle the udt case
- if (f.UDT) {
- switch (Slot[0]) {
- case 't':
- case 'T': {
- if (!f.SRV) {
- S.Diag(D->getLocation(),
+ if (Flags.UDT) {
+ switch (getRegisterTypeIndex(Slot)) {
+ case 0: {
+ if (!Flags.SRV) {
+ S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, D) << "t" << 0;
+ << getHLSLResourceTypeStr(S, TheDecl) << "t" << 0;
}
break;
}
- case 'u':
- case 'U': {
- if (!f.UAV) {
- S.Diag(D->getLocation(),
+ case 1: {
+ if (!Flags.UAV) {
+ S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, D) << "u" << 1;
+ << getHLSLResourceTypeStr(S, TheDecl) << "u" << 1;
}
break;
}
- case 'b':
- case 'B': {
- if (!f.CBV) {
- S.Diag(D->getLocation(),
+ case 2: {
+ if (!Flags.CBV) {
+ S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, D) << "b" << 2;
+ << getHLSLResourceTypeStr(S, TheDecl) << "b" << 2;
}
break;
}
- case 's':
- case 'S': {
- if (!f.Sampler) {
- S.Diag(D->getLocation(),
+ case 3: {
+ if (!Flags.Sampler) {
+ S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, D) << "s" << 3;
+ << getHLSLResourceTypeStr(S, TheDecl) << "s" << 3;
}
break;
}
- case 'c':
- case 'C': {
- if (!f.ContainsNumeric)
- S.Diag(D->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
+ case 4: {
+ if (!Flags.ContainsNumeric)
+ S.Diag(TheDecl->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
break;
}
default: {
- S.Diag(D->getLocation(),
+ S.Diag(TheDecl->getLocation(),
diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot.front() << getHLSLResourceTypeStr(S, D);
+ << Slot.front() << getHLSLResourceTypeStr(S, TheDecl);
}
}
return;
}
}
-void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
- if (dyn_cast<VarDecl>(D)) {
- if (SemaRef.RequireCompleteType(D->getBeginLoc(),
- cast<ValueDecl>(D)->getType(),
+void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
+ if (dyn_cast<VarDecl>(TheDecl)) {
+ if (SemaRef.RequireCompleteType(TheDecl->getBeginLoc(),
+ cast<ValueDecl>(TheDecl)->getType(),
diag::err_incomplete_type))
return;
}
@@ -949,12 +940,12 @@ void SemaHLSL::handleResourceBindingAttr(Decl *D, const ParsedAttr &AL) {
return;
}
- DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, D, Slot);
+ DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, TheDecl, Slot);
HLSLResourceBindingAttr *NewAttr =
HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
if (NewAttr)
- D->addAttr(NewAttr);
+ TheDecl->addAttr(NewAttr);
}
void SemaHLSL::handleParamModifierAttr(Decl *D, const ParsedAttr &AL) {
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index 787fd54a3258f9..056941a651d735 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -21,11 +21,11 @@ tbuffer g_tbuffer1 {
};
cbuffer g_cbuffer2 {
-// expected-error at +1{{register binding type 'float' not supported for variable of type 'b'}}
+// expected-error at +1{{register binding type 'b' not supported for variable of type 'float'}}
float f6 : register(b2);
};
tbuffer g_tbuffer2 {
-// expected-error at +1{{register binding type 'float' not supported for variable of type 'i'}}
+// expected-error at +1{{register binding type 'i' not supported for variable of type 'float'}}
float f7 : register(i2);
};
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
new file mode 100644
index 00000000000000..4e227c85e6bcf8
--- /dev/null
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only -Wno-disallow-legacy-binding-rules %s -verify
+
+// expected-no-diagnostics
+float f2 : register(b9);
+
+float f3 : register(i9);
+
+cbuffer g_cbuffer1 {
+ float f4 : register(c2);
+};
+
+
+struct Eg12{
+ RWBuffer<int> a;
+};
+
+Eg12 e12 : register(c9);
+
+
+struct Eg7 {
+ struct Bar {
+ float f;
+ };
+ Bar b;
+};
+Eg7 e7 : register(t0);
\ No newline at end of file
>From 36362bac9491be4dd3be4b0f46c1984dd1a5cefb Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 12 Jul 2024 15:55:30 -0700
Subject: [PATCH 21/43] merge in spelled attribute change / rebase
---
clang/lib/Sema/HLSLExternalSemaSource.cpp | 20 +++++----
clang/lib/Sema/SemaHLSL.cpp | 42 ++++++++++++++-----
.../ast-dump-comment-cbuffe-tbufferr.hlsl | 6 ++-
clang/test/AST/HLSL/cbuffer_tbuffer.hlsl | 10 +++--
clang/test/AST/HLSL/packoffset.hlsl | 3 +-
clang/test/AST/HLSL/pch_hlsl_buffer.hlsl | 6 ++-
.../test/AST/HLSL/resource_binding_attr.hlsl | 10 +++--
7 files changed, 66 insertions(+), 31 deletions(-)
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 14845a96b5297b..886e25e4850bb9 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -490,8 +490,9 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() {
}
/// Set up common members and attributes for buffer types
-static BuiltinTypeDeclBuilder setupBufferHandle(CXXRecordDecl *Decl, Sema &S,
- ResourceClass RC) {
+static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
+ ResourceClass RC, ResourceKind RK,
+ bool IsROV) {
return BuiltinTypeDeclBuilder(Decl)
.addHandleMember(RC, RK, IsROV)
.addDefaultHandleConstructor(S, RC);
@@ -502,12 +503,14 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
.addSimpleTemplateParams(*SemaPtr, {"element_type"})
- .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
- /*IsROV=*/false)
+ .annotateHLSLResource(ResourceClass::UAV, ResourceKind::TypedBuffer,
+ /*IsROV=*/false)
.Record;
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
- setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
+ setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
+ ResourceKind::TypedBuffer,
+ /*IsROV=*/false)
.addArraySubscriptOperators()
.completeDefinition();
});
@@ -515,11 +518,12 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
.addSimpleTemplateParams(*SemaPtr, {"element_type"})
- .annotateResourceClass(ResourceClass::UAV, ResourceKind::TypedBuffer,
- /*IsROV=*/true)
+ .annotateHLSLResource(ResourceClass::UAV, ResourceKind::TypedBuffer,
+ /*IsROV=*/true)
.Record;
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
- setupBufferHandle(Decl, *SemaPtr, ResourceClass::UAV)
+ setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
+ ResourceKind::TypedBuffer, /*IsROV=*/true)
.addArraySubscriptOperators()
.completeDefinition();
});
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index effc3b0b1eca49..724a1f5068e35f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -42,7 +42,8 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
: llvm::hlsl::ResourceClass::SRV;
auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
: llvm::hlsl::ResourceKind::TBuffer;
- Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RC, RK,
+ Result->addAttr(HLSLResourceClassAttr::CreateImplicit(getASTContext(), RC));
+ Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RK,
/*IsROV=*/false));
SemaRef.PushOnScopeChains(Result, BufferScope);
@@ -511,6 +512,24 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
return TheRecordDecl;
}
+const HLSLResourceClassAttr *
+getHLSLResourceClassAttrFromEitherDecl(VarDecl *VD,
+ HLSLBufferDecl *CBufferOrTBuffer) {
+
+ if (VD) {
+ const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
+ if (!TheRecordDecl)
+ return nullptr;
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
+ return Attr;
+ } else if (CBufferOrTBuffer) {
+ const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
+ return Attr;
+ }
+ llvm_unreachable("one of the two conditions should be true.");
+ return nullptr;
+}
+
const HLSLResourceAttr *
getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
HLSLBufferDecl *CBufferOrTBuffer) {
@@ -542,7 +561,7 @@ void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
TheRecordDecl = TheRecordDecl->getCanonicalDecl();
- const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
@@ -610,11 +629,11 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
else
Flags.SRV = true;
} else if (TheVarDecl) {
- const HLSLResourceAttr *res_attr =
- getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
- if (res_attr) {
+ const HLSLResourceClassAttr *resClassAttr =
+ getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+ if (resClassAttr) {
llvm::hlsl::ResourceClass DeclResourceClass =
- res_attr->getResourceClass();
+ resClassAttr->getResourceClass();
Flags.Resource = true;
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
@@ -741,12 +760,15 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
if (Flags.Resource) {
- const HLSLResourceAttr *res_attr =
+ const HLSLResourceAttr *resAttr =
getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
- assert(res_attr && "any decl that set the resource flag on analysis should "
- "have a resource attribute attached.");
+ const HLSLResourceClassAttr *resClassAttr =
+ getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+ assert(resAttr && resClassAttr &&
+ "any decl that set the resource flag on analysis should "
+ "have a resource attribute and resource class attribute attached.");
const llvm::hlsl::ResourceClass DeclResourceClass =
- res_attr->getResourceClass();
+ resClassAttr->getResourceClass();
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
diff --git a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
index 727d505471bcb7..e6a2ea7c6d2dc6 100644
--- a/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
+++ b/clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
@@ -38,14 +38,16 @@ tbuffer B {
}
// AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A
-// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+// AST-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit CBuffer
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
// AST-NEXT:FullComment {{.*}}<line:10:4, col:17>
// AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
// AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
// AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
// AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
// AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
-// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
+// AST-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit SRV
+// AST-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
// AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
// AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
// AST-NEXT: `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
index a67688d510ea64..5e558354cd3a03 100644
--- a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
+++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
@@ -1,14 +1,16 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB
+// CHECK:HLSLResourceClassAttr 0x{{[0-9a-f]+}} <<invalid sloc>> Implicit CBuffer
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
cbuffer CB {
float a;
}
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB
+// CHECK:HLSLResourceClassAttr 0x{{[0-9a-f]+}} <<invalid sloc>> Implicit SRV
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
tbuffer TB {
float b;
diff --git a/clang/test/AST/HLSL/packoffset.hlsl b/clang/test/AST/HLSL/packoffset.hlsl
index 9deb63caa500a1..9c928bd6d922ed 100644
--- a/clang/test/AST/HLSL/packoffset.hlsl
+++ b/clang/test/AST/HLSL/packoffset.hlsl
@@ -4,7 +4,8 @@
// CHECK: HLSLBufferDecl {{.*}} cbuffer A
cbuffer A
{
- // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+ // CHECK-NEXT:-HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit CBuffer
+ // CHECK-NEXT:-HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
// CHECK-NEXT: VarDecl {{.*}} A1 'float4'
// CHECK-NEXT: HLSLPackOffsetAttr {{.*}} 0 0
float4 A1 : packoffset(c);
diff --git a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
index e264241e62e924..281d8be8addf09 100644
--- a/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
+++ b/clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
@@ -17,10 +17,12 @@ float foo() {
}
// Make sure cbuffer/tbuffer works for PCH.
// CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported <undeserialized declarations> cbuffer A
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer CBuffer
+// CHECK-NEXT:HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit CBuffer
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit CBuffer
// CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] <line:8:3, col:9> col:9 imported used a 'float'
// CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} <line:11:1, line:13:1> line:11:9 imported <undeserialized declarations> tbuffer B
-// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit SRV TBuffer
+// CHECK-NEXT:HLSLResourceClassAttr {{.*}} <<invalid sloc>> Implicit SRV
+// CHECK-NEXT:HLSLResourceAttr {{.*}} <<invalid sloc>> Implicit TBuffer
// CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] <line:12:3, col:9> col:9 imported used b 'float'
// CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} <line:15:1, line:17:1> line:15:7 imported foo 'float ()'
// CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} <col:13, line:17:1>
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index 9752494f5adc9b..13957ad3c1fcc7 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -1,15 +1,17 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
-// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:7:9 cbuffer CB
-// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer CBuffer
+// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:8:9 cbuffer CB
+// CHECK-NEXT:HLSLResourceClassAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit CBuffer
// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "b3" "space2"
// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
cbuffer CB : register(b3, space2) {
float a;
}
-// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:15:9 tbuffer TB
-// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV TBuffer
+// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:17:9 tbuffer TB
+// CHECK-NEXT:HLSLResourceClassAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit SRV
+// CHECK-NEXT:HLSLResourceAttr 0x[[CB:[0-9a-f]+]] {{.*}} Implicit TBuffer
// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "t2" "space1"
// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
tbuffer TB : register(t2, space1) {
>From e7999d23bb2f0fc9da28933d289aab45434dd156 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 12 Jul 2024 16:34:01 -0700
Subject: [PATCH 22/43] add test case for static global var
---
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index bd2e25f0db24e8..698465eefbc084 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -37,6 +37,9 @@ cbuffer D : register(b 2, space 3) {}
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
static RWBuffer<float> U : register(u5);
+// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
+static float sa : register(c1);
+
void foo() {
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<float> U : register(u3);
>From 2f0dc873e3d97acb505899acdf21ae18b9f922a3 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 12 Jul 2024 18:58:18 -0700
Subject: [PATCH 23/43] add groupshared test
---
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 698465eefbc084..0697c3c43ae3f0 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -40,6 +40,9 @@ static RWBuffer<float> U : register(u5);
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
static float sa : register(c1);
+// expected-warning at +1 {{register 'c' used on type with no contents to allocate in a constant buffer}}
+ groupshared float fa[10] : register(c1);
+
void foo() {
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<float> U : register(u3);
>From 3d93ee18dfa0f83a5bdfad4fbd8aba92e55d6203 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 15 Jul 2024 12:48:15 -0700
Subject: [PATCH 24/43] add groupshared attr handling, and improve tests for
groupshared basic types / array types
---
clang/lib/Sema/SemaDeclAttr.cpp | 3 ++
clang/lib/Sema/SemaHLSL.cpp | 38 +++++++++++++------
.../SemaHLSL/resource_binding_attr_error.hlsl | 8 +++-
3 files changed, 35 insertions(+), 14 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 3b5e984f4ee773..e3a7f7d78441cd 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6889,6 +6889,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLSV_GroupIndex:
handleSimpleAttribute<HLSLSV_GroupIndexAttr>(S, D, AL);
break;
+ case ParsedAttr::AT_HLSLGroupSharedAddressSpace:
+ handleSimpleAttribute<HLSLGroupSharedAddressSpaceAttr>(S, D, AL);
+ break;
case ParsedAttr::AT_HLSLSV_DispatchThreadID:
S.HLSL().handleSV_DispatchThreadIDAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 724a1f5068e35f..32a8646f1d01ad 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -611,13 +611,24 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
"either VD or CBufferOrTBuffer should be set");
RegisterBindingFlags Flags;
+
+ // check if the decl type is groupshared
+ if (TheDecl->hasAttr<HLSLGroupSharedAddressSpaceAttr>()) {
+ Flags.Other = true;
+ return Flags;
+ }
+
if (!isDeclaredWithinCOrTBuffer(TheDecl)) {
// make sure the type is a basic / numeric type
if (TheVarDecl) {
QualType TheQualTy = TheVarDecl->getType();
- // a numeric variable will inevitably end up in $Globals buffer
- if (TheQualTy->isIntegralType(S.getASTContext()) ||
- TheQualTy->isFloatingType())
+ // a numeric variable or an array of numeric variables
+ // will inevitably end up in $Globals buffer
+ const clang::Type *TheBaseType = TheQualTy.getTypePtr();
+ while (TheBaseType->isArrayType())
+ TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+ if (TheBaseType->isIntegralType(S.getASTContext()) ||
+ TheBaseType->isFloatingType())
Flags.DefaultGlobals = true;
}
}
@@ -631,6 +642,10 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
} else if (TheVarDecl) {
const HLSLResourceClassAttr *resClassAttr =
getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+ const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
+ while (TheBaseType->isArrayType())
+ TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+
if (resClassAttr) {
llvm::hlsl::ResourceClass DeclResourceClass =
resClassAttr->getResourceClass();
@@ -650,16 +665,15 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
break;
}
} else {
- if (TheVarDecl->getType()->isBuiltinType())
+ if (TheBaseType->isArithmeticType())
Flags.Basic = true;
- else if (TheVarDecl->getType()->isAggregateType()) {
+ else if (TheBaseType->isRecordType()) {
Flags.UDT = true;
- QualType TheQualTy = TheVarDecl->getType();
- if (const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>()) {
- const RecordDecl *TheRecordDecl = TheRecordTy->getDecl();
- // recurse through members, set appropriate resource class flags.
- setResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
- }
+ const RecordType *TheRecordTy = TheBaseType->getAs<RecordType>();
+ assert(TheRecordTy && "The Qual Type should be Record Type");
+ const RecordDecl *TheRecordDecl = TheRecordTy->getDecl();
+ // recurse through members, set appropriate resource class flags.
+ setResourceClassFlagsFromRecordDecl(Flags, TheRecordDecl);
} else
Flags.Other = true;
}
@@ -750,7 +764,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// first, if "other" is set, emit an error
if (Flags.Other) {
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot << getHLSLResourceTypeStr(S, TheDecl);
+ << Slot.substr(0, 1) << getHLSLResourceTypeStr(S, TheDecl);
return;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 0697c3c43ae3f0..b61e11243a313b 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -40,8 +40,12 @@ static RWBuffer<float> U : register(u5);
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
static float sa : register(c1);
-// expected-warning at +1 {{register 'c' used on type with no contents to allocate in a constant buffer}}
- groupshared float fa[10] : register(c1);
+float x[2] : register(c2); // valid
+float y[2][2] : register(c3); // valid
+float z[2][2][3] : register(c4); // valid
+
+// expected-error at +1 {{register binding type 'c' not supported for variable of type 'groupshared float[10]}}
+groupshared float fa[10] : register(c5);
void foo() {
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
>From 5468006e968788a9c3e4baa9ecb218ab6cde2e42 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 15 Jul 2024 15:25:14 -0700
Subject: [PATCH 25/43] get resource class attr from non template
specialization decls, use spelled attr in tests
---
clang/lib/Sema/SemaHLSL.cpp | 34 ++++++-
.../resource_binding_attr_error_udt.hlsl | 96 +++++++++++--------
2 files changed, 88 insertions(+), 42 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 32a8646f1d01ad..027dd5e970bc06 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -549,15 +549,25 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
}
void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
+ // if the member's type is a numeric type, set the ContainsNumeric flag
if (TheQualTy->isIntegralOrEnumerationType() || TheQualTy->isFloatingType()) {
Flags.ContainsNumeric = true;
return;
}
- const RecordType *TheRecordTy = TheQualTy->getAs<RecordType>();
+
+ // otherwise, if the member's base type is not a record type, return
+ const clang::Type *TheBaseType = TheQualTy.getTypePtr();
+ while (TheBaseType->isArrayType())
+ TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+
+ const RecordType *TheRecordTy = TheBaseType->getAs<RecordType>();
if (!TheRecordTy)
return;
RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
+ bool resClassSet = false;
+ // if the member's base type is a ClassTemplateSpecializationDecl,
+ // check if it has a resource class attr
if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
TheRecordDecl = TheRecordDecl->getCanonicalDecl();
@@ -577,9 +587,29 @@ void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
Flags.Sampler = true;
break;
}
+ resClassSet = true;
+ }
+ // otherwise, check if the member has a resource class attr
+ else if (auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
+ llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
+ switch (DeclResourceClass) {
+ case llvm::hlsl::ResourceClass::SRV:
+ Flags.SRV = true;
+ break;
+ case llvm::hlsl::ResourceClass::UAV:
+ Flags.UAV = true;
+ break;
+ case llvm::hlsl::ResourceClass::CBuffer:
+ Flags.CBV = true;
+ break;
+ case llvm::hlsl::ResourceClass::Sampler:
+ Flags.Sampler = true;
+ break;
+ }
+ resClassSet = true;
}
- else if (SubRecordDecl->isCompleteDefinition()) {
+ if (!resClassSet) {
for (auto Field : SubRecordDecl->fields()) {
traverseType(Field->getType(), Flags);
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 9c93224add0772..29c7fdf593eff1 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -1,103 +1,119 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-// TODO: Implement "Buffer"
+// TODO: Implement "Buffer", we use a substitute UDT
+// to test the 't' binding type for this test.
+
+template<typename T>
+struct [[hlsl::resource_class(SRV)]] Buffer {
+ T x;
+};
+
+
+template<typename T>
+struct [[hlsl::resource_class(SRV)]] [[hlsl::resource_class(UAV)]] MultiClassBuffer {
+ T x;
+};
+
+// TODO: Implement "SamplerState", we use a substitute UDT
+// to test the 's' binding type for this test.
+struct [[hlsl::resource_class(Sampler)]] SamplerState {
+ int x;
+};
+
+// TODO: Implement "Texture2D", we use a substitute UDT
+// to test a non-templated 't' binding type for this test.
+struct [[hlsl::resource_class(UAV)]] Texture2D {
+ int x;
+};
+
struct Eg1 {
float f;
- // Buffer<float> Buf;
+ Buffer<float> Buf;
RWBuffer<float> RWBuf;
};
-Eg1 e1 : /* register(t0) :*/ register(u0);
+Eg1 e1 : register(t0) : register(u0);
// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0
struct Eg2 {
float f;
- // Buffer<float> Buf;
+ Buffer<float> Buf;
RWBuffer<float> RWBuf;
RWBuffer<float> RWBuf2;
};
-Eg2 e2 : /* register(t0) :*/ register(u0);
+Eg2 e2 : register(t0) : register(u0);
// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0.
// RWBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
-/*
struct Eg3 {
- float f;
- // Buffer<float> Buf;
- };
-Eg3 e3 : register(t0) : register(u0);
-// Valid: Buf gets bound to t0. Buf will also be bound to u0.
-*/
-
-struct Eg4 {
struct Bar {
RWBuffer<int> a;
};
Bar b;
};
-Eg4 e4 : register(u0);
-// Valid: Bar, the struct within Eg4, has a valid resource that can be bound to t0.
+Eg3 e3 : register(u0);
+// Valid: Bar, the struct within Eg3, has a valid resource that can be bound to t0.
-/* Light up this test when SamplerState is implemented
-struct Eg5 {
+struct Eg4 {
SamplerState s[3];
};
-Eg5 e5 : register(s5);
+Eg4 e4 : register(s5);
// Valid: the first sampler state object within Eg5's s is bound to slot 5
-*/
-struct Eg6 {
+
+struct Eg5 {
float f;
};
-// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
-Eg6 e6 : register(t0);
+// expected-warning at +1{{variable of type 'Eg5' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg5 e5 : register(t0);
-struct Eg7 {
+struct Eg6 {
struct Bar {
float f;
};
Bar b;
};
-// expected-warning at +1{{variable of type 'Eg7' bound to register type 't' does not contain a matching 'srv' resource}}
-Eg7 e7 : register(t0);
+// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
+Eg6 e6 : register(t0);
-struct Eg8 {
+struct Eg7 {
RWBuffer<int> a;
};
// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
-Eg8 e8 : register(c0);
+Eg7 e7 : register(c0);
-struct Eg9{
+struct Eg8{
// expected-error at +1{{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<int> a : register(u9);
};
+Eg8 e8;
+
-Eg9 e9;
-/* Light up this test when Texture2D is implemented
template<typename R>
-struct Eg10 {
+struct Eg9 {
R b;
};
-// expecting warning: {{variable of type 'Eg10' bound to register type 'u' does not contain a matching 'uav' resource}}
-Eg10<Texture2D> e10 : register(u0);
+// expecting warning: {{variable of type 'Eg9' bound to register type 'u' does not contain a matching 'uav' resource}}
+Eg9<Texture2D> e9 : register(u0);
// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
-*/
-struct Eg11{
+
+struct Eg10{
RWBuffer<int> a;
RWBuffer<int> b;
};
// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
-Eg11 e11 : register(u9) : register(u10);
+Eg10 e10 : register(u9) : register(u10);
// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
-Eg11 e11a : register(u9, space0) : register(u9, space1);
-struct Eg12{
+Eg10 e10a : register(u9, space0) : register(u9, space1);
+
+struct Eg11{
RWBuffer<int> a;
};
// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
-Eg12 e12 : register(c9);
\ No newline at end of file
+Eg11 e11 : register(c9);
\ No newline at end of file
>From 34a5a14c7da95bc1ab26c21fd4d6c1cf1492e87d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 15 Jul 2024 15:29:14 -0700
Subject: [PATCH 26/43] remove unneeded type
---
clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl | 6 ------
1 file changed, 6 deletions(-)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 29c7fdf593eff1..db3a4b71aaaaf3 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -8,12 +8,6 @@ struct [[hlsl::resource_class(SRV)]] Buffer {
T x;
};
-
-template<typename T>
-struct [[hlsl::resource_class(SRV)]] [[hlsl::resource_class(UAV)]] MultiClassBuffer {
- T x;
-};
-
// TODO: Implement "SamplerState", we use a substitute UDT
// to test the 's' binding type for this test.
struct [[hlsl::resource_class(Sampler)]] SamplerState {
>From 32f42264ed59449582a23584f5e4b9511ab8175b Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 16 Jul 2024 14:20:32 -0700
Subject: [PATCH 27/43] clean up tests, order diagnostics according to spec
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +-
.../resource_binding_attr_error_basic.hlsl | 22 +++++++++++++------
.../resource_binding_attr_error_udt.hlsl | 8 -------
3 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d78e1e073b3f7e..cae3ad8f5d6596 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12347,9 +12347,9 @@ def err_hlsl_unsupported_register_type_and_variable_type: Error<"register bindin
def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register annotations detected for register type '%0'">;
+def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">, InGroup<DisallowLegacyBindingRules>;
def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">, InGroup<DisallowLegacyBindingRules>;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index 056941a651d735..f9c51696c1eb5d 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -1,31 +1,39 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
// expected-error at +1{{unsupported resource register binding 't' on variable of type 'float'}}
-float f1 : register(t9);
+float f1 : register(t0);
+
+
+float f2 : register(c0);
// expected-error at +1{{deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding}}
-float f2 : register(b9);
+float f3 : register(b9);
// expected-error at +1{{deprecated legacy int constant register binding 'i' used}}
-float f3 : register(i9);
+float f4 : register(i9);
+// expected-error at +1{{invalid register type 'x' used; expected 't', 'u', 'b', or 's'}}
+float f5 : register(x9);
cbuffer g_cbuffer1 {
// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
- float f4 : register(c2);
+ float f6 : register(c2);
};
tbuffer g_tbuffer1 {
// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
- float f5 : register(c2);
+ float f7 : register(c2);
};
cbuffer g_cbuffer2 {
// expected-error at +1{{register binding type 'b' not supported for variable of type 'float'}}
- float f6 : register(b2);
+ float f8 : register(b2);
};
tbuffer g_tbuffer2 {
// expected-error at +1{{register binding type 'i' not supported for variable of type 'float'}}
- float f7 : register(i2);
+ float f9 : register(i2);
};
+
+// expected-error at +1{{uav type 'RWBuffer<float>' requires register type 'u', but register type 'c' was used}}
+RWBuffer<float> f10 : register(c3);
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index db3a4b71aaaaf3..b354b46f3eb47c 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -91,7 +91,6 @@ struct Eg9 {
};
// expecting warning: {{variable of type 'Eg9' bound to register type 'u' does not contain a matching 'uav' resource}}
Eg9<Texture2D> e9 : register(u0);
-
// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
@@ -104,10 +103,3 @@ struct Eg10{
Eg10 e10 : register(u9) : register(u10);
// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
Eg10 e10a : register(u9, space0) : register(u9, space1);
-
-struct Eg11{
- RWBuffer<int> a;
-};
-
-// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
-Eg11 e11 : register(c9);
\ No newline at end of file
>From efd62026e3e3bd34f22c6e09b44f51195f726182 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 16 Jul 2024 17:30:18 -0700
Subject: [PATCH 28/43] use enum instead of ints
---
clang/lib/Sema/SemaHLSL.cpp | 44 +++++++++++++++++++------------------
1 file changed, 23 insertions(+), 21 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 027dd5e970bc06..a187af6a994595 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -711,26 +711,28 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
return Flags;
}
+enum RegisterType { SRV, UAV, CBuffer, Sampler, C, I };
+
int getRegisterTypeIndex(StringRef Slot) {
switch (Slot[0]) {
case 't':
case 'T':
- return 0;
+ return RegisterType::SRV;
case 'u':
case 'U':
- return 1;
+ return RegisterType::UAV;
case 'b':
case 'B ':
- return 2;
+ return RegisterType::CBuffer;
case 's':
case 'S':
- return 3;
+ return RegisterType::Sampler;
case 'c':
case 'C':
- return 4;
+ return RegisterType::C;
case 'i':
case 'I':
- return 5;
+ return RegisterType::I;
default:
llvm_unreachable("invalid register type");
}
@@ -816,28 +818,28 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
- if (getRegisterTypeIndex(Slot) != 0)
+ if (getRegisterTypeIndex(Slot) != RegisterType::SRV)
S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
<< getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
<< 0 /*srv*/;
break;
case llvm::hlsl::ResourceClass::UAV:
- if (getRegisterTypeIndex(Slot) != 1)
+ if (getRegisterTypeIndex(Slot) != RegisterType::UAV)
S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
<< getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
<< 1 /*uav*/;
break;
case llvm::hlsl::ResourceClass::CBuffer:
- if (getRegisterTypeIndex(Slot) != 2)
+ if (getRegisterTypeIndex(Slot) != RegisterType::CBuffer)
S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
<< getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
<< 2 /*cbv*/;
break;
case llvm::hlsl::ResourceClass::Sampler:
- if (getRegisterTypeIndex(Slot) != 3)
+ if (getRegisterTypeIndex(Slot) != RegisterType::Sampler)
S.Diag(TheDecl->getLocation(),
diag::err_hlsl_mismatching_register_type_and_resource_type)
<< getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
@@ -851,23 +853,23 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// including the legacy "i" and "b" register types.
if (Flags.Basic) {
if (Flags.DefaultGlobals) {
- if (getRegisterTypeIndex(Slot) == 2)
+ if (getRegisterTypeIndex(Slot) == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
- if (getRegisterTypeIndex(Slot) == 5)
+ if (getRegisterTypeIndex(Slot) == RegisterType::I)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
}
- if (getRegisterTypeIndex(Slot) == 4) {
+ if (getRegisterTypeIndex(Slot) == RegisterType::C) {
if (!Flags.DefaultGlobals) {
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
}
- } else if (getRegisterTypeIndex(Slot) == 0) {
+ } else if (getRegisterTypeIndex(Slot) == RegisterType::SRV) {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 0 << getHLSLResourceTypeStr(S, TheDecl);
- } else if (getRegisterTypeIndex(Slot) == 1) {
+ } else if (getRegisterTypeIndex(Slot) == RegisterType::UAV) {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 1 << getHLSLResourceTypeStr(S, TheDecl);
- } else if (getRegisterTypeIndex(Slot) == 3) {
+ } else if (getRegisterTypeIndex(Slot) == RegisterType::Sampler) {
S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
<< 3 << getHLSLResourceTypeStr(S, TheDecl);
// any other register type should emit
@@ -882,7 +884,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// finally, we handle the udt case
if (Flags.UDT) {
switch (getRegisterTypeIndex(Slot)) {
- case 0: {
+ case RegisterType::SRV: {
if (!Flags.SRV) {
S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -890,7 +892,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 1: {
+ case RegisterType::UAV: {
if (!Flags.UAV) {
S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -898,7 +900,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 2: {
+ case RegisterType::CBuffer: {
if (!Flags.CBV) {
S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -906,7 +908,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 3: {
+ case RegisterType::Sampler: {
if (!Flags.Sampler) {
S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_UDT_missing_resource_type_member)
@@ -914,7 +916,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
}
break;
}
- case 4: {
+ case RegisterType::C: {
if (!Flags.ContainsNumeric)
S.Diag(TheDecl->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
break;
>From f7495a549f167a0f7f887e9ab34143a89f2df393 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 5 Aug 2024 22:16:51 -0700
Subject: [PATCH 29/43] update implementation to match the spec
---
.../clang/Basic/DiagnosticSemaKinds.td | 17 +--
clang/lib/Sema/SemaHLSL.cpp | 125 +++++++++---------
.../SemaHLSL/resource_binding_attr_error.hlsl | 8 +-
.../resource_binding_attr_error_basic.hlsl | 18 +--
.../resource_binding_attr_error_resource.hlsl | 8 +-
.../resource_binding_attr_error_udt.hlsl | 12 +-
6 files changed, 93 insertions(+), 95 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cae3ad8f5d6596..3c51d76174936c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12342,16 +12342,13 @@ def err_hlsl_missing_semantic_annotation : Error<
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
-def err_hlsl_mismatching_register_type_and_variable_type: Error<"unsupported resource register binding '%select{t|u|b|s}0' on variable of type '%1'">;
-def err_hlsl_unsupported_register_type_and_variable_type: Error<"register binding type '%0' not supported for variable of type '%1'">;
-def err_hlsl_mismatching_register_type_and_resource_type: Error<"%select{srv|uav|cbv|sampler}2 type '%0' requires register type '%select{t|u|b|s}2', but register type '%1' was used">;
-def err_hlsl_unsupported_register_type_and_resource_type: Error<"invalid register type '%0' used; expected 't', 'u', 'b', or 's'">;
-def err_hlsl_conflicting_register_annotations: Error<"conflicting register annotations: multiple register annotations detected for register type '%0'">;
-def warn_hlsl_register_type_c_not_in_global_scope: Warning<"register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_deprecated_register_type_b: Warning<"deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_deprecated_register_type_i: Warning<"deprecated legacy int constant register binding 'i' used">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_UDT_missing_resource_type_member: Warning<"variable of type '%0' bound to register type '%1' does not contain a matching '%select{srv|uav|cbv|sampler}2' resource">, InGroup<DisallowLegacyBindingRules>;
-def warn_hlsl_UDT_missing_basic_type: Warning<"register 'c' used on type with no contents to allocate in a constant buffer">, InGroup<DisallowLegacyBindingRules>;
+def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{srv resources|uav resources|constant buffer resources|sampler state|numeric types}0">, InGroup<DisallowLegacyBindingRules>;
+def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{srv resources|uav resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
+def err_hlsl_binding_type_invalid: Error<"binding type '%0' is invalid">;
+def err_hlsl_duplicate_register_annotation: Error<"binding type '%select{t|u|b|s|c|i}0' cannot be applied more than once">;
+def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<DisallowLegacyBindingRules>, DefaultError;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a187af6a994595..64bd3b0b471aa0 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -752,8 +752,8 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
int registerTypeIndex = getRegisterTypeIndex(attr->getSlot());
if (RegisterTypesDetected[registerTypeIndex]) {
S.Diag(TheDecl->getLocation(),
- diag::err_hlsl_conflicting_register_annotations)
- << attr->getSlot().substr(0, 1);
+ diag::err_hlsl_duplicate_register_annotation)
+ << registerTypeIndex;
} else {
RegisterTypesDetected[registerTypeIndex] = true;
}
@@ -774,8 +774,8 @@ std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
}
}
-static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
- Decl *TheDecl, StringRef &Slot) {
+static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
+ Decl *TheDecl, StringRef &Slot) {
// Samplers, UAVs, and SRVs are VarDecl types
VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
@@ -793,10 +793,16 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
1 &&
"only one resource analysis result should be expected");
+ int regType = getRegisterTypeIndex(Slot);
+
// first, if "other" is set, emit an error
if (Flags.Other) {
- S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot.substr(0, 1) << getHLSLResourceTypeStr(S, TheDecl);
+ if (regType == RegisterType::I) {
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_invalid)
+ << "i";
+ return;
+ }
+ S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
return;
}
@@ -806,6 +812,11 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
if (Flags.Resource) {
+ if (regType == RegisterType::I) {
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_invalid)
+ << "i";
+ return;
+ }
const HLSLResourceAttr *resAttr =
getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
const HLSLResourceClassAttr *resClassAttr =
@@ -818,32 +829,24 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
- if (getRegisterTypeIndex(Slot) != RegisterType::SRV)
- S.Diag(TheDecl->getLocation(),
- diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
- << 0 /*srv*/;
+ if (regType != RegisterType::SRV)
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
+ << regType;
break;
case llvm::hlsl::ResourceClass::UAV:
- if (getRegisterTypeIndex(Slot) != RegisterType::UAV)
- S.Diag(TheDecl->getLocation(),
- diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
- << 1 /*uav*/;
+ if (regType != RegisterType::UAV)
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
+ << regType;
break;
case llvm::hlsl::ResourceClass::CBuffer:
- if (getRegisterTypeIndex(Slot) != RegisterType::CBuffer)
- S.Diag(TheDecl->getLocation(),
- diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
- << 2 /*cbv*/;
+ if (regType != RegisterType::CBuffer)
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
+ << regType;
break;
case llvm::hlsl::ResourceClass::Sampler:
- if (getRegisterTypeIndex(Slot) != RegisterType::Sampler)
- S.Diag(TheDecl->getLocation(),
- diag::err_hlsl_mismatching_register_type_and_resource_type)
- << getHLSLResourceTypeStr(S, TheDecl) << Slot.substr(0, 1)
- << 3 /*sampler*/;
+ if (regType != RegisterType::Sampler)
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
+ << regType;
break;
}
return;
@@ -853,79 +856,78 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// including the legacy "i" and "b" register types.
if (Flags.Basic) {
if (Flags.DefaultGlobals) {
- if (getRegisterTypeIndex(Slot) == RegisterType::CBuffer)
+ if (regType == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
- if (getRegisterTypeIndex(Slot) == RegisterType::I)
+ else if (regType == RegisterType::I)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+ else if (regType != RegisterType::C)
+ S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
+ return;
}
- if (getRegisterTypeIndex(Slot) == RegisterType::C) {
- if (!Flags.DefaultGlobals) {
- S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_not_in_global_scope);
- }
- } else if (getRegisterTypeIndex(Slot) == RegisterType::SRV) {
- S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 0 << getHLSLResourceTypeStr(S, TheDecl);
- } else if (getRegisterTypeIndex(Slot) == RegisterType::UAV) {
- S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 1 << getHLSLResourceTypeStr(S, TheDecl);
- } else if (getRegisterTypeIndex(Slot) == RegisterType::Sampler) {
- S.Diag(ArgLoc, diag::err_hlsl_mismatching_register_type_and_variable_type)
- << 3 << getHLSLResourceTypeStr(S, TheDecl);
- // any other register type should emit
- // err_hlsl_unsupported_register_type_and_variable_type
- } else if (!Flags.DefaultGlobals) {
- S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot.substr(0, 1) << getHLSLResourceTypeStr(S, TheDecl);
- }
+ if (regType == RegisterType::C)
+ S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
+ else if (regType == RegisterType::I)
+ S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+ else
+ S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
+
return;
}
// finally, we handle the udt case
if (Flags.UDT) {
+ if (regType == RegisterType::I) {
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_invalid)
+ << "i";
+ return;
+ }
switch (getRegisterTypeIndex(Slot)) {
case RegisterType::SRV: {
if (!Flags.SRV) {
S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, TheDecl) << "t" << 0;
+ diag::warn_hlsl_user_defined_type_missing_member)
+ << regType;
}
break;
}
case RegisterType::UAV: {
if (!Flags.UAV) {
S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, TheDecl) << "u" << 1;
+ diag::warn_hlsl_user_defined_type_missing_member)
+ << getHLSLResourceTypeStr(S, TheDecl) << regType;
}
break;
}
case RegisterType::CBuffer: {
if (!Flags.CBV) {
S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, TheDecl) << "b" << 2;
+ diag::warn_hlsl_user_defined_type_missing_member)
+ << getHLSLResourceTypeStr(S, TheDecl) << regType;
}
break;
}
case RegisterType::Sampler: {
if (!Flags.Sampler) {
S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_UDT_missing_resource_type_member)
- << getHLSLResourceTypeStr(S, TheDecl) << "s" << 3;
+ diag::warn_hlsl_user_defined_type_missing_member)
+ << getHLSLResourceTypeStr(S, TheDecl) << regType;
}
break;
}
case RegisterType::C: {
if (!Flags.ContainsNumeric)
- S.Diag(TheDecl->getLocation(), diag::warn_hlsl_UDT_missing_basic_type);
+ S.Diag(TheDecl->getLocation(),
+ diag::warn_hlsl_user_defined_type_missing_member)
+ << regType;
break;
}
- default: {
+ case RegisterType::I: {
S.Diag(TheDecl->getLocation(),
- diag::err_hlsl_unsupported_register_type_and_variable_type)
- << Slot.front() << getHLSLResourceTypeStr(S, TheDecl);
+ diag::warn_hlsl_deprecated_register_type_i);
}
+ default:
+ llvm_unreachable("invalid register type");
}
return;
}
@@ -984,8 +986,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
case 'I':
break;
default:
- Diag(ArgLoc, diag::err_hlsl_unsupported_register_type_and_resource_type)
- << Slot.substr(0, 1);
+ Diag(ArgLoc, diag::err_hlsl_binding_type_invalid) << Slot.substr(0, 1);
return;
}
@@ -1008,7 +1009,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
return;
}
- DiagnoseHLSLResourceRegType(SemaRef, ArgLoc, TheDecl, Slot);
+ DiagnoseHLSLRegisterAttribute(SemaRef, ArgLoc, TheDecl, Slot);
HLSLResourceBindingAttr *NewAttr =
HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index b61e11243a313b..d1710699284a53 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -4,7 +4,7 @@
// specifying a constant register binding offset within the $Globals cbuffer, which is legacy behavior from DX9.
float a : register(c0);
-// expected-error at +1 {{cbv type 'cbuffer' requires register type 'b', but register type 'i' was used}}
+// expected-error at +1 {{binding type 'i' is invalid}}
cbuffer b : register(i0) {
}
@@ -44,7 +44,7 @@ float x[2] : register(c2); // valid
float y[2][2] : register(c3); // valid
float z[2][2][3] : register(c4); // valid
-// expected-error at +1 {{register binding type 'c' not supported for variable of type 'groupshared float[10]}}
+// expected-error at +1 {{binding type 'c' only applies to numeric variables in the global scope}}
groupshared float fa[10] : register(c5);
void foo() {
@@ -56,7 +56,7 @@ void foo2() {
extern RWBuffer<float> U2 : register(u5);
}
-// expected-error at +1 {{unsupported resource register binding 'u' on variable of type 'float'}}
+// expected-error at +1 {{binding type 'u' only applies to uav resources}}
float b : register(u0, space1);
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
@@ -69,5 +69,5 @@ struct S {
RWBuffer<float> U : register(u3);
};
-// expected-error at +1 {{invalid register type 'z' used; expected 't', 'u', 'b', or 's'}}
+// expected-error at +1 {{binding type 'z' is invalid}}
RWBuffer<float> U3 : register(z5);
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index f9c51696c1eb5d..d797f22c8ccba0 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -1,39 +1,39 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-// expected-error at +1{{unsupported resource register binding 't' on variable of type 'float'}}
+// expected-error at +1{{binding type 't' only applies to srv resources}}
float f1 : register(t0);
float f2 : register(c0);
-// expected-error at +1{{deprecated legacy bool constant register binding 'b' used. 'b' is only used for constant buffer resource binding}}
+// expected-error at +1{{binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported}}
float f3 : register(b9);
-// expected-error at +1{{deprecated legacy int constant register binding 'i' used}}
+// expected-error at +1{{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
float f4 : register(i9);
-// expected-error at +1{{invalid register type 'x' used; expected 't', 'u', 'b', or 's'}}
+// expected-error at +1{{binding type 'x' is invalid}}
float f5 : register(x9);
cbuffer g_cbuffer1 {
-// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
+// expected-error at +1{{binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?}}
float f6 : register(c2);
};
tbuffer g_tbuffer1 {
-// expected-error at +1{{register binding 'c' ignored inside cbuffer/tbuffer declarations; use pack_offset instead}}
+// expected-error at +1{{binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?}}
float f7 : register(c2);
};
cbuffer g_cbuffer2 {
-// expected-error at +1{{register binding type 'b' not supported for variable of type 'float'}}
+// expected-error at +1{{binding type 'b' only applies to constant buffer resources}}
float f8 : register(b2);
};
tbuffer g_tbuffer2 {
-// expected-error at +1{{register binding type 'i' not supported for variable of type 'float'}}
+// expected-error at +1{{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
float f9 : register(i2);
};
-// expected-error at +1{{uav type 'RWBuffer<float>' requires register type 'u', but register type 'c' was used}}
+// expected-error at +1{{binding type 'c' only applies to numeric variables in the global scope}}
RWBuffer<float> f10 : register(c3);
\ No newline at end of file
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index 2be78b44314993..81580a341e8acc 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -3,10 +3,10 @@
// This test validates the diagnostics that are emitted when a variable with a "resource" type
// is bound to a register using the register annotation
-// expected-error at +1 {{uav type 'RWBuffer<int>' requires register type 'u', but register type 'b' was used}}
+// expected-error at +1 {{binding type 'b' only applies to constant buffer resources}}
RWBuffer<int> a : register(b2, space1);
-// expected-error at +1 {{uav type 'RWBuffer<int>' requires register type 'u', but register type 't' was used}}
+// expected-error at +1 {{binding type 't' only applies to srv resources}}
RWBuffer<int> b : register(t2, space1);
// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
@@ -49,14 +49,14 @@ RWBuffer<int> b : register(t2, space1);
// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2 : register(T2);
-// expected-error at +1 {{cbv type 'cbuffer' requires register type 'b', but register type 's' was used}}
+// expected-error at +1 {{binding type 's' only applies to sampler state}}
cbuffer f : register(s2, space1) {}
// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
// Can this type just be Sampler instead of SamplerState?
// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
-// expected-error at +1 {{srv type 'tbuffer' requires register type 't', but register type 's' was used}}
+// expected-error at +1 {{binding type 's' only applies to sampler state}}
tbuffer f : register(s2, space1) {}
// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index b354b46f3eb47c..e4eff6e98bd52f 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -59,7 +59,7 @@ Eg4 e4 : register(s5);
struct Eg5 {
float f;
};
-// expected-warning at +1{{variable of type 'Eg5' bound to register type 't' does not contain a matching 'srv' resource}}
+// expected-warning at +1{{binding type 't' only applies to types containing srv resources}}
Eg5 e5 : register(t0);
struct Eg6 {
@@ -68,13 +68,13 @@ struct Eg6 {
};
Bar b;
};
-// expected-warning at +1{{variable of type 'Eg6' bound to register type 't' does not contain a matching 'srv' resource}}
+// expected-warning at +1{{binding type 't' only applies to types containing srv resources}}
Eg6 e6 : register(t0);
struct Eg7 {
RWBuffer<int> a;
};
-// expected-warning at +1{{register 'c' used on type with no contents to allocate in a constant buffer}}
+// expected-warning at +1{{binding type 'c' only applies to types containing numeric types}}
Eg7 e7 : register(c0);
@@ -89,7 +89,7 @@ template<typename R>
struct Eg9 {
R b;
};
-// expecting warning: {{variable of type 'Eg9' bound to register type 'u' does not contain a matching 'uav' resource}}
+// expecting warning: {{binding type 'u' only applies to types containing uav resources}}
Eg9<Texture2D> e9 : register(u0);
// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
@@ -99,7 +99,7 @@ struct Eg10{
RWBuffer<int> b;
};
-// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
+// expected-error at +1{{binding type 'u' cannot be applied more than once}}
Eg10 e10 : register(u9) : register(u10);
-// expected-error at +1{{conflicting register annotations: multiple register annotations detected for register type 'u'}}
+// expected-error at +1{{binding type 'u' cannot be applied more than once}}
Eg10 e10a : register(u9, space0) : register(u9, space1);
>From 927c67dd7d8cde6a2b01acafab69d2012ac07dcc Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 6 Aug 2024 11:13:48 -0700
Subject: [PATCH 30/43] emit warning for i rather than error to conform to spec
---
clang/lib/Sema/SemaHLSL.cpp | 12 ++++++------
clang/test/SemaHLSL/resource_binding_attr_error.hlsl | 2 +-
.../resource_binding_attr_error_silence_diags.hlsl | 1 +
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 64bd3b0b471aa0..7449c851cb981e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -798,8 +798,8 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
// first, if "other" is set, emit an error
if (Flags.Other) {
if (regType == RegisterType::I) {
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_invalid)
- << "i";
+ S.Diag(TheDecl->getLocation(),
+ diag::warn_hlsl_deprecated_register_type_i);
return;
}
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
@@ -813,8 +813,8 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
// annotation is compatible with the variable's resource type.
if (Flags.Resource) {
if (regType == RegisterType::I) {
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_invalid)
- << "i";
+ S.Diag(TheDecl->getLocation(),
+ diag::warn_hlsl_deprecated_register_type_i);
return;
}
const HLSLResourceAttr *resAttr =
@@ -878,8 +878,8 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
// finally, we handle the udt case
if (Flags.UDT) {
if (regType == RegisterType::I) {
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_invalid)
- << "i";
+ S.Diag(TheDecl->getLocation(),
+ diag::warn_hlsl_deprecated_register_type_i);
return;
}
switch (getRegisterTypeIndex(Slot)) {
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index d1710699284a53..33eaa152cf5be1 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -4,7 +4,7 @@
// specifying a constant register binding offset within the $Globals cbuffer, which is legacy behavior from DX9.
float a : register(c0);
-// expected-error at +1 {{binding type 'i' is invalid}}
+// expected-error at +1 {{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
cbuffer b : register(i0) {
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
index 4e227c85e6bcf8..7bcde02728f6d7 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
@@ -16,6 +16,7 @@ struct Eg12{
Eg12 e12 : register(c9);
+Eg12 bar : register(i1);
struct Eg7 {
struct Bar {
>From 7466f73221c8b79d5a1cff7d396c07f3591ea774 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 6 Aug 2024 12:13:45 -0700
Subject: [PATCH 31/43] remove space variant test
---
clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index e4eff6e98bd52f..88d4a3be04a567 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -101,5 +101,3 @@ struct Eg10{
// expected-error at +1{{binding type 'u' cannot be applied more than once}}
Eg10 e10 : register(u9) : register(u10);
-// expected-error at +1{{binding type 'u' cannot be applied more than once}}
-Eg10 e10a : register(u9, space0) : register(u9, space1);
>From 504356b5e5627ac4fa743a9918e63ecc7788a1f7 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 6 Aug 2024 17:13:46 -0700
Subject: [PATCH 32/43] check for resource attr in fields
---
clang/lib/Sema/HLSLExternalSemaSource.cpp | 11 +++------
clang/lib/Sema/SemaHLSL.cpp | 27 +++++++++++++++++++++++
2 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 886e25e4850bb9..9aacbe4ad9548e 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -500,12 +500,9 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
CXXRecordDecl *Decl;
- Decl =
- BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
- .addSimpleTemplateParams(*SemaPtr, {"element_type"})
- .annotateHLSLResource(ResourceClass::UAV, ResourceKind::TypedBuffer,
- /*IsROV=*/false)
- .Record;
+ Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer")
+ .addSimpleTemplateParams(*SemaPtr, {"element_type"})
+ .Record;
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
@@ -518,8 +515,6 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
Decl =
BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer")
.addSimpleTemplateParams(*SemaPtr, {"element_type"})
- .annotateHLSLResource(ResourceClass::UAV, ResourceKind::TypedBuffer,
- /*IsROV=*/true)
.Record;
onCompletion(Decl, [this](CXXRecordDecl *Decl) {
setupBufferType(Decl, *SemaPtr, ResourceClass::UAV,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7449c851cb981e..71bace3458282c 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -520,7 +520,17 @@ getHLSLResourceClassAttrFromEitherDecl(VarDecl *VD,
const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
if (!TheRecordDecl)
return nullptr;
+
+ // the resource class attr could be on the record decl itself or on one of
+ // its fields (the resource handle, most commonly)
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceClassAttr>();
+ if (Attr)
+ break;
+ }
+ }
return Attr;
} else if (CBufferOrTBuffer) {
const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
@@ -538,7 +548,17 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
if (!TheRecordDecl)
return nullptr;
+
+ // the resource attr could be on the record decl itself or on one of
+ // its fields (the resource handle, most commonly)
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceAttr>();
+ if (Attr)
+ break;
+ }
+ }
return Attr;
} else if (CBufferOrTBuffer) {
const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
@@ -572,6 +592,13 @@ void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
TheRecordDecl = TheRecordDecl->getCanonicalDecl();
const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceClassAttr>();
+ if (Attr)
+ break;
+ }
+ }
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
>From 10dc55bd253f5381fea375ced60b521ec4c23eb3 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Thu, 15 Aug 2024 16:28:37 -0700
Subject: [PATCH 33/43] address Damyan, clean up, and also retain info about
conflicts to not emit duplicate diagnostics
---
clang/lib/Sema/SemaHLSL.cpp | 305 +++++++-----------
.../resource_binding_attr_error_udt.hlsl | 102 +++---
2 files changed, 172 insertions(+), 235 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 71bace3458282c..6ce25f6dd31ae0 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -38,6 +38,7 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
HLSLBufferDecl *Result = HLSLBufferDecl::Create(
getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
+ // if CBuffer is false, then it's a TBuffer
auto RC = CBuffer ? llvm::hlsl::ResourceClass::CBuffer
: llvm::hlsl::ResourceClass::SRV;
auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
@@ -482,7 +483,7 @@ struct RegisterBindingFlags {
bool DefaultGlobals = false;
};
-bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
+static bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
if (!TheDecl)
return false;
@@ -495,7 +496,7 @@ bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
return false;
}
-const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
+static const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
assert(Ty && "Resource class must have an element type.");
@@ -512,37 +513,26 @@ const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
return TheRecordDecl;
}
-const HLSLResourceClassAttr *
-getHLSLResourceClassAttrFromEitherDecl(VarDecl *VD,
- HLSLBufferDecl *CBufferOrTBuffer) {
-
- if (VD) {
- const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
- if (!TheRecordDecl)
- return nullptr;
-
- // the resource class attr could be on the record decl itself or on one of
- // its fields (the resource handle, most commonly)
- const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
- if (!Attr) {
- for (auto *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<HLSLResourceClassAttr>();
- if (Attr)
- break;
- }
- }
- return Attr;
- } else if (CBufferOrTBuffer) {
- const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
- return Attr;
+static void setResourceClassFlagsFromDeclResourceClass(
+ RegisterBindingFlags &Flags, llvm::hlsl::ResourceClass DeclResourceClass) {
+ switch (DeclResourceClass) {
+ case llvm::hlsl::ResourceClass::SRV:
+ Flags.SRV = true;
+ break;
+ case llvm::hlsl::ResourceClass::UAV:
+ Flags.UAV = true;
+ break;
+ case llvm::hlsl::ResourceClass::CBuffer:
+ Flags.CBV = true;
+ break;
+ case llvm::hlsl::ResourceClass::Sampler:
+ Flags.Sampler = true;
+ break;
}
- llvm_unreachable("one of the two conditions should be true.");
- return nullptr;
}
-const HLSLResourceAttr *
-getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
- HLSLBufferDecl *CBufferOrTBuffer) {
+template <typename T>
+static const T *getSpecifiedHLSLAttrFromVarDecl(VarDecl *VD) {
if (VD) {
const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
@@ -551,24 +541,21 @@ getHLSLResourceAttrFromEitherDecl(VarDecl *VD,
// the resource attr could be on the record decl itself or on one of
// its fields (the resource handle, most commonly)
- const auto *Attr = TheRecordDecl->getAttr<HLSLResourceAttr>();
+ const auto *Attr = TheRecordDecl->getAttr<T>();
if (!Attr) {
for (auto *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<HLSLResourceAttr>();
+ Attr = FD->getAttr<T>();
if (Attr)
break;
}
}
return Attr;
- } else if (CBufferOrTBuffer) {
- const auto *Attr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
- return Attr;
}
- llvm_unreachable("one of the two conditions should be true.");
+ llvm_unreachable("VD should not be null");
return nullptr;
}
-void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
+static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
// if the member's type is a numeric type, set the ContainsNumeric flag
if (TheQualTy->isIntegralOrEnumerationType() || TheQualTy->isFloatingType()) {
Flags.ContainsNumeric = true;
@@ -600,63 +587,38 @@ void traverseType(QualType TheQualTy, RegisterBindingFlags &Flags) {
}
}
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
- switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV:
- Flags.SRV = true;
- break;
- case llvm::hlsl::ResourceClass::UAV:
- Flags.UAV = true;
- break;
- case llvm::hlsl::ResourceClass::CBuffer:
- Flags.CBV = true;
- break;
- case llvm::hlsl::ResourceClass::Sampler:
- Flags.Sampler = true;
- break;
- }
+ setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
resClassSet = true;
}
// otherwise, check if the member has a resource class attr
else if (auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
- switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV:
- Flags.SRV = true;
- break;
- case llvm::hlsl::ResourceClass::UAV:
- Flags.UAV = true;
- break;
- case llvm::hlsl::ResourceClass::CBuffer:
- Flags.CBV = true;
- break;
- case llvm::hlsl::ResourceClass::Sampler:
- Flags.Sampler = true;
- break;
- }
+ setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
resClassSet = true;
}
if (!resClassSet) {
for (auto Field : SubRecordDecl->fields()) {
- traverseType(Field->getType(), Flags);
+ setFlagsFromType(Field->getType(), Flags);
}
}
}
-void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &Flags,
- const RecordDecl *RD) {
+static void setResourceClassFlagsFromRecordDecl(RegisterBindingFlags &Flags,
+ const RecordDecl *RD) {
if (!RD)
return;
if (RD->isCompleteDefinition()) {
for (auto Field : RD->fields()) {
QualType T = Field->getType();
- traverseType(T, Flags);
+ setFlagsFromType(T, Flags);
}
}
}
-RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
+static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
+ Decl *TheDecl) {
// Cbuffers and Tbuffers are HLSLBufferDecl types
HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
@@ -665,7 +627,7 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
assert(((TheVarDecl && !CBufferOrTBuffer) ||
(!TheVarDecl && CBufferOrTBuffer)) &&
- "either VD or CBufferOrTBuffer should be set");
+ "either TheVarDecl or CBufferOrTBuffer should be set");
RegisterBindingFlags Flags;
@@ -698,7 +660,7 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
Flags.SRV = true;
} else if (TheVarDecl) {
const HLSLResourceClassAttr *resClassAttr =
- getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+ getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
while (TheBaseType->isArrayType())
TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
@@ -707,20 +669,7 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
llvm::hlsl::ResourceClass DeclResourceClass =
resClassAttr->getResourceClass();
Flags.Resource = true;
- switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV:
- Flags.SRV = true;
- break;
- case llvm::hlsl::ResourceClass::UAV:
- Flags.UAV = true;
- break;
- case llvm::hlsl::ResourceClass::CBuffer:
- Flags.CBV = true;
- break;
- case llvm::hlsl::ResourceClass::Sampler:
- Flags.Sampler = true;
- break;
- }
+ setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
} else {
if (TheBaseType->isArithmeticType())
Flags.Basic = true;
@@ -740,7 +689,7 @@ RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S, Decl *TheDecl) {
enum RegisterType { SRV, UAV, CBuffer, Sampler, C, I };
-int getRegisterTypeIndex(StringRef Slot) {
+static RegisterType getRegisterTypeIndex(StringRef Slot) {
switch (Slot[0]) {
case 't':
case 'T':
@@ -757,38 +706,46 @@ int getRegisterTypeIndex(StringRef Slot) {
case 'c':
case 'C':
return RegisterType::C;
- case 'i':
- case 'I':
- return RegisterType::I;
+ // we don't need to check for 'i' here, because
+ // any attribute that has the 'i' register type
+ // will be immediately caught by handleResourceBindingAttr
+ // so it's impossible for the decl to already have an 'i' register type
default:
llvm_unreachable("invalid register type");
}
}
static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
- StringRef &Slot) {
- // make sure that there are no tworegister annotations
+ RegisterType regType) {
+ // make sure that there are no two register annotations
// applied to the decl with the same register type
- bool RegisterTypesDetected[6] = {false};
- RegisterTypesDetected[getRegisterTypeIndex(Slot)] = true;
+ bool RegisterTypesDetected[5] = {false};
+ RegisterTypesDetected[regType] = true;
+
+ // we need a static map to keep track of previous conflicts
+ // so that we don't emit the same error multiple times
+ static std::map<Decl *, std::set<RegisterType>> PreviousConflicts;
for (auto it = TheDecl->attr_begin(); it != TheDecl->attr_end(); ++it) {
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
- int registerTypeIndex = getRegisterTypeIndex(attr->getSlot());
- if (RegisterTypesDetected[registerTypeIndex]) {
+ RegisterType regType = getRegisterTypeIndex(attr->getSlot());
+ if (RegisterTypesDetected[regType]) {
+ if (PreviousConflicts[TheDecl].count(regType))
+ continue;
S.Diag(TheDecl->getLocation(),
diag::err_hlsl_duplicate_register_annotation)
- << registerTypeIndex;
+ << regType;
+ PreviousConflicts[TheDecl].insert(regType);
} else {
- RegisterTypesDetected[registerTypeIndex] = true;
+ RegisterTypesDetected[regType] = true;
}
}
}
}
-std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
+static std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
@@ -802,7 +759,7 @@ std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
}
static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
- Decl *TheDecl, StringRef &Slot) {
+ Decl *TheDecl, RegisterType regType) {
// Samplers, UAVs, and SRVs are VarDecl types
VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
@@ -820,61 +777,52 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
1 &&
"only one resource analysis result should be expected");
- int regType = getRegisterTypeIndex(Slot);
-
// first, if "other" is set, emit an error
if (Flags.Other) {
- if (regType == RegisterType::I) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_deprecated_register_type_i);
- return;
- }
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
return;
}
// next, if multiple register annotations exist, check that none conflict.
- ValidateMultipleRegisterAnnotations(S, TheDecl, Slot);
+ ValidateMultipleRegisterAnnotations(S, TheDecl, regType);
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
if (Flags.Resource) {
- if (regType == RegisterType::I) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_deprecated_register_type_i);
- return;
+ const HLSLResourceAttr *resAttr = nullptr;
+ const HLSLResourceClassAttr *resClassAttr = nullptr;
+ if (CBufferOrTBuffer) {
+ resAttr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
+ resClassAttr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
+ } else if (TheVarDecl) {
+ resAttr = getSpecifiedHLSLAttrFromVarDecl<HLSLResourceAttr>(TheVarDecl);
+ resClassAttr =
+ getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
}
- const HLSLResourceAttr *resAttr =
- getHLSLResourceAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
- const HLSLResourceClassAttr *resClassAttr =
- getHLSLResourceClassAttrFromEitherDecl(TheVarDecl, CBufferOrTBuffer);
+
assert(resAttr && resClassAttr &&
"any decl that set the resource flag on analysis should "
"have a resource attribute and resource class attribute attached.");
const llvm::hlsl::ResourceClass DeclResourceClass =
resClassAttr->getResourceClass();
- switch (DeclResourceClass) {
- case llvm::hlsl::ResourceClass::SRV:
- if (regType != RegisterType::SRV)
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
- << regType;
- break;
- case llvm::hlsl::ResourceClass::UAV:
- if (regType != RegisterType::UAV)
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
- << regType;
- break;
- case llvm::hlsl::ResourceClass::CBuffer:
- if (regType != RegisterType::CBuffer)
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
- << regType;
- break;
- case llvm::hlsl::ResourceClass::Sampler:
- if (regType != RegisterType::Sampler)
- S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
- << regType;
- break;
+ // confirm that the register type is bound to its expected resource class
+ static llvm::SmallVector<RegisterType>
+ ExpectedRegisterTypesForResourceClass = {
+ RegisterType::SRV,
+ RegisterType::UAV,
+ RegisterType::CBuffer,
+ RegisterType::Sampler,
+ };
+ assert((int)DeclResourceClass <
+ ExpectedRegisterTypesForResourceClass.size() &&
+ "DeclResourceClass has unexpected value");
+
+ RegisterType ExpectedRegisterType =
+ ExpectedRegisterTypesForResourceClass[(int)DeclResourceClass];
+ if (regType != ExpectedRegisterType) {
+ S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
+ << regType;
}
return;
}
@@ -885,8 +833,6 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
if (Flags.DefaultGlobals) {
if (regType == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
- else if (regType == RegisterType::I)
- S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
else if (regType != RegisterType::C)
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
return;
@@ -894,8 +840,6 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
if (regType == RegisterType::C)
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
- else if (regType == RegisterType::I)
- S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
else
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
@@ -904,58 +848,16 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
// finally, we handle the udt case
if (Flags.UDT) {
- if (regType == RegisterType::I) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_deprecated_register_type_i);
- return;
- }
- switch (getRegisterTypeIndex(Slot)) {
- case RegisterType::SRV: {
- if (!Flags.SRV) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_user_defined_type_missing_member)
- << regType;
- }
- break;
- }
- case RegisterType::UAV: {
- if (!Flags.UAV) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_user_defined_type_missing_member)
- << getHLSLResourceTypeStr(S, TheDecl) << regType;
- }
- break;
- }
- case RegisterType::CBuffer: {
- if (!Flags.CBV) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_user_defined_type_missing_member)
- << getHLSLResourceTypeStr(S, TheDecl) << regType;
- }
- break;
- }
- case RegisterType::Sampler: {
- if (!Flags.Sampler) {
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_user_defined_type_missing_member)
- << getHLSLResourceTypeStr(S, TheDecl) << regType;
- }
- break;
- }
- case RegisterType::C: {
- if (!Flags.ContainsNumeric)
- S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_user_defined_type_missing_member)
- << regType;
- break;
- }
- case RegisterType::I: {
+ const SmallVector<bool> ExpectedRegisterTypesForUDT = {
+ Flags.SRV, Flags.UAV, Flags.CBV, Flags.Sampler, Flags.ContainsNumeric};
+ assert((int)regType < ExpectedRegisterTypesForUDT.size() &&
+ "regType has unexpected value");
+
+ if (!ExpectedRegisterTypesForUDT[(int)regType])
S.Diag(TheDecl->getLocation(),
- diag::warn_hlsl_deprecated_register_type_i);
- }
- default:
- llvm_unreachable("invalid register type");
- }
+ diag::warn_hlsl_user_defined_type_missing_member)
+ << regType;
+
return;
}
}
@@ -996,22 +898,35 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
Slot = Str;
}
+ RegisterType regType;
+
// Validate.
if (!Slot.empty()) {
switch (Slot[0]) {
case 't':
case 'T':
+ regType = RegisterType::SRV;
+ break;
case 'u':
case 'U':
+ regType = RegisterType::UAV;
+ break;
case 'b':
- case 'B':
+ case 'B ':
+ regType = RegisterType::CBuffer;
+ break;
case 's':
case 'S':
+ regType = RegisterType::Sampler;
+ break;
case 'c':
case 'C':
+ regType = RegisterType::C;
+ break;
case 'i':
case 'I':
- break;
+ Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+ return;
default:
Diag(ArgLoc, diag::err_hlsl_binding_type_invalid) << Slot.substr(0, 1);
return;
@@ -1036,7 +951,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
return;
}
- DiagnoseHLSLRegisterAttribute(SemaRef, ArgLoc, TheDecl, Slot);
+ DiagnoseHLSLRegisterAttribute(SemaRef, ArgLoc, TheDecl, regType);
HLSLResourceBindingAttr *NewAttr =
HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 88d4a3be04a567..7d0a9dd1b19e6f 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -4,56 +4,59 @@
// to test the 't' binding type for this test.
template<typename T>
-struct [[hlsl::resource_class(SRV)]] Buffer {
+struct [[hlsl::resource_class(SRV)]] MyTemplatedSRV {
T x;
};
-// TODO: Implement "SamplerState", we use a substitute UDT
-// to test the 's' binding type for this test.
-struct [[hlsl::resource_class(Sampler)]] SamplerState {
+struct [[hlsl::resource_class(SRV)]] MySRV {
int x;
};
-// TODO: Implement "Texture2D", we use a substitute UDT
-// to test a non-templated 't' binding type for this test.
-struct [[hlsl::resource_class(UAV)]] Texture2D {
+struct [[hlsl::resource_class(Sampler)]] MySampler {
int x;
};
+struct [[hlsl::resource_class(UAV)]] MyUAV {
+ int x;
+};
+
+struct [[hlsl::resource_class(CBuffer)]] MyCBuffer {
+ int x;
+};
+
+// Valid: f is skipped, SRVBuf is bound to t0, UAVBuf is bound to u0
struct Eg1 {
float f;
- Buffer<float> Buf;
- RWBuffer<float> RWBuf;
+ MySRV SRVBuf;
+ MyUAV UAVBuf;
};
Eg1 e1 : register(t0) : register(u0);
-// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0
-
+// Valid: f is skipped, SRVBuf is bound to t0, UAVBuf is bound to u0.
+// UAVBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
struct Eg2 {
float f;
- Buffer<float> Buf;
- RWBuffer<float> RWBuf;
- RWBuffer<float> RWBuf2;
+ MySRV SRVBuf;
+ MyUAV UAVBuf;
+ MyUAV UAVBuf2;
};
Eg2 e2 : register(t0) : register(u0);
-// Valid: f is skipped, Buf is bound to t0, RWBuf is bound to u0.
-// RWBuf2 gets automatically assigned to u1 even though there is no explicit binding for u1.
+// Valid: Bar, the struct within Eg3, has a valid resource that can be bound to t0.
struct Eg3 {
struct Bar {
- RWBuffer<int> a;
+ MyUAV a;
};
Bar b;
};
Eg3 e3 : register(u0);
-// Valid: Bar, the struct within Eg3, has a valid resource that can be bound to t0.
+// Valid: the first sampler state object within 's' is bound to slot 5
struct Eg4 {
- SamplerState s[3];
+ MySampler s[3];
};
Eg4 e4 : register(s5);
-// Valid: the first sampler state object within Eg5's s is bound to slot 5
struct Eg5 {
@@ -63,41 +66,60 @@ struct Eg5 {
Eg5 e5 : register(t0);
struct Eg6 {
- struct Bar {
- float f;
- };
- Bar b;
-};
-// expected-warning at +1{{binding type 't' only applies to types containing srv resources}}
-Eg6 e6 : register(t0);
+ float f;
+};
+// expected-warning at +1{{binding type 'u' only applies to types containing uav resources}}
+Eg6 e6 : register(u0);
struct Eg7 {
- RWBuffer<int> a;
+ float f;
};
-// expected-warning at +1{{binding type 'c' only applies to types containing numeric types}}
-Eg7 e7 : register(c0);
+// expected-warning at +1{{binding type 'b' only applies to types containing constant buffer resources}}
+Eg7 e7 : register(b0);
+struct Eg8 {
+ float f;
+};
+// expected-warning at +1{{binding type 's' only applies to types containing sampler state}}
+Eg8 e8 : register(s0);
+
+struct Eg9 {
+ MySRV s;
+};
+// expected-warning at +1{{binding type 'c' only applies to types containing numeric types}}
+Eg9 e9 : register(c0);
-struct Eg8{
+struct Eg10{
// expected-error at +1{{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
RWBuffer<int> a : register(u9);
};
-Eg8 e8;
+Eg10 e10;
template<typename R>
-struct Eg9 {
+struct Eg11 {
R b;
};
-// expecting warning: {{binding type 'u' only applies to types containing uav resources}}
-Eg9<Texture2D> e9 : register(u0);
-// invalid because after template expansion, there are no valid resources inside Eg10 to bind as a UAV.
+// expected-warning at +1{{binding type 'u' only applies to types containing uav resources}}
+Eg11<MySRV> e11 : register(u0);
+// invalid because after template expansion, there are no valid resources inside Eg11 to bind as a UAV, only an SRV
-struct Eg10{
- RWBuffer<int> a;
- RWBuffer<int> b;
+struct Eg12{
+ MySRV s1;
+ MySRV s2;
};
+// expected-warning at +3{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +2{{binding type 'u' only applies to types containing uav resources}}
+// expected-error at +1{{binding type 'u' cannot be applied more than once}}
+Eg12 e12 : register(u9) : register(u10);
+struct Eg13{
+ MySRV s1;
+ MySRV s2;
+};
+// expected-warning at +4{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +3{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +2{{binding type 'u' only applies to types containing uav resources}}
// expected-error at +1{{binding type 'u' cannot be applied more than once}}
-Eg10 e10 : register(u9) : register(u10);
+Eg13 e13 : register(u9) : register(u10) : register(u11);
>From e8aca071789cc17e382de2dc59b8c60943f56811 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 19 Aug 2024 10:13:10 -0700
Subject: [PATCH 34/43] address damyan except for common code
---
clang/lib/Sema/SemaHLSL.cpp | 48 ++++++++-----------
.../resource_binding_attr_error_udt.hlsl | 6 +++
2 files changed, 26 insertions(+), 28 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 6ce25f6dd31ae0..aca9eb60300dae 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -533,26 +533,19 @@ static void setResourceClassFlagsFromDeclResourceClass(
template <typename T>
static const T *getSpecifiedHLSLAttrFromVarDecl(VarDecl *VD) {
+ assert(VD && "VD should not be null");
+ const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
+ if (!TheRecordDecl)
+ return nullptr;
- if (VD) {
- const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
- if (!TheRecordDecl)
- return nullptr;
-
- // the resource attr could be on the record decl itself or on one of
- // its fields (the resource handle, most commonly)
- const auto *Attr = TheRecordDecl->getAttr<T>();
- if (!Attr) {
- for (auto *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<T>();
- if (Attr)
- break;
- }
- }
- return Attr;
+ // get the attr from the handle member, a field of the record decl
+ const T *Attr = nullptr;
+ for (FieldDecl *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<T>();
+ if (Attr)
+ break;
}
- llvm_unreachable("VD should not be null");
- return nullptr;
+ return Attr;
}
static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
@@ -574,24 +567,23 @@ static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
bool resClassSet = false;
// if the member's base type is a ClassTemplateSpecializationDecl,
- // check if it has a resource class attr
+ // check if it has a member handle with a resource class attr
+ // this is necessary while resources like RWBuffer are defined externally
if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
TheRecordDecl = TheRecordDecl->getCanonicalDecl();
- const auto *Attr = TheRecordDecl->getAttr<HLSLResourceClassAttr>();
- if (!Attr) {
- for (auto *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<HLSLResourceClassAttr>();
- if (Attr)
- break;
- }
+ const HLSLResourceClassAttr *Attr = nullptr;
+ for (FieldDecl *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<HLSLResourceClassAttr>();
+ if (Attr)
+ break;
}
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
resClassSet = true;
}
- // otherwise, check if the member has a resource class attr
- else if (auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
+ // otherwise, check if the member of the UDT has a resource class attr
+ else if (const auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
resClassSet = true;
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 7d0a9dd1b19e6f..8bc88e6d065382 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -123,3 +123,9 @@ struct Eg13{
// expected-warning at +2{{binding type 'u' only applies to types containing uav resources}}
// expected-error at +1{{binding type 'u' cannot be applied more than once}}
Eg13 e13 : register(u9) : register(u10) : register(u11);
+
+struct Eg14{
+ RWBuffer<int> r1;
+};
+// expected-warning at +1{{binding type 't' only applies to types containing srv resources}}
+Eg14 e14 : register(t9);
\ No newline at end of file
>From ff58d0725356dbec7a2055bc5589a507abb88d01 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 19 Aug 2024 11:33:26 -0700
Subject: [PATCH 35/43] refactor complete, but we can do better
---
clang/lib/Sema/SemaHLSL.cpp | 88 ++++++++++++++++++++++---------------
1 file changed, 53 insertions(+), 35 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index aca9eb60300dae..184e9a11230cce 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -496,20 +496,16 @@ static bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
return false;
}
-static const CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
+static CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
assert(Ty && "Resource class must have an element type.");
if (const auto *TheBuiltinTy = dyn_cast<BuiltinType>(Ty))
return nullptr;
- const CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
+ CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
assert(TheRecordDecl &&
"Resource class should have a resource type declaration.");
-
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl))
- TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
- TheRecordDecl = TheRecordDecl->getCanonicalDecl();
return TheRecordDecl;
}
@@ -532,20 +528,46 @@ static void setResourceClassFlagsFromDeclResourceClass(
}
template <typename T>
-static const T *getSpecifiedHLSLAttrFromVarDecl(VarDecl *VD) {
- assert(VD && "VD should not be null");
- const CXXRecordDecl *TheRecordDecl = getRecordDeclFromVarDecl(VD);
- if (!TheRecordDecl)
- return nullptr;
+static const T *
+getSpecifiedHLSLAttrFromVarDeclOrRecordDecl(VarDecl *VD,
+ RecordDecl *TheRecordDecl) {
+ if (VD) {
+ TheRecordDecl = getRecordDeclFromVarDecl(VD);
+ if (!TheRecordDecl)
+ return nullptr;
+ }
+
+ if (TheRecordDecl) {
+ // if the member's base type is a ClassTemplateSpecializationDecl,
+ // check if it has a member handle with a resource class attr
+ // this is necessary while resources like RWBuffer are defined externally
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl)) {
+ auto TheCXXRecordDecl =
+ TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheCXXRecordDecl = TheCXXRecordDecl->getCanonicalDecl();
+ const auto *Attr = TheCXXRecordDecl->getAttr<T>();
+ if (!Attr) {
+ for (auto *FD : TheCXXRecordDecl->fields()) {
+ Attr = FD->getAttr<T>();
+ if (Attr)
+ break;
+ }
+ }
+ return Attr;
+ }
- // get the attr from the handle member, a field of the record decl
- const T *Attr = nullptr;
- for (FieldDecl *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<T>();
- if (Attr)
- break;
+ const auto *Attr = TheRecordDecl->getAttr<T>();
+ if (!Attr) {
+ for (auto *FD : TheRecordDecl->fields()) {
+ Attr = FD->getAttr<T>();
+ if (Attr)
+ break;
+ }
+ }
+ return Attr;
}
- return Attr;
+ llvm_unreachable("TheRecordDecl should not be null");
+ return nullptr;
}
static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
@@ -566,29 +588,22 @@ static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
bool resClassSet = false;
- // if the member's base type is a ClassTemplateSpecializationDecl,
- // check if it has a member handle with a resource class attr
- // this is necessary while resources like RWBuffer are defined externally
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(SubRecordDecl)) {
- auto TheRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
- TheRecordDecl = TheRecordDecl->getCanonicalDecl();
- const HLSLResourceClassAttr *Attr = nullptr;
- for (FieldDecl *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<HLSLResourceClassAttr>();
- if (Attr)
- break;
- }
+ const HLSLResourceClassAttr *Attr =
+ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
+ nullptr, SubRecordDecl);
+ // find the attr if it's on the member (the handle) of the resource
+ if (Attr) {
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
resClassSet = true;
}
- // otherwise, check if the member of the UDT has a resource class attr
+ // otherwise, check if the member of the UDT itself has a resource class attr
else if (const auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
resClassSet = true;
}
-
+ // recurse if there are more fields to analyze
if (!resClassSet) {
for (auto Field : SubRecordDecl->fields()) {
setFlagsFromType(Field->getType(), Flags);
@@ -652,7 +667,8 @@ static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
Flags.SRV = true;
} else if (TheVarDecl) {
const HLSLResourceClassAttr *resClassAttr =
- getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
+ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
+ TheVarDecl, nullptr);
const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
while (TheBaseType->isArrayType())
TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
@@ -787,9 +803,11 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
resAttr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
resClassAttr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
} else if (TheVarDecl) {
- resAttr = getSpecifiedHLSLAttrFromVarDecl<HLSLResourceAttr>(TheVarDecl);
+ resAttr = getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceAttr>(
+ TheVarDecl, nullptr);
resClassAttr =
- getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
+ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
+ TheVarDecl, nullptr);
}
assert(resAttr && resClassAttr &&
>From 3f168e5c09e5f1c703b7c894e9a6db88c9c070ba Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 19 Aug 2024 11:50:48 -0700
Subject: [PATCH 36/43] make a lambda fxn to reduce repetition
---
clang/lib/Sema/SemaHLSL.cpp | 32 ++++++++++++++------------------
1 file changed, 14 insertions(+), 18 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 184e9a11230cce..7bd0d702eeb9b4 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -537,6 +537,17 @@ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl(VarDecl *VD,
return nullptr;
}
+ // make a lambda that loops over the field members and checks for the
+ // templated attribute
+ auto f = [](RecordDecl *TheRecordDecl) -> const T * {
+ for (auto *FD : TheRecordDecl->fields()) {
+ const T *Attr = FD->getAttr<T>();
+ if (Attr)
+ return Attr;
+ }
+ return nullptr;
+ };
+
if (TheRecordDecl) {
// if the member's base type is a ClassTemplateSpecializationDecl,
// check if it has a member handle with a resource class attr
@@ -545,26 +556,11 @@ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl(VarDecl *VD,
auto TheCXXRecordDecl =
TDecl->getSpecializedTemplate()->getTemplatedDecl();
TheCXXRecordDecl = TheCXXRecordDecl->getCanonicalDecl();
- const auto *Attr = TheCXXRecordDecl->getAttr<T>();
- if (!Attr) {
- for (auto *FD : TheCXXRecordDecl->fields()) {
- Attr = FD->getAttr<T>();
- if (Attr)
- break;
- }
- }
- return Attr;
- }
- const auto *Attr = TheRecordDecl->getAttr<T>();
- if (!Attr) {
- for (auto *FD : TheRecordDecl->fields()) {
- Attr = FD->getAttr<T>();
- if (Attr)
- break;
- }
+ return f(TheCXXRecordDecl);
}
- return Attr;
+
+ return f(TheRecordDecl);
}
llvm_unreachable("TheRecordDecl should not be null");
return nullptr;
>From acd77070de871368c19fbd3402f497e23b7ec029 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 20 Aug 2024 17:40:54 -0700
Subject: [PATCH 37/43] address Damyan
---
clang/include/clang/Basic/Attr.td | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 42 ++++----
.../resource_binding_attr_error_resource.hlsl | 97 +++++++++----------
.../resource_binding_attr_error_udt.hlsl | 24 ++---
4 files changed, 74 insertions(+), 91 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index cf3fff94711f16..da66975a8423d2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4613,7 +4613,7 @@ def HLSLROV : InheritableAttr {
def HLSLResourceClass : InheritableAttr {
let Spellings = [CXX11<"hlsl", "resource_class">];
- let Subjects = SubjectList<[Struct]>;
+ let Subjects = SubjectList<[Field]>;
let LangOpts = [HLSL];
let Args = [
EnumArgument<"ResourceClass", "llvm::hlsl::ResourceClass",
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7bd0d702eeb9b4..d30a830650ede2 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -496,16 +496,17 @@ static bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
return false;
}
+// get the record decl from a var decl that we expect
+// represents a resource
static CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
- assert(Ty && "Resource class must have an element type.");
+ assert(Ty && "Resource must have an element type.");
if (const auto *TheBuiltinTy = dyn_cast<BuiltinType>(Ty))
return nullptr;
CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
- assert(TheRecordDecl &&
- "Resource class should have a resource type declaration.");
+ assert(TheRecordDecl && "Resource should have a resource type declaration.");
return TheRecordDecl;
}
@@ -537,8 +538,9 @@ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl(VarDecl *VD,
return nullptr;
}
- // make a lambda that loops over the field members and checks for the
- // templated attribute
+ // make a lambda that checks if the decl has the specified attr,
+ // and if not, loops over the field members and checks for the
+ // specified attribute
auto f = [](RecordDecl *TheRecordDecl) -> const T * {
for (auto *FD : TheRecordDecl->fields()) {
const T *Attr = FD->getAttr<T>();
@@ -793,35 +795,30 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
if (Flags.Resource) {
- const HLSLResourceAttr *resAttr = nullptr;
const HLSLResourceClassAttr *resClassAttr = nullptr;
if (CBufferOrTBuffer) {
- resAttr = CBufferOrTBuffer->getAttr<HLSLResourceAttr>();
resClassAttr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
} else if (TheVarDecl) {
- resAttr = getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceAttr>(
- TheVarDecl, nullptr);
resClassAttr =
getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
TheVarDecl, nullptr);
}
- assert(resAttr && resClassAttr &&
+ assert(resClassAttr &&
"any decl that set the resource flag on analysis should "
- "have a resource attribute and resource class attribute attached.");
+ "have a resource class attribute attached.");
const llvm::hlsl::ResourceClass DeclResourceClass =
resClassAttr->getResourceClass();
// confirm that the register type is bound to its expected resource class
- static llvm::SmallVector<RegisterType>
- ExpectedRegisterTypesForResourceClass = {
- RegisterType::SRV,
- RegisterType::UAV,
- RegisterType::CBuffer,
- RegisterType::Sampler,
- };
+ static RegisterType ExpectedRegisterTypesForResourceClass[] = {
+ RegisterType::SRV,
+ RegisterType::UAV,
+ RegisterType::CBuffer,
+ RegisterType::Sampler,
+ };
assert((int)DeclResourceClass <
- ExpectedRegisterTypesForResourceClass.size() &&
+ std::size(ExpectedRegisterTypesForResourceClass) &&
"DeclResourceClass has unexpected value");
RegisterType ExpectedRegisterType =
@@ -833,8 +830,7 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
return;
}
- // next, handle diagnostics for when the "basic" flag is set,
- // including the legacy "i" and "b" register types.
+ // next, handle diagnostics for when the "basic" flag is set
if (Flags.Basic) {
if (Flags.DefaultGlobals) {
if (regType == RegisterType::CBuffer)
@@ -854,9 +850,9 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
// finally, we handle the udt case
if (Flags.UDT) {
- const SmallVector<bool> ExpectedRegisterTypesForUDT = {
+ const bool ExpectedRegisterTypesForUDT[] = {
Flags.SRV, Flags.UAV, Flags.CBV, Flags.Sampler, Flags.ContainsNumeric};
- assert((int)regType < ExpectedRegisterTypesForUDT.size() &&
+ assert((int)regType < std::size(ExpectedRegisterTypesForUDT) &&
"regType has unexpected value");
if (!ExpectedRegisterTypesForUDT[(int)regType])
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index 81580a341e8acc..fc3bcd99be03ee 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -3,74 +3,69 @@
// This test validates the diagnostics that are emitted when a variable with a "resource" type
// is bound to a register using the register annotation
-// expected-error at +1 {{binding type 'b' only applies to constant buffer resources}}
-RWBuffer<int> a : register(b2, space1);
-
-// expected-error at +1 {{binding type 't' only applies to srv resources}}
-RWBuffer<int> b : register(t2, space1);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture1D' (expected 't')}}
-// NOT YET IMPLEMENTED Texture1D<float> tex : register(u3);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 's' for register type 'Texture2D' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2D<float> Texture : register(s0);
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMS' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DMS<float4, 4> T2DMS_t2 : register(u2)
+/*
+template<typename T>
+struct [[hlsl::resource_class(SRV)]] MyTemplatedSRV {
+ T x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'RWTexture3D' (expected 'u')}}
-// NOT YET IMPLEMENTED RWTexture3D<float4> RWT3D_u1 : register(t1)
+struct [[hlsl::resource_class(SRV)]] MySRV {
+ int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCube' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCube <float> t8 : register(b8);
+struct [[hlsl::resource_class(Sampler)]] MySampler {
+ int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'TextureCubeArray' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_t2 : register(b2);
+struct [[hlsl::resource_class(UAV)]] MyUAV {
+ int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'b' for register type 'Texture1DArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture1DArray T1DArray_t2 : register(b2);
+struct [[hlsl::resource_class(CBuffer)]] MyCBuffer {
+ int x;
+};
+*/
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'B' for register type 'Texture2DArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DArray T2DArray_b2 : register(B2);
+template<typename T>
+struct MyTemplatedSRV {
+ [[hlsl::resource_class(SRV)]] T x;
+};
+struct MySRV {
+ [[hlsl::resource_class(SRV)]] int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'Texture2DMSArray' (expected 't')}}
-// NOT YET IMPLEMENTED Texture2DMSArray<float4> msTextureArray : register(u2, space2);
+struct MySampler {
+ [[hlsl::resource_class(Sampler)]] int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TextureCubeArray' (expected 't')}}
-// NOT YET IMPLEMENTED TextureCubeArray TCubeArray_f2 : register(u2);
+struct MyUAV {
+ [[hlsl::resource_class(UAV)]] int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'TypedBuffer' (expected 't')}}
-// NOT YET IMPLEMENTED TypedBuffer tbuf : register(u2);
+struct MyCBuffer {
+ [[hlsl::resource_class(CBuffer)]] int x;
+};
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'RawBuffer' (expected 't')}}
-// NOT YET IMPLEMENTED RawBuffer rbuf : register(u2);
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'StructuredBuffer' (expected 'u')}}
-// NOT YET IMPLEMENTED StructuredBuffer ROVStructuredBuff_t2 : register(T2);
+// expected-error at +1 {{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
+MySRV invalid : register(i2);
-// expected-error at +1 {{binding type 's' only applies to sampler state}}
-cbuffer f : register(s2, space1) {}
-
-// NOT YET IMPLEMENTED : {{invalid register name prefix 't' for register type 'Sampler' (expected 's')}}
-// Can this type just be Sampler instead of SamplerState?
-// NOT YET IMPLEMENTED SamplerState MySampler : register(t3, space1);
-
-// expected-error at +1 {{binding type 's' only applies to sampler state}}
-tbuffer f : register(s2, space1) {}
-
-// NOT YET IMPLEMENTED : RTAccelerationStructure doesn't have any example tests in DXC
+// expected-error at +1 {{binding type 't' only applies to srv resources}}
+MyUAV a : register(t2, space1);
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2D' (expected 't')}}
-// NOT YET IMPLEMENTED FeedbackTexture2D<float> FBTex2D[3][] : register(u0, space26);
+// expected-error at +1 {{binding type 'u' only applies to uav resources}}
+MySampler b : register(u2, space1);
-// NOT YET IMPLEMENTED : {{invalid register name prefix 'u' for register type 'FeedbackTexture2DArray' (expected 't')}}
-// NOT YET IMPLEMENTED FeedbackTexture2DArray<float> FBTex2DArr[3][2][] : register(u0, space27);
+// expected-error at +1 {{binding type 'b' only applies to constant buffer resources}}
+MyTemplatedSRV<int> c : register(b2);
+// expected-error at +1 {{binding type 's' only applies to sampler state}}
+MyUAV d : register(s2, space1);
// empty binding prefix cases:
// expected-error at +1 {{expected identifier}}
-RWBuffer<int> c: register();
+MyTemplatedSRV<int> e: register();
// expected-error at +1 {{expected identifier}}
-RWBuffer<int> d: register("");
+MyTemplatedSRV<int> f: register("");
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index 8bc88e6d065382..f9eef656ee75bf 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -1,27 +1,19 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-// TODO: Implement "Buffer", we use a substitute UDT
-// to test the 't' binding type for this test.
-
-template<typename T>
-struct [[hlsl::resource_class(SRV)]] MyTemplatedSRV {
- T x;
-};
-
-struct [[hlsl::resource_class(SRV)]] MySRV {
- int x;
+struct MySRV {
+ [[hlsl::resource_class(SRV)]] int x;
};
-struct [[hlsl::resource_class(Sampler)]] MySampler {
- int x;
+struct MySampler {
+ [[hlsl::resource_class(Sampler)]] int x;
};
-struct [[hlsl::resource_class(UAV)]] MyUAV {
- int x;
+struct MyUAV {
+ [[hlsl::resource_class(UAV)]] int x;
};
-struct [[hlsl::resource_class(CBuffer)]] MyCBuffer {
- int x;
+struct MyCBuffer {
+ [[hlsl::resource_class(CBuffer)]] int x;
};
// Valid: f is skipped, SRVBuf is bound to t0, UAVBuf is bound to u0
>From 662fed9535f0860cb7bc8dd609d526175a6681a1 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 20 Aug 2024 18:04:35 -0700
Subject: [PATCH 38/43] adjust getRegisterTypeIndex
---
clang/lib/Sema/SemaHLSL.cpp | 39 +++++++++----------------------------
1 file changed, 9 insertions(+), 30 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d30a830650ede2..5250d07f36aa35 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -693,7 +693,7 @@ static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
return Flags;
}
-enum RegisterType { SRV, UAV, CBuffer, Sampler, C, I };
+enum RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };
static RegisterType getRegisterTypeIndex(StringRef Slot) {
switch (Slot[0]) {
@@ -712,12 +712,11 @@ static RegisterType getRegisterTypeIndex(StringRef Slot) {
case 'c':
case 'C':
return RegisterType::C;
- // we don't need to check for 'i' here, because
- // any attribute that has the 'i' register type
- // will be immediately caught by handleResourceBindingAttr
- // so it's impossible for the decl to already have an 'i' register type
+ case 'i':
+ case 'I':
+ return RegisterType::I;
default:
- llvm_unreachable("invalid register type");
+ return RegisterType::Invalid;
}
}
@@ -904,32 +903,12 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
// Validate.
if (!Slot.empty()) {
- switch (Slot[0]) {
- case 't':
- case 'T':
- regType = RegisterType::SRV;
- break;
- case 'u':
- case 'U':
- regType = RegisterType::UAV;
- break;
- case 'b':
- case 'B ':
- regType = RegisterType::CBuffer;
- break;
- case 's':
- case 'S':
- regType = RegisterType::Sampler;
- break;
- case 'c':
- case 'C':
- regType = RegisterType::C;
- break;
- case 'i':
- case 'I':
+ regType = getRegisterTypeIndex(Slot);
+ if (regType == RegisterType::I) {
Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
return;
- default:
+ }
+ if (regType == RegisterType::Invalid) {
Diag(ArgLoc, diag::err_hlsl_binding_type_invalid) << Slot.substr(0, 1);
return;
}
>From 9b72907046a6cb9c55178d3df26d651b02f7f197 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 21 Aug 2024 10:46:07 -0700
Subject: [PATCH 39/43] fix failing tests, adjust for attr on handle member now
---
...a-attribute-supported-attributes-list.test | 2 +-
.../ParserHLSL/hlsl_resource_class_attr.hlsl | 30 +++++++++----------
.../hlsl_resource_class_attr_error.hlsl | 15 +++++++---
3 files changed, 27 insertions(+), 20 deletions(-)
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 1a71556213bb16..e59a367d7be863 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -82,7 +82,7 @@
// CHECK-NEXT: GNUInline (SubjectMatchRule_function)
// CHECK-NEXT: HIPManaged (SubjectMatchRule_variable)
// CHECK-NEXT: HLSLROV (SubjectMatchRule_record_not_is_union)
-// CHECK-NEXT: HLSLResourceClass (SubjectMatchRule_record_not_is_union)
+// CHECK-NEXT: HLSLResourceClass (SubjectMatchRule_field)
// CHECK-NEXT: Hot (SubjectMatchRule_function)
// CHECK-NEXT: HybridPatchable (SubjectMatchRule_function)
// CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance)
diff --git a/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl b/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
index 410b4524f1c3df..4b002e2d890093 100644
--- a/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
+++ b/clang/test/ParserHLSL/hlsl_resource_class_attr.hlsl
@@ -1,31 +1,31 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s | FileCheck %s
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> SRV
-struct [[hlsl::resource_class(SRV)]] Eg1 {
- int i;
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> SRV
+struct Eg1 {
+ [[hlsl::resource_class(SRV)]] int i;
};
Eg1 e1;
-// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:13:1, line:15:1> line:13:38 referenced struct Eg2 definition
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> UAV
-struct [[hlsl::resource_class(UAV)]] Eg2 {
- int i;
+// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:13:1, line:15:1> line:13:8 referenced struct Eg2 definition
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> UAV
+struct Eg2 {
+ [[hlsl::resource_class(UAV)]] int i;
};
Eg2 e2;
-// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:20:1, line:22:1> line:20:42 referenced struct Eg3 definition
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> CBuffer
-struct [[hlsl::resource_class(CBuffer)]] Eg3 {
- int i;
+// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:20:1, line:22:1> line:20:8 referenced struct Eg3 definition
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> CBuffer
+struct Eg3 {
+ [[hlsl::resource_class(CBuffer)]] int i;
};
Eg3 e3;
-// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:27:1, line:29:1> line:27:42 referenced struct Eg4 definition
-// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:31> Sampler
-struct [[hlsl::resource_class(Sampler)]] Eg4 {
- int i;
+// CHECK: -CXXRecordDecl 0x{{[0-9a-f]+}} <line:27:1, line:29:1> line:27:8 referenced struct Eg4 definition
+// CHECK: -HLSLResourceClassAttr 0x{{[0-9a-f]+}} <col:26> Sampler
+struct Eg4 {
+ [[hlsl::resource_class(Sampler)]] int i;
};
Eg4 e4;
diff --git a/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl b/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
index 00fcd769760bba..76bed2f0607830 100644
--- a/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
+++ b/clang/test/ParserHLSL/hlsl_resource_class_attr_error.hlsl
@@ -1,15 +1,22 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s -verify
+struct Eg1 {
// expected-error at +1{{'resource_class' attribute takes one argument}}
-struct [[hlsl::resource_class()]] Eg1 {
- int i;
+ [[hlsl::resource_class()]] int i;
};
Eg1 e1;
+struct Eg2 {
// expected-warning at +1{{ResourceClass attribute argument not supported: gibberish}}
-struct [[hlsl::resource_class(gibberish)]] Eg2 {
- int i;
+ [[hlsl::resource_class(gibberish)]] int i;
};
Eg2 e2;
+
+// expected-warning at +1{{'resource_class' attribute only applies to non-static data members}}
+struct [[hlsl::resource_class(SRV)]] Eg3 {
+ int i;
+};
+
+Eg3 e3;
>From 9110569d0f57989a5308c395a732c26d4e5fcca1 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Wed, 21 Aug 2024 12:21:06 -0700
Subject: [PATCH 40/43] remove rov arg
---
clang/lib/Sema/SemaHLSL.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 5250d07f36aa35..2ee6c4acd5360c 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -44,8 +44,7 @@ Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
auto RK = CBuffer ? llvm::hlsl::ResourceKind::CBuffer
: llvm::hlsl::ResourceKind::TBuffer;
Result->addAttr(HLSLResourceClassAttr::CreateImplicit(getASTContext(), RC));
- Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RK,
- /*IsROV=*/false));
+ Result->addAttr(HLSLResourceAttr::CreateImplicit(getASTContext(), RK));
SemaRef.PushOnScopeChains(Result, BufferScope);
SemaRef.PushDeclContext(BufferScope, Result);
>From 62954bf4b152be95d351684fe49d12d5a12f43c6 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Thu, 22 Aug 2024 11:33:47 -0700
Subject: [PATCH 41/43] address Damyan
---
clang/include/clang/Sema/SemaHLSL.h | 1 -
clang/lib/Sema/SemaHLSL.cpp | 34 +++++++++++--------
.../resource_binding_attr_error_resource.hlsl | 22 ------------
3 files changed, 19 insertions(+), 38 deletions(-)
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 99ff3ec2531a0d..d60cb2a57d4918 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -31,7 +31,6 @@ class SemaHLSL : public SemaBase {
public:
SemaHLSL(Sema &S);
- HLSLResourceAttr *mergeHLSLResourceAttr(bool CBuffer);
Decl *ActOnStartBuffer(Scope *BufferScope, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *Ident, SourceLocation IdentLoc,
SourceLocation LBrace);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2ee6c4acd5360c..8c880342275da9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -692,7 +692,7 @@ static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
return Flags;
}
-enum RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };
+enum class RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };
static RegisterType getRegisterTypeIndex(StringRef Slot) {
switch (Slot[0]) {
@@ -724,7 +724,8 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
// make sure that there are no two register annotations
// applied to the decl with the same register type
bool RegisterTypesDetected[5] = {false};
- RegisterTypesDetected[regType] = true;
+
+ RegisterTypesDetected[static_cast<int>(regType)] = true;
// we need a static map to keep track of previous conflicts
// so that we don't emit the same error multiple times
@@ -734,16 +735,17 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
- RegisterType regType = getRegisterTypeIndex(attr->getSlot());
- if (RegisterTypesDetected[regType]) {
- if (PreviousConflicts[TheDecl].count(regType))
+ RegisterType otherRegType = getRegisterTypeIndex(attr->getSlot());
+ if (RegisterTypesDetected[static_cast<int>(otherRegType)]) {
+ if (PreviousConflicts[TheDecl].count(otherRegType))
continue;
+ int otherRegTypeNum = static_cast<int>(otherRegType);
S.Diag(TheDecl->getLocation(),
diag::err_hlsl_duplicate_register_annotation)
- << regType;
- PreviousConflicts[TheDecl].insert(regType);
+ << otherRegTypeNum;
+ PreviousConflicts[TheDecl].insert(otherRegType);
} else {
- RegisterTypesDetected[regType] = true;
+ RegisterTypesDetected[static_cast<int>(otherRegType)] = true;
}
}
}
@@ -781,9 +783,11 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
1 &&
"only one resource analysis result should be expected");
+ int regTypeNum = static_cast<int>(regType);
+
// first, if "other" is set, emit an error
if (Flags.Other) {
- S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
+ S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regTypeNum;
return;
}
@@ -823,7 +827,7 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
ExpectedRegisterTypesForResourceClass[(int)DeclResourceClass];
if (regType != ExpectedRegisterType) {
S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
- << regType;
+ << regTypeNum;
}
return;
}
@@ -834,14 +838,14 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
if (regType == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
else if (regType != RegisterType::C)
- S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
+ S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regTypeNum;
return;
}
if (regType == RegisterType::C)
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
else
- S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regType;
+ S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << regTypeNum;
return;
}
@@ -850,13 +854,13 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
if (Flags.UDT) {
const bool ExpectedRegisterTypesForUDT[] = {
Flags.SRV, Flags.UAV, Flags.CBV, Flags.Sampler, Flags.ContainsNumeric};
- assert((int)regType < std::size(ExpectedRegisterTypesForUDT) &&
+ assert(regTypeNum < std::size(ExpectedRegisterTypesForUDT) &&
"regType has unexpected value");
- if (!ExpectedRegisterTypesForUDT[(int)regType])
+ if (!ExpectedRegisterTypesForUDT[regTypeNum])
S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_user_defined_type_missing_member)
- << regType;
+ << regTypeNum;
return;
}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index fc3bcd99be03ee..abd878908608b0 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -3,28 +3,6 @@
// This test validates the diagnostics that are emitted when a variable with a "resource" type
// is bound to a register using the register annotation
-/*
-template<typename T>
-struct [[hlsl::resource_class(SRV)]] MyTemplatedSRV {
- T x;
-};
-
-struct [[hlsl::resource_class(SRV)]] MySRV {
- int x;
-};
-
-struct [[hlsl::resource_class(Sampler)]] MySampler {
- int x;
-};
-
-struct [[hlsl::resource_class(UAV)]] MyUAV {
- int x;
-};
-
-struct [[hlsl::resource_class(CBuffer)]] MyCBuffer {
- int x;
-};
-*/
template<typename T>
struct MyTemplatedSRV {
>From 1c3fe3f503bd7bea39b60d28549a0eb14c398576 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Thu, 22 Aug 2024 12:44:26 -0700
Subject: [PATCH 42/43] change group name, capitalize acronyms
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 +-
.../include/clang/Basic/DiagnosticSemaKinds.td | 10 +++++-----
.../SemaHLSL/resource_binding_attr_error.hlsl | 2 +-
.../resource_binding_attr_error_basic.hlsl | 2 +-
.../resource_binding_attr_error_resource.hlsl | 4 ++--
...ource_binding_attr_error_silence_diags.hlsl | 2 +-
.../resource_binding_attr_error_udt.hlsl | 18 +++++++++---------
7 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 0702efd36366e7..28d315f63e5c47 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1548,7 +1548,7 @@ def DXILValidation : DiagGroup<"dxil-validation">;
def HLSLAvailability : DiagGroup<"hlsl-availability">;
// Warnings for legacy binding behavior
-def DisallowLegacyBindingRules : DiagGroup<"disallow-legacy-binding-rules">;
+def LegacyConstantRegisterBinding : DiagGroup<"legacy-constant-register-binding">;
// Warnings and notes related to const_var_decl_type attribute checks
def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3c51d76174936c..94bb8bff5fdbac 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12342,13 +12342,13 @@ def err_hlsl_missing_semantic_annotation : Error<
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
-def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{srv resources|uav resources|constant buffer resources|sampler state|numeric types}0">, InGroup<DisallowLegacyBindingRules>;
-def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{srv resources|uav resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
+def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric types}0">, InGroup<LegacyConstantRegisterBinding>;
+def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
def err_hlsl_binding_type_invalid: Error<"binding type '%0' is invalid">;
def err_hlsl_duplicate_register_annotation: Error<"binding type '%select{t|u|b|s|c|i}0' cannot be applied more than once">;
-def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<DisallowLegacyBindingRules>, DefaultError;
-def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<DisallowLegacyBindingRules>, DefaultError;
+def warn_hlsl_register_type_c_packoffset: Warning<"binding type 'c' ignored in buffer declaration. Did you mean 'packoffset'?">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
+def warn_hlsl_deprecated_register_type_b: Warning<"binding type 'b' only applies to constant buffers. The 'bool constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
+def warn_hlsl_deprecated_register_type_i: Warning<"binding type 'i' ignored. The 'integer constant' binding type is no longer supported">, InGroup<LegacyConstantRegisterBinding>, DefaultError;
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
def warn_hlsl_packoffset_mix : Warning<"cannot mix packoffset elements with nonpackoffset elements in a cbuffer">,
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 33eaa152cf5be1..5c641d5da44005 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -56,7 +56,7 @@ void foo2() {
extern RWBuffer<float> U2 : register(u5);
}
-// expected-error at +1 {{binding type 'u' only applies to uav resources}}
+// expected-error at +1 {{binding type 'u' only applies to UAV resources}}
float b : register(u0, space1);
// expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index d797f22c8ccba0..858966635c0440 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
-// expected-error at +1{{binding type 't' only applies to srv resources}}
+// expected-error at +1{{binding type 't' only applies to SRV resources}}
float f1 : register(t0);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
index abd878908608b0..c40d1d7f60b347 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_resource.hlsl
@@ -29,10 +29,10 @@ struct MyCBuffer {
// expected-error at +1 {{binding type 'i' ignored. The 'integer constant' binding type is no longer supported}}
MySRV invalid : register(i2);
-// expected-error at +1 {{binding type 't' only applies to srv resources}}
+// expected-error at +1 {{binding type 't' only applies to SRV resources}}
MyUAV a : register(t2, space1);
-// expected-error at +1 {{binding type 'u' only applies to uav resources}}
+// expected-error at +1 {{binding type 'u' only applies to UAV resources}}
MySampler b : register(u2, space1);
// expected-error at +1 {{binding type 'b' only applies to constant buffer resources}}
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
index 7bcde02728f6d7..3ce4739d6ebe28 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only -Wno-disallow-legacy-binding-rules %s -verify
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only -Wno-legacy-constant-register-binding %s -verify
// expected-no-diagnostics
float f2 : register(b9);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index f9eef656ee75bf..f07ee21d502001 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -54,13 +54,13 @@ Eg4 e4 : register(s5);
struct Eg5 {
float f;
};
-// expected-warning at +1{{binding type 't' only applies to types containing srv resources}}
+// expected-warning at +1{{binding type 't' only applies to types containing SRV resources}}
Eg5 e5 : register(t0);
struct Eg6 {
float f;
};
-// expected-warning at +1{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +1{{binding type 'u' only applies to types containing UAV resources}}
Eg6 e6 : register(u0);
struct Eg7 {
@@ -92,7 +92,7 @@ template<typename R>
struct Eg11 {
R b;
};
-// expected-warning at +1{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +1{{binding type 'u' only applies to types containing UAV resources}}
Eg11<MySRV> e11 : register(u0);
// invalid because after template expansion, there are no valid resources inside Eg11 to bind as a UAV, only an SRV
@@ -101,8 +101,8 @@ struct Eg12{
MySRV s1;
MySRV s2;
};
-// expected-warning at +3{{binding type 'u' only applies to types containing uav resources}}
-// expected-warning at +2{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +3{{binding type 'u' only applies to types containing UAV resources}}
+// expected-warning at +2{{binding type 'u' only applies to types containing UAV resources}}
// expected-error at +1{{binding type 'u' cannot be applied more than once}}
Eg12 e12 : register(u9) : register(u10);
@@ -110,14 +110,14 @@ struct Eg13{
MySRV s1;
MySRV s2;
};
-// expected-warning at +4{{binding type 'u' only applies to types containing uav resources}}
-// expected-warning at +3{{binding type 'u' only applies to types containing uav resources}}
-// expected-warning at +2{{binding type 'u' only applies to types containing uav resources}}
+// expected-warning at +4{{binding type 'u' only applies to types containing UAV resources}}
+// expected-warning at +3{{binding type 'u' only applies to types containing UAV resources}}
+// expected-warning at +2{{binding type 'u' only applies to types containing UAV resources}}
// expected-error at +1{{binding type 'u' cannot be applied more than once}}
Eg13 e13 : register(u9) : register(u10) : register(u11);
struct Eg14{
RWBuffer<int> r1;
};
-// expected-warning at +1{{binding type 't' only applies to types containing srv resources}}
+// expected-warning at +1{{binding type 't' only applies to types containing SRV resources}}
Eg14 e14 : register(t9);
\ No newline at end of file
>From f3581c34740888ffac8498e233af6be5c4d7a55e Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Thu, 22 Aug 2024 15:30:25 -0700
Subject: [PATCH 43/43] address Helena
---
clang/lib/Sema/SemaHLSL.cpp | 100 +++++++-----------
.../SemaHLSL/resource_binding_attr_error.hlsl | 2 +-
.../resource_binding_attr_error_basic.hlsl | 2 +-
...urce_binding_attr_error_silence_diags.hlsl | 2 +-
.../resource_binding_attr_error_udt.hlsl | 6 +-
5 files changed, 47 insertions(+), 65 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 8c880342275da9..c2a6c1c74e7269 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -483,16 +483,7 @@ struct RegisterBindingFlags {
};
static bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
- if (!TheDecl)
- return false;
-
- // Traverse up the parent contexts
- const DeclContext *context = TheDecl->getDeclContext();
- if (isa<HLSLBufferDecl>(context)) {
- return true;
- }
-
- return false;
+ return TheDecl && isa<HLSLBufferDecl>(TheDecl->getDeclContext());
}
// get the record decl from a var decl that we expect
@@ -501,7 +492,7 @@ static CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
const Type *Ty = VD->getType()->getPointeeOrArrayElementType();
assert(Ty && "Resource must have an element type.");
- if (const auto *TheBuiltinTy = dyn_cast<BuiltinType>(Ty))
+ if (Ty->isBuiltinType())
return nullptr;
CXXRecordDecl *TheRecordDecl = Ty->getAsCXXRecordDecl();
@@ -528,19 +519,16 @@ static void setResourceClassFlagsFromDeclResourceClass(
}
template <typename T>
-static const T *
-getSpecifiedHLSLAttrFromVarDeclOrRecordDecl(VarDecl *VD,
- RecordDecl *TheRecordDecl) {
- if (VD) {
- TheRecordDecl = getRecordDeclFromVarDecl(VD);
- if (!TheRecordDecl)
- return nullptr;
- }
+static const T *getSpecifiedHLSLAttrFromRecordDecl(RecordDecl *TheRecordDecl) {
+ if (!TheRecordDecl)
+ return nullptr;
// make a lambda that checks if the decl has the specified attr,
// and if not, loops over the field members and checks for the
// specified attribute
auto f = [](RecordDecl *TheRecordDecl) -> const T * {
+ if (TheRecordDecl->hasAttr<T>())
+ return TheRecordDecl->getAttr<T>();
for (auto *FD : TheRecordDecl->fields()) {
const T *Attr = FD->getAttr<T>();
if (Attr)
@@ -549,22 +537,29 @@ getSpecifiedHLSLAttrFromVarDeclOrRecordDecl(VarDecl *VD,
return nullptr;
};
- if (TheRecordDecl) {
- // if the member's base type is a ClassTemplateSpecializationDecl,
- // check if it has a member handle with a resource class attr
- // this is necessary while resources like RWBuffer are defined externally
- if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl)) {
- auto TheCXXRecordDecl =
- TDecl->getSpecializedTemplate()->getTemplatedDecl();
- TheCXXRecordDecl = TheCXXRecordDecl->getCanonicalDecl();
+ // if the member's base type is a ClassTemplateSpecializationDecl,
+ // check if it has a member handle with a resource class attr
+ // this is necessary while resources like RWBuffer are defined externally
+ if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(TheRecordDecl)) {
+ auto TheCXXRecordDecl = TDecl->getSpecializedTemplate()->getTemplatedDecl();
+ TheCXXRecordDecl = TheCXXRecordDecl->getCanonicalDecl();
- return f(TheCXXRecordDecl);
- }
+ return f(TheCXXRecordDecl);
+ }
+
+ return f(TheRecordDecl);
+}
- return f(TheRecordDecl);
+template <typename T>
+static const T *getSpecifiedHLSLAttrFromVarDecl(VarDecl *VD) {
+ RecordDecl *TheRecordDecl = nullptr;
+ if (VD) {
+ TheRecordDecl = getRecordDeclFromVarDecl(VD);
+ if (!TheRecordDecl)
+ return nullptr;
}
- llvm_unreachable("TheRecordDecl should not be null");
- return nullptr;
+
+ return getSpecifiedHLSLAttrFromRecordDecl<T>(TheRecordDecl);
}
static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
@@ -584,24 +579,16 @@ static void setFlagsFromType(QualType TheQualTy, RegisterBindingFlags &Flags) {
return;
RecordDecl *SubRecordDecl = TheRecordTy->getDecl();
- bool resClassSet = false;
const HLSLResourceClassAttr *Attr =
- getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
- nullptr, SubRecordDecl);
+ getSpecifiedHLSLAttrFromRecordDecl<HLSLResourceClassAttr>(SubRecordDecl);
// find the attr if it's on the member (the handle) of the resource
if (Attr) {
llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
- resClassSet = true;
- }
- // otherwise, check if the member of the UDT itself has a resource class attr
- else if (const auto *Attr = SubRecordDecl->getAttr<HLSLResourceClassAttr>()) {
- llvm::hlsl::ResourceClass DeclResourceClass = Attr->getResourceClass();
- setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
- resClassSet = true;
}
+
// recurse if there are more fields to analyze
- if (!resClassSet) {
+ else {
for (auto Field : SubRecordDecl->fields()) {
setFlagsFromType(Field->getType(), Flags);
}
@@ -664,11 +651,7 @@ static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
Flags.SRV = true;
} else if (TheVarDecl) {
const HLSLResourceClassAttr *resClassAttr =
- getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
- TheVarDecl, nullptr);
- const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
- while (TheBaseType->isArrayType())
- TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
+ getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
if (resClassAttr) {
llvm::hlsl::ResourceClass DeclResourceClass =
@@ -676,6 +659,9 @@ static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
Flags.Resource = true;
setResourceClassFlagsFromDeclResourceClass(Flags, DeclResourceClass);
} else {
+ const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
+ while (TheBaseType->isArrayType())
+ TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();
if (TheBaseType->isArithmeticType())
Flags.Basic = true;
else if (TheBaseType->isRecordType()) {
@@ -694,7 +680,7 @@ static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
enum class RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };
-static RegisterType getRegisterTypeIndex(StringRef Slot) {
+static RegisterType getRegisterType(StringRef Slot) {
switch (Slot[0]) {
case 't':
case 'T':
@@ -735,7 +721,7 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
if (HLSLResourceBindingAttr *attr =
dyn_cast<HLSLResourceBindingAttr>(*it)) {
- RegisterType otherRegType = getRegisterTypeIndex(attr->getSlot());
+ RegisterType otherRegType = getRegisterType(attr->getSlot());
if (RegisterTypesDetected[static_cast<int>(otherRegType)]) {
if (PreviousConflicts[TheDecl].count(otherRegType))
continue;
@@ -752,16 +738,13 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
}
static std::string getHLSLResourceTypeStr(Sema &S, Decl *TheDecl) {
- VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl);
- HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl);
-
- if (TheVarDecl) {
+ if (VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
QualType TheQualTy = TheVarDecl->getType();
PrintingPolicy PP = S.getPrintingPolicy();
return QualType::getAsString(TheQualTy.split(), PP);
- } else {
- return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
}
+ if (HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl))
+ return CBufferOrTBuffer->isCBuffer() ? "cbuffer" : "tbuffer";
}
static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
@@ -802,8 +785,7 @@ static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
resClassAttr = CBufferOrTBuffer->getAttr<HLSLResourceClassAttr>();
} else if (TheVarDecl) {
resClassAttr =
- getSpecifiedHLSLAttrFromVarDeclOrRecordDecl<HLSLResourceClassAttr>(
- TheVarDecl, nullptr);
+ getSpecifiedHLSLAttrFromVarDecl<HLSLResourceClassAttr>(TheVarDecl);
}
assert(resClassAttr &&
@@ -906,7 +888,7 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
// Validate.
if (!Slot.empty()) {
- regType = getRegisterTypeIndex(Slot);
+ regType = getRegisterType(Slot);
if (regType == RegisterType::I) {
Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
return;
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 5c641d5da44005..0ffa4fed3e5752 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -70,4 +70,4 @@ struct S {
};
// expected-error at +1 {{binding type 'z' is invalid}}
-RWBuffer<float> U3 : register(z5);
\ No newline at end of file
+RWBuffer<float> U3 : register(z5);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
index 858966635c0440..0a547ed66af0a2 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_basic.hlsl
@@ -36,4 +36,4 @@ tbuffer g_tbuffer2 {
};
// expected-error at +1{{binding type 'c' only applies to numeric variables in the global scope}}
-RWBuffer<float> f10 : register(c3);
\ No newline at end of file
+RWBuffer<float> f10 : register(c3);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
index 3ce4739d6ebe28..e63f264452da79 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_silence_diags.hlsl
@@ -24,4 +24,4 @@ struct Eg7 {
};
Bar b;
};
-Eg7 e7 : register(t0);
\ No newline at end of file
+Eg7 e7 : register(t0);
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
index f07ee21d502001..c0e2e927a9a1d6 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error_udt.hlsl
@@ -38,8 +38,8 @@ Eg2 e2 : register(t0) : register(u0);
struct Eg3 {
struct Bar {
MyUAV a;
- };
- Bar b;
+ };
+ Bar b;
};
Eg3 e3 : register(u0);
@@ -120,4 +120,4 @@ struct Eg14{
RWBuffer<int> r1;
};
// expected-warning at +1{{binding type 't' only applies to types containing SRV resources}}
-Eg14 e14 : register(t9);
\ No newline at end of file
+Eg14 e14 : register(t9);
More information about the cfe-commits
mailing list