[llvm-branch-commits] [clang] [llvm] [HLSL] Allow resource annotations to specify only register space (PR #135287)

Helena Kotas via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Apr 11 12:21:53 PDT 2025


https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/135287

>From 6fb658a603f116f29e635e4802a8d77b896150ff Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 10 Apr 2025 17:31:57 -0700
Subject: [PATCH 1/4] [HLSL] Allow register annotations to specify only `space`

Specifying only `space` in a `register` annotation means the compiler should
implicitly assign a register slot to the resource from the provided virtual
register space.
---
 clang/include/clang/Basic/Attr.td             | 11 ++-
 clang/lib/CodeGen/CGHLSLRuntime.cpp           |  2 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 74 +++++++++++--------
 .../test/AST/HLSL/resource_binding_attr.hlsl  |  4 +
 .../SemaHLSL/resource_binding_attr_error.hlsl |  4 +-
 test.ll                                       |  7 ++
 6 files changed, 64 insertions(+), 38 deletions(-)
 create mode 100644 test.ll

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fd9e686485552..1fe37ad4e2d4d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4751,20 +4751,25 @@ def HLSLResourceBinding: InheritableAttr {
 
   private:
       RegisterType RegType;
-      unsigned SlotNumber;
+      int SlotNumber; // -1 if the register slot was not specified
       unsigned SpaceNumber;
 
   public:
-      void setBinding(RegisterType RT, unsigned SlotNum, unsigned SpaceNum) {
+      void setBinding(RegisterType RT, int SlotNum, unsigned SpaceNum) {
         RegType = RT;
         SlotNumber = SlotNum;
         SpaceNumber = SpaceNum;
       }
+      bool isImplicit() const {
+        return SlotNumber < 0;
+      }
       RegisterType getRegisterType() const {
+        assert(!isImplicit() && "binding does not have register slot");
         return RegType;
       }
       unsigned getSlotNumber() const {
-        return SlotNumber;
+        assert(!isImplicit() && "binding does not have register slot");
+        return (unsigned)SlotNumber;
       }
       unsigned getSpaceNumber() const {
         return SpaceNumber;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 450213fcec676..e42bb8e16e80b 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -261,7 +261,7 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
       BufDecl->getAttr<HLSLResourceBindingAttr>();
   // FIXME: handle implicit binding if no binding attribute is found
   // (llvm/llvm-project#110722)
-  if (RBA)
+  if (RBA && !RBA->isImplicit())
     initializeBufferFromBinding(CGM, BufGV, RBA->getSlotNumber(),
                                 RBA->getSpaceNumber());
 }
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 27959f61f1dc3..73b8c19840026 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1529,72 +1529,82 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
                                     diag::err_incomplete_type))
       return;
   }
-  StringRef Space = "space0";
+
   StringRef Slot = "";
+  StringRef Space = "";
+  SourceLocation SlotLoc, SpaceLoc;
 
   if (!AL.isArgIdent(0)) {
     Diag(AL.getLoc(), diag::err_attribute_argument_type)
         << AL << AANT_ArgumentIdentifier;
     return;
   }
-
   IdentifierLoc *Loc = AL.getArgAsIdent(0);
-  StringRef Str = Loc->Ident->getName();
-  SourceLocation ArgLoc = Loc->Loc;
 
-  SourceLocation SpaceArgLoc;
-  bool SpecifiedSpace = false;
   if (AL.getNumArgs() == 2) {
-    SpecifiedSpace = true;
-    Slot = Str;
+    Slot = Loc->Ident->getName();
+    SlotLoc = Loc->Loc;
+
     if (!AL.isArgIdent(1)) {
       Diag(AL.getLoc(), diag::err_attribute_argument_type)
           << AL << AANT_ArgumentIdentifier;
       return;
     }
 
-    IdentifierLoc *Loc = AL.getArgAsIdent(1);
+    Loc = AL.getArgAsIdent(1);
     Space = Loc->Ident->getName();
-    SpaceArgLoc = Loc->Loc;
+    SpaceLoc = Loc->Loc;
   } else {
-    Slot = Str;
+    StringRef Str = Loc->Ident->getName();
+    if (Str.starts_with("space")) {
+      Space = Str;
+      SpaceLoc = Loc->Loc;
+    } else {
+      Slot = Str;
+      SlotLoc = Loc->Loc;
+      Space = "space0";
+    }
   }
 
-  RegisterType RegType;
-  unsigned SlotNum = 0;
+  RegisterType RegType = RegisterType::SRV;
+  int SlotNum = -1;
   unsigned SpaceNum = 0;
 
-  // Validate.
+  // Validate slot
   if (!Slot.empty()) {
     if (!convertToRegisterType(Slot, &RegType)) {
-      Diag(ArgLoc, diag::err_hlsl_binding_type_invalid) << Slot.substr(0, 1);
+      Diag(SlotLoc, diag::err_hlsl_binding_type_invalid) << Slot.substr(0, 1);
       return;
     }
     if (RegType == RegisterType::I) {
-      Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_i);
+      Diag(SlotLoc, diag::warn_hlsl_deprecated_register_type_i);
       return;
     }
-
     StringRef SlotNumStr = Slot.substr(1);
     if (SlotNumStr.getAsInteger(10, SlotNum)) {
-      Diag(ArgLoc, diag::err_hlsl_unsupported_register_number);
+      Diag(SlotLoc, diag::err_hlsl_unsupported_register_number);
       return;
     }
   }
 
-  if (!Space.starts_with("space")) {
-    Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space;
-    return;
-  }
-  StringRef SpaceNumStr = Space.substr(5);
-  if (SpaceNumStr.getAsInteger(10, SpaceNum)) {
-    Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space;
-    return;
+  // Validate space
+  if (!Space.empty()) {
+    if (!Space.starts_with("space")) {
+      Diag(SpaceLoc, diag::err_hlsl_expected_space) << Space;
+      return;
+    }
+    StringRef SpaceNumStr = Space.substr(5);
+    if (SpaceNumStr.getAsInteger(10, SpaceNum)) {
+      Diag(SpaceLoc, diag::err_hlsl_expected_space) << Space;
+      return;
+    }
   }
 
-  if (!DiagnoseHLSLRegisterAttribute(SemaRef, ArgLoc, TheDecl, RegType,
-                                     SpecifiedSpace))
-    return;
+  // If we have slot, diagnose it is the right register type for the decl
+  if (SlotNum >= 0)
+    if (!DiagnoseHLSLRegisterAttribute(SemaRef, SlotLoc, TheDecl, RegType,
+                                       !SpaceLoc.isInvalid()))
+      return;
 
   HLSLResourceBindingAttr *NewAttr =
       HLSLResourceBindingAttr::Create(getASTContext(), Slot, Space, AL);
@@ -1967,7 +1977,7 @@ void SemaHLSL::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
     for (const Decl *VD : DefaultCBufferDecls) {
       const HLSLResourceBindingAttr *RBA =
           VD->getAttr<HLSLResourceBindingAttr>();
-      if (RBA &&
+      if (RBA && !RBA->isImplicit() &&
           RBA->getRegisterType() == HLSLResourceBindingAttr::RegisterType::C) {
         DefaultCBuffer->setHasValidPackoffset(true);
         break;
@@ -3227,7 +3237,7 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
 
 static bool initGlobalResourceDecl(Sema &S, VarDecl *VD) {
   HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
-  if (!RBA)
+  if (!RBA || RBA->isImplicit())
     // FIXME: add support for implicit binding (llvm/llvm-project#110722)
     return false;
 
@@ -3310,7 +3320,7 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
 
   for (Attr *A : VD->attrs()) {
     HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
-    if (!RBA)
+    if (!RBA || RBA->isImplicit())
       continue;
 
     RegisterType RT = RBA->getRegisterType();
diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl
index af43eddc45edd..c073cd4dc1476 100644
--- a/clang/test/AST/HLSL/resource_binding_attr.hlsl
+++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl
@@ -30,6 +30,10 @@ RWBuffer<float> UAV : register(u3);
 // CHECK: HLSLResourceBindingAttr {{.*}} "u4" "space0"
 RWBuffer<float> UAV1 : register(u2), UAV2 : register(u4);
 
+// CHECK: VarDecl {{.*}} UAV3 'RWBuffer<float>':'hlsl::RWBuffer<float>'
+// CHECK: HLSLResourceBindingAttr {{.*}} "" "space5"
+RWBuffer<float> UAV3 : register(space5);
+
 //
 // Default constants ($Globals) layout annotations
 
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 74aff79f0e37f..5f0ab66061315 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -22,8 +22,8 @@ cbuffer c : register(bf, s2) {
 // expected-error at +1 {{expected identifier}}
 cbuffer A : register() {}
 
-// expected-error at +1 {{register number should be an integer}}
-cbuffer B : register(space1) {}
+// expected-error at +1 {{invalid space specifier 'space' used; expected 'space' followed by an integer, like space1}}
+cbuffer B : register(space) {}
 
 // expected-error at +1 {{wrong argument format for hlsl attribute, use b2 instead}}
 cbuffer C : register(b 2) {}
diff --git a/test.ll b/test.ll
new file mode 100644
index 0000000000000..dc674289e3e74
--- /dev/null
+++ b/test.ll
@@ -0,0 +1,7 @@
+ByteAddressBuffer Buffer0: register(t0);
+RWBuffer<float> Buf : register(u0);
+
+[numthreads(4,1,1)]
+void main() {
+  Buf[0] = 10;
+}

>From c5c7ab8ace5d82a3f84c5b00738b1d7fd3b3b311 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 10 Apr 2025 18:13:35 -0700
Subject: [PATCH 2/4] remove condition - Space is never empty

---
 clang/lib/Sema/SemaHLSL.cpp | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 73b8c19840026..786bfcb3acf7a 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1588,16 +1588,14 @@ void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
   }
 
   // Validate space
-  if (!Space.empty()) {
-    if (!Space.starts_with("space")) {
-      Diag(SpaceLoc, diag::err_hlsl_expected_space) << Space;
-      return;
-    }
-    StringRef SpaceNumStr = Space.substr(5);
-    if (SpaceNumStr.getAsInteger(10, SpaceNum)) {
-      Diag(SpaceLoc, diag::err_hlsl_expected_space) << Space;
-      return;
-    }
+  if (!Space.starts_with("space")) {
+    Diag(SpaceLoc, diag::err_hlsl_expected_space) << Space;
+    return;
+  }
+  StringRef SpaceNumStr = Space.substr(5);
+  if (SpaceNumStr.getAsInteger(10, SpaceNum)) {
+    Diag(SpaceLoc, diag::err_hlsl_expected_space) << Space;
+    return;
   }
 
   // If we have slot, diagnose it is the right register type for the decl

>From 7084cf1978d01f1d4295e3facacf667b3f020fb1 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Thu, 10 Apr 2025 18:16:31 -0700
Subject: [PATCH 3/4] remove unintentionally added file

---
 test.ll | 7 -------
 1 file changed, 7 deletions(-)
 delete mode 100644 test.ll

diff --git a/test.ll b/test.ll
deleted file mode 100644
index dc674289e3e74..0000000000000
--- a/test.ll
+++ /dev/null
@@ -1,7 +0,0 @@
-ByteAddressBuffer Buffer0: register(t0);
-RWBuffer<float> Buf : register(u0);
-
-[numthreads(4,1,1)]
-void main() {
-  Buf[0] = 10;
-}

>From b79e4ddae3485511dd75038a1e2be8210d041969 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Fri, 11 Apr 2025 12:21:09 -0700
Subject: [PATCH 4/4] Handle register(u-1);

---
 clang/lib/Parse/ParseHLSL.cpp                        | 11 ++++++++---
 clang/test/SemaHLSL/resource_binding_attr_error.hlsl |  3 +++
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp
index f4c109f9a81a2..6e8fd56fe5262 100644
--- a/clang/lib/Parse/ParseHLSL.cpp
+++ b/clang/lib/Parse/ParseHLSL.cpp
@@ -163,11 +163,16 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
     SourceLocation SlotLoc = Tok.getLocation();
     ArgExprs.push_back(ParseIdentifierLoc());
 
-    // Add numeric_constant for fix-it.
-    if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
+    if (SlotStr.size() == 1) {
+      if (!Tok.is(tok::numeric_constant)) {
+        Diag(Tok.getLocation(), diag::err_expected) << tok::numeric_constant;
+        SkipUntil(tok::r_paren, StopAtSemi); // skip through )
+        return;
+      }
+      // Add numeric_constant for fix-it.
       fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
                                   Actions.Context, PP);
-
+    }
     if (Tok.is(tok::comma)) {
       ConsumeToken(); // consume comma
       if (!Tok.is(tok::identifier)) {
diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
index 5f0ab66061315..d126d25bd94f0 100644
--- a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
+++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl
@@ -31,6 +31,9 @@ cbuffer C : register(b 2) {}
 // expected-error at +1 {{wrong argument format for hlsl attribute, use b2 instead}}
 cbuffer D : register(b 2, space3) {}
 
+// expected-error at +1 {{expected <numeric_constant>}}
+cbuffer E : register(u-1) {};
+
 // expected-error at +1 {{'register' attribute only applies to cbuffer/tbuffer and external global variables}}
 static MyTemplatedSRV<float> U : register(u5);
 



More information about the llvm-branch-commits mailing list