[clang] Implement resource binding type prefix mismatch flag setting logic (PR #97103)
Joshua Batista via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 5 17:01:35 PDT 2024
https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/97103
>From c784272b3f66ca06be4ab8e72a0963e5ebb6a869 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/12] 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 | 26 +-
clang/lib/Sema/SemaHLSL.cpp | 232 +++++++++++++++++-
.../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, 346 insertions(+), 33 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 452cd1810f653..c3d67e19656da 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4467,7 +4467,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 96f0c0f0205c2..3bf15ff5f2657 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12303,7 +12303,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 4d6958a1be3e5..d3d36d04d1019 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 a2b29a7bdf505..b82cd8373403a 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -490,23 +490,24 @@ 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()
- .addDefaultHandleConstructor(S, RC)
- .annotateResourceClass(RC, RK, IsROV);
+ .addDefaultHandleConstructor(S, RC);
}
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();
});
@@ -514,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 eebe17a5b4bf7..d2b2f163231c9 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);
@@ -437,7 +460,206 @@ void SemaHLSL::handleShaderAttr(Decl *D, const ParsedAttr &AL) {
D->addAttr(NewAttr);
}
+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 = "";
@@ -470,13 +692,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;
}
@@ -500,8 +724,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 a98dc0f4ce431..727d505471bcb 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 7204dcd16e0a9..a67688d510ea6 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 060288c2f7f76..1dc99620c55c4 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 e9a6ea1a16312..e264241e62e92 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 71900f2dbda55..9752494f5adc9 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 2f8aa098db701..58a1f3f1f64d7 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 0000000000000..b47171f878436
--- /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 8e20fce2dbb0e88bc6c286815d36339f37d34d80 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/12] 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 1dc99620c55c4..9deb63caa500a 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 b47171f878436..0000000000000
--- 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 8e19f3dd702e0e65b991b2a741405c224a066ba0 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/12] clang-format
---
clang/include/clang/Sema/SemaHLSL.h | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index d3d36d04d1019..4e2a93209386e 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 d2b2f163231c9..8b6ecc6b7d992 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -546,7 +546,7 @@ void traverseType(QualType T, register_binding_flags &r) {
case llvm::hlsl::ResourceClass::Sampler: {
r.sampler = true;
break;
- }
+ }
}
}
@@ -617,7 +617,7 @@ register_binding_flags HLSLFillRegisterBindingFlags(Sema &S, Decl *D) {
case llvm::hlsl::ResourceClass::Sampler: {
r.sampler = true;
break;
- }
+ }
}
} else {
if (SamplerUAVOrSRV->getType()->isBuiltinType())
@@ -653,11 +653,11 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
// 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))
+ if (dyn_cast<VarDecl>(D)) {
+ if (SemaRef.RequireCompleteType(D->getBeginLoc(),
+ cast<ValueDecl>(D)->getType(),
+ diag::err_incomplete_type))
return;
}
StringRef Space = "space0";
>From ba058afa3c79185396351cfe9d80ff6aa6ac3318 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/12] 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 4e2a93209386e..d72e136bd5457 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 c02869d8be071354dd26d3bb8af62056a0f3a2ee 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/12] 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 3bf15ff5f2657..a4994922b15c9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12303,6 +12303,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 8b6ecc6b7d992..0cdce9e53bd93 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -639,10 +639,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
@@ -650,7 +666,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 b75f9fb7356b9c4539cad609957b4f2773c42216 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/12] 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 0cdce9e53bd93..8cd793ceff4c9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -667,7 +667,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;
@@ -675,33 +676,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;
@@ -732,54 +732,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) {
@@ -789,26 +786,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 a2bf4416ed91cf9ab408b6fb773fcc2e5c528696 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/12] 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 8cd793ceff4c9..1d1ced083b1d3 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -492,31 +492,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) {
@@ -596,7 +604,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();
@@ -663,8 +671,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 &&
@@ -679,6 +691,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)
@@ -691,42 +705,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;
}
@@ -747,18 +762,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;
}
@@ -772,37 +787,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 58a1f3f1f64d7..ce81574a79608 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 0000000000000..54e852483f832
--- /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 7007fdccf9afa97f6d26dc6ec7eced7270332968 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/12] 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 1d1ced083b1d3..06942577afb96 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -491,40 +491,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) {
@@ -780,46 +787,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 54e852483f832..2be78b4431499 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 0000000000000..2f4d20b40a0fd
--- /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 d2fd280e7e3177598f7856e0e2d149d3d461a07e 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/12] add warnings to warning-flags.c file, do a nullptr
check
---
clang/lib/Sema/SemaHLSL.cpp | 2 ++
clang/test/Misc/warning-flags.c | 7 ++++++-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 06942577afb96..c64d0c144d0eb 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -524,6 +524,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 ae14720959a0e..a5565ad193dfa 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -18,7 +18,7 @@ This test serves two purposes:
The list of warnings below should NEVER grow. It should gradually shrink to 0.
-CHECK: Warnings without flags (65):
+CHECK: Warnings without flags (70):
CHECK-NEXT: ext_expected_semi_decl_list
CHECK-NEXT: ext_missing_whitespace_after_macro_name
@@ -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 9e6cf8396f63295ae56fa30a8e32f4cb5567de32 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/12] 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 c64d0c144d0eb..d0f1ab8dfd060 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -660,10 +660,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)
@@ -780,8 +779,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 837e4f4d870bfc2ba9c3e5c76487f8885ce2c5e2 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/12] 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 a4994922b15c9..0bc9999a34129 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12307,7 +12307,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">;
@@ -12315,7 +12315,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 6880fa4bb0b03..e8121e4d7a160 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3016,7 +3016,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");
@@ -3024,7 +3024,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 a07f7ad2233fe..baae55a92e34b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2311,7 +2311,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 d0f1ab8dfd060..ec1d335e1ded4 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -658,18 +658,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,
@@ -705,6 +725,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.
@@ -754,6 +775,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
break;
}
}
+ return;
}
// next, handle diagnostics for when the "basic" flag is set,
@@ -785,6 +807,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
@@ -833,6 +856,7 @@ static void DiagnoseHLSLResourceRegType(Sema &S, SourceLocation &ArgLoc,
<< Slot.front() << typestr;
}
}
+ return;
}
}
@@ -883,7 +907,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 2f4d20b40a0fd..28a747b965b22 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 5dfc0bf4ba768f02f408e9315d2ba3170556fe3f 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/12] 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 de99cb46d3145..edb04f75dcf71 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1520,6 +1520,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 0bc9999a34129..9439bc0f81a91 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12303,14 +12303,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 ec1d335e1ded4..f696bbff2823b 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -788,7 +788,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);
}
@@ -803,9 +803,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 0000000000000..64cff79274edd
--- /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 0000000000000..4c9e9a6b44c92
--- /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);
More information about the cfe-commits
mailing list