Skip to content

Commit e3fd0b2

Browse files
committed
[HLSL] Add resource binding attribute for HLSL.
The resource binding attribute is to set the virtual registers and logical register spaces resources in HLSL are bound to. Format is ''register(ID, space)'' like register(t3, space1). ID must be start with t – for shader resource views (SRV), s – for samplers, u – for unordered access views (UAV), b – for constant buffer views (CBV). Register space is default to space0. The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3d12/resource-binding-in-hlsl Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D130033
1 parent 91563c4 commit e3fd0b2

File tree

8 files changed

+259
-6
lines changed

8 files changed

+259
-6
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ def HLSLEntry
145145
: SubsetSubject<Function,
146146
[{S->isExternallyVisible() && !isa<CXXMethodDecl>(S)}],
147147
"global functions">;
148+
def HLSLBufferObj : SubsetSubject<HLSLBuffer,
149+
[{isa<HLSLBufferDecl>(S)}],
150+
"cbuffer/tbuffer">;
148151

149152
def ClassTmpl : SubsetSubject<CXXRecord, [{S->getDescribedClassTemplate()}],
150153
"class templates">;
@@ -4041,6 +4044,14 @@ def HLSLSV_GroupIndex: HLSLAnnotationAttr {
40414044
let Documentation = [HLSLSV_GroupIndexDocs];
40424045
}
40434046

4047+
def HLSLResourceBinding: InheritableAttr {
4048+
let Spellings = [HLSLSemantic<"register">];
4049+
let Subjects = SubjectList<[HLSLBufferObj, GlobalVar]>;
4050+
let LangOpts = [HLSL];
4051+
let Args = [StringArgument<"Slot">, StringArgument<"Space", 1>];
4052+
let Documentation = [HLSLResourceBindingDocs];
4053+
}
4054+
40444055
def HLSLShader : InheritableAttr {
40454056
let Spellings = [Microsoft<"shader">];
40464057
let Subjects = SubjectList<[HLSLEntry]>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6590,6 +6590,31 @@ The full documentation is available here: https://docs.microsoft.com/en-us/windo
65906590
}];
65916591
}
65926592

6593+
def HLSLResourceBindingDocs : Documentation {
6594+
let Category = DocCatFunction;
6595+
let Content = [{
6596+
The resource binding attribute sets the virtual register and logical register space for a resource.
6597+
Attribute spelling in HLSL is: ``register(slot [, space])``.
6598+
``slot`` takes the format ``[type][number]``,
6599+
where ``type`` is a single character specifying the resource type and ``number`` is the virtual register number.
6600+
6601+
Register types are:
6602+
t for shader resource views (SRV),
6603+
s for samplers,
6604+
u for unordered access views (UAV),
6605+
b for constant buffer views (CBV).
6606+
6607+
Register space is specified in the format ``space[number]`` and defaults to ``space0`` if omitted.
6608+
Here're resource binding examples with and without space:
6609+
.. code-block:: c++
6610+
6611+
RWBuffer<float> Uav : register(u3, space1);
6612+
Buffer<float> Buf : register(t1);
6613+
6614+
The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3d12/resource-binding-in-hlsl
6615+
}];
6616+
}
6617+
65936618
def AnnotateTypeDocs : Documentation {
65946619
let Category = DocCatType;
65956620
let Heading = "annotate_type";

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,6 +1619,7 @@ def err_expected_semantic_identifier : Error<
16191619
def err_invalid_declaration_in_hlsl_buffer : Error<
16201620
"invalid declaration inside %select{tbuffer|cbuffer}0">;
16211621
def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">;
1622+
def err_hlsl_separate_attr_arg_and_number : Error<"wrong argument format for hlsl attribute, use %0 instead">;
16221623
def ext_hlsl_access_specifiers : ExtWarn<
16231624
"access specifiers are a clang HLSL extension">,
16241625
InGroup<HLSLExtension>;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11672,6 +11672,9 @@ def err_hlsl_missing_semantic_annotation : Error<
1167211672
def err_hlsl_init_priority_unsupported : Error<
1167311673
"initializer priorities are not supported in HLSL">;
1167411674

11675+
def err_hlsl_unsupported_register_type : Error<"invalid resource class specifier '%0' used; expected 'b', 's', 't', or 'u'">;
11676+
def err_hlsl_unsupported_register_number : Error<"register number should be an integer">;
11677+
def err_hlsl_expected_space : Error<"invalid space specifier '%0' used; expected 'space' followed by an integer, like space1">;
1167511678
def err_hlsl_pointers_unsupported : Error<
1167611679
"%select{pointers|references}0 are unsupported in HLSL">;
1167711680

clang/lib/Parse/ParseHLSL.cpp

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "clang/AST/Attr.h"
1314
#include "clang/Basic/AttributeCommonInfo.h"
1415
#include "clang/Parse/ParseDiagnostic.h"
1516
#include "clang/Parse/Parser.h"
@@ -60,6 +61,9 @@ Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
6061
IdentifierInfo *Identifier = Tok.getIdentifierInfo();
6162
SourceLocation IdentifierLoc = ConsumeToken();
6263

64+
ParsedAttributes Attrs(AttrFactory);
65+
MaybeParseHLSLSemantics(Attrs, nullptr);
66+
6367
ParseScope BufferScope(this, Scope::DeclScope);
6468
BalancedDelimiterTracker T(*this, tok::l_brace);
6569
if (T.consumeOpen()) {
@@ -71,7 +75,6 @@ Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
7175
Identifier, IdentifierLoc,
7276
T.getOpenLocation());
7377

74-
// FIXME: support attribute on cbuffer/tbuffer.
7578
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
7679
// FIXME: support attribute on constants inside cbuffer/tbuffer.
7780
ParsedAttributes Attrs(AttrFactory);
@@ -92,30 +95,103 @@ Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
9295
BufferScope.Exit();
9396
Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
9497

98+
Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
9599
return D;
96100
}
97101

102+
static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
103+
Token Tok, ArgsVector &ArgExprs,
104+
Parser &P, ASTContext &Ctx,
105+
Preprocessor &PP) {
106+
StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
107+
SourceLocation EndNumLoc = Tok.getEndLoc();
108+
109+
P.ConsumeToken(); // consume constant.
110+
std::string FixedArg = ArgStr.str() + Num.str();
111+
P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
112+
<< FixedArg
113+
<< FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
114+
ArgsUnion &Slot = ArgExprs.back();
115+
Slot = IdentifierLoc::create(Ctx, ArgLoc, PP.getIdentifierInfo(FixedArg));
116+
}
117+
98118
void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs,
99119
SourceLocation *EndLoc) {
120+
// FIXME: HLSLSemantic is shared for Semantic and resource binding which is
121+
// confusing. Need a better name to avoid misunderstanding. Issue
122+
// https://github.com/llvm/llvm-project/issues/57882
100123
assert(Tok.is(tok::colon) && "Not a HLSL Semantic");
101124
ConsumeToken();
102125

103-
if (!Tok.is(tok::identifier)) {
126+
IdentifierInfo *II = nullptr;
127+
if (Tok.is(tok::kw_register))
128+
II = PP.getIdentifierInfo("register");
129+
else if (Tok.is(tok::identifier))
130+
II = Tok.getIdentifierInfo();
131+
132+
if (!II) {
104133
Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
105134
return;
106135
}
107136

108-
IdentifierInfo *II = Tok.getIdentifierInfo();
109137
SourceLocation Loc = ConsumeToken();
110138
if (EndLoc)
111139
*EndLoc = Tok.getLocation();
112140
ParsedAttr::Kind AttrKind =
113141
ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLSemantic);
114142

115-
if (AttrKind == ParsedAttr::UnknownAttribute) {
143+
ArgsVector ArgExprs;
144+
switch (AttrKind) {
145+
case ParsedAttr::AT_HLSLResourceBinding: {
146+
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
147+
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
148+
return;
149+
}
150+
if (!Tok.is(tok::identifier)) {
151+
Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
152+
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
153+
return;
154+
}
155+
StringRef SlotStr = Tok.getIdentifierInfo()->getName();
156+
SourceLocation SlotLoc = Tok.getLocation();
157+
ArgExprs.push_back(ParseIdentifierLoc());
158+
159+
// Add numeric_constant for fix-it.
160+
if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
161+
fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
162+
Actions.Context, PP);
163+
164+
if (Tok.is(tok::comma)) {
165+
ConsumeToken(); // consume comma
166+
if (!Tok.is(tok::identifier)) {
167+
Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
168+
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
169+
return;
170+
}
171+
StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
172+
SourceLocation SpaceLoc = Tok.getLocation();
173+
ArgExprs.push_back(ParseIdentifierLoc());
174+
175+
// Add numeric_constant for fix-it.
176+
if (SpaceStr.equals("space") && Tok.is(tok::numeric_constant))
177+
fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
178+
Actions.Context, PP);
179+
}
180+
if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
181+
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
182+
return;
183+
}
184+
} break;
185+
case ParsedAttr::UnknownAttribute:
116186
Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
117187
return;
188+
case ParsedAttr::AT_HLSLSV_GroupIndex:
189+
break;
190+
default:
191+
llvm_unreachable("invalid HLSL Semantic");
192+
break;
118193
}
119-
Attrs.addNew(II, Loc, nullptr, SourceLocation(), nullptr, 0,
120-
ParsedAttr::AS_HLSLSemantic);
194+
195+
Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(),
196+
ArgExprs.size(), ParsedAttr::AS_HLSLSemantic);
121197
}

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6947,6 +6947,78 @@ Sema::mergeHLSLShaderAttr(Decl *D, const AttributeCommonInfo &AL,
69476947
return HLSLShaderAttr::Create(Context, ShaderType, AL);
69486948
}
69496949

6950+
static void handleHLSLResourceBindingAttr(Sema &S, Decl *D,
6951+
const ParsedAttr &AL) {
6952+
StringRef Space = "space0";
6953+
StringRef Slot = "";
6954+
6955+
if (!AL.isArgIdent(0)) {
6956+
S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
6957+
<< AL << AANT_ArgumentIdentifier;
6958+
return;
6959+
}
6960+
6961+
IdentifierLoc *Loc = AL.getArgAsIdent(0);
6962+
StringRef Str = Loc->Ident->getName();
6963+
SourceLocation ArgLoc = Loc->Loc;
6964+
6965+
SourceLocation SpaceArgLoc;
6966+
if (AL.getNumArgs() == 2) {
6967+
Slot = Str;
6968+
if (!AL.isArgIdent(1)) {
6969+
S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
6970+
<< AL << AANT_ArgumentIdentifier;
6971+
return;
6972+
}
6973+
6974+
IdentifierLoc *Loc = AL.getArgAsIdent(1);
6975+
Space = Loc->Ident->getName();
6976+
SpaceArgLoc = Loc->Loc;
6977+
} else {
6978+
Slot = Str;
6979+
}
6980+
6981+
// Validate.
6982+
if (!Slot.empty()) {
6983+
switch (Slot[0]) {
6984+
case 'u':
6985+
case 'b':
6986+
case 's':
6987+
case 't':
6988+
break;
6989+
default:
6990+
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type)
6991+
<< Slot.substr(0, 1);
6992+
return;
6993+
}
6994+
6995+
StringRef SlotNum = Slot.substr(1);
6996+
unsigned Num = 0;
6997+
if (SlotNum.getAsInteger(10, Num)) {
6998+
S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_number);
6999+
return;
7000+
}
7001+
}
7002+
7003+
if (!Space.startswith("space")) {
7004+
S.Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space;
7005+
return;
7006+
}
7007+
StringRef SpaceNum = Space.substr(5);
7008+
unsigned Num = 0;
7009+
if (SpaceNum.getAsInteger(10, Num)) {
7010+
S.Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space;
7011+
return;
7012+
}
7013+
7014+
// FIXME: check reg type match decl. Issue
7015+
// https://github.com/llvm/llvm-project/issues/57886.
7016+
HLSLResourceBindingAttr *NewAttr =
7017+
HLSLResourceBindingAttr::Create(S.getASTContext(), Slot, Space, AL);
7018+
if (NewAttr)
7019+
D->addAttr(NewAttr);
7020+
}
7021+
69507022
static void handleMSInheritanceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
69517023
if (!S.LangOpts.CPlusPlus) {
69527024
S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang)
@@ -8926,6 +8998,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
89268998
case ParsedAttr::AT_HLSLShader:
89278999
handleHLSLShaderAttr(S, D, AL);
89289000
break;
9001+
case ParsedAttr::AT_HLSLResourceBinding:
9002+
handleHLSLResourceBindingAttr(S, D, AL);
9003+
break;
89299004

89309005
case ParsedAttr::AT_AbiTag:
89319006
handleAbiTagAttr(S, D, AL);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
2+
3+
// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB
4+
// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "b3" "space2"
5+
// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
6+
cbuffer CB : register(b3, space2) {
7+
float a;
8+
}
9+
10+
// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:13:9 tbuffer TB
11+
// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} <col:14> "t2" "space1"
12+
// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
13+
tbuffer TB : register(t2, space1) {
14+
float b;
15+
}
16+
17+
float foo() {
18+
// CHECK: BinaryOperator 0x{{[0-9a-f]+}} <col:10, col:14> 'float' '+'
19+
// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue>
20+
// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'float' lvalue Var 0x[[A]] 'a' 'float'
21+
// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue>
22+
// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:14> 'float' lvalue Var 0x[[B]] 'b' 'float'
23+
return a + b;
24+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
2+
3+
// expected-error@+5 {{expected ';' after top level declarator}}
4+
// expected-error@+4 {{expected ')'}}
5+
// expected-note@+3 {{to match this '('}}
6+
// expected-error@+2 {{a type specifier is required for all declarations}}
7+
// expected-error@+1 {{illegal storage class on file-scoped variable}}
8+
float a : register(c0, space1);
9+
10+
// expected-error@+1 {{invalid resource class specifier 'i' used; expected 'b', 's', 't', or 'u'}}
11+
cbuffer b : register(i0) {
12+
13+
}
14+
// expected-error@+1 {{invalid space specifier 's2' used; expected 'space' followed by an integer, like space1}}
15+
cbuffer c : register(b0, s2) {
16+
17+
}
18+
// expected-error@+1 {{register number should be an integer}}
19+
cbuffer d : register(bf, s2) {
20+
21+
}
22+
// expected-error@+1 {{invalid space specifier 'spaces' used; expected 'space' followed by an integer, like space1}}
23+
cbuffer e : register(b2, spaces) {
24+
25+
}
26+
27+
// expected-error@+1 {{expected identifier}}
28+
cbuffer A : register() {}
29+
30+
// expected-error@+1 {{register number should be an integer}}
31+
cbuffer B : register(space1) {}
32+
33+
// expected-error@+1 {{wrong argument format for hlsl attribute, use b2 instead}}
34+
cbuffer C : register(b 2) {}
35+
36+
// expected-error@+2 {{wrong argument format for hlsl attribute, use b2 instead}}
37+
// expected-error@+1 {{wrong argument format for hlsl attribute, use space3 instead}}
38+
cbuffer D : register(b 2, space 3) {}

0 commit comments

Comments
 (0)