diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -145,6 +145,9 @@ : SubsetSubjectisExternallyVisible() && !isa(S)}], "global functions">; +def HLSLBufferObj : SubsetSubject(S)}], + "cbuffer/tbuffer">; def ClassTmpl : SubsetSubjectgetDescribedClassTemplate()}], "class templates">; @@ -4012,6 +4015,14 @@ let Documentation = [HLSLSV_GroupIndexDocs]; } +def HLSLResourceBinding: InheritableAttr { + let Spellings = [HLSLSemantic<"register">]; + let Subjects = SubjectList<[HLSLBufferObj, GlobalVar]>; + let LangOpts = [HLSL]; + let Args = [StringArgument<"Slot">, StringArgument<"Space", 1>]; + let Documentation = [HLSLResourceBindingDocs]; +} + def HLSLShader : InheritableAttr { let Spellings = [Microsoft<"shader">]; let Subjects = SubjectList<[HLSLEntry]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6538,6 +6538,28 @@ }]; } +def HLSLResourceBindingDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The resource binding attribute sets the virtual register and logical register space for a resource. +Attribute spelling in HLSL is: ``register(slot [, space])``. +``slot`` takes the format ``[type][number]``, +where ``type`` is a single character specifying the resource type and ``number`` is the virtual register number. + +Register types are: +t for shader resource views (SRV), +s for samplers, +u for unordered access views (UAV), +b for constant buffer views (CBV). + +Register space is specified in the format ``space[number]`` and defaults to ``space0`` if omitted. +Here're resource binding examples with and without space: +``register(t3, space1)`` +``register(t1)`` +The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3d12/resource-binding-in-hlsl + }]; +} + def AnnotateTypeDocs : Documentation { let Category = DocCatType; let Heading = "annotate_type"; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1606,6 +1606,7 @@ def err_expected_semantic_identifier : Error< "expected HLSL Semantic identifier">; def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">; + def ext_hlsl_access_specifiers : ExtWarn< "access specifiers are a clang HLSL extension">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11647,7 +11647,9 @@ def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">; def err_hlsl_missing_numthreads : Error<"missing numthreads attribute for %0 shader entry">; def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not match the previous declaration">; - +def err_hlsl_unsupported_register_type : Error<"invalid resource class specifier '%0' used; expected 'b', 's', 't', or 'u'">; +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 err_hlsl_pointers_unsupported : Error< "%select{pointers|references}0 are unsupported in HLSL">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3584,6 +3584,9 @@ int X, int Y, int Z); HLSLShaderAttr *mergeHLSLShaderAttr(Decl *D, const AttributeCommonInfo &AL, HLSLShaderAttr::ShaderType ShaderType); + HLSLResourceBindingAttr * + mergeHLSLResourceBindingAttr(Decl *D, const AttributeCommonInfo &AL, + StringRef Slot, StringRef Space); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp --- a/clang/lib/Parse/ParseHLSL.cpp +++ b/clang/lib/Parse/ParseHLSL.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/Attr.h" #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" @@ -32,6 +33,9 @@ IdentifierInfo *Identifier = Tok.getIdentifierInfo(); SourceLocation IdentifierLoc = ConsumeToken(); // consume identifier + ParsedAttributes Attrs(AttrFactory); + MaybeParseHLSLSemantics(Attrs, nullptr); + ParseScope BufferScope(this, Scope::DeclScope); BalancedDelimiterTracker T(*this, tok::l_brace); if (T.consumeOpen()) { @@ -43,8 +47,6 @@ Identifier, IdentifierLoc, T.getOpenLocation()); - // FIXME: support attribute on cbuffer/tbuffer. - while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { ParsedAttributes Attrs(AttrFactory); // FIXME: support attribute on constants inside cbuffer/tbuffer. @@ -56,30 +58,72 @@ BufferScope.Exit(); Actions.ActOnFinishHLSLBuffer(D, DeclEnd); + Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs); return D; } void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs, SourceLocation *EndLoc) { + // FIXME: HLSLSemantic is shared for Semantic and resource binding which is + // confusing. Need a better name to avoid misunderstanding. assert(Tok.is(tok::colon) && "Not a HLSL Semantic"); ConsumeToken(); - if (!Tok.is(tok::identifier)) { + IdentifierInfo *II = nullptr; + if (Tok.is(tok::kw_register)) + II = PP.getIdentifierInfo("register"); + else if (Tok.is(tok::identifier)) + II = Tok.getIdentifierInfo(); + + if (!II) { Diag(Tok.getLocation(), diag::err_expected_semantic_identifier); return; } - IdentifierInfo *II = Tok.getIdentifierInfo(); SourceLocation Loc = ConsumeToken(); if (EndLoc) *EndLoc = Tok.getLocation(); ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLSemantic); - if (AttrKind == ParsedAttr::UnknownAttribute) { + ArgsVector ArgExprs; + switch (AttrKind) { + case ParsedAttr::AT_HLSLResourceBinding: { + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) { + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + if (!Tok.is(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::identifier; + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + ArgExprs.push_back(ParseIdentifierLoc()); + + if (Tok.is(tok::comma)) { + ConsumeToken(); // consume comma + if (!Tok.is(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::identifier; + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + ArgExprs.push_back(ParseIdentifierLoc()); + } + if (ExpectAndConsume(tok::r_paren, diag::err_expected)) { + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + } break; + case ParsedAttr::UnknownAttribute: Diag(Loc, diag::err_unknown_hlsl_semantic) << II; return; + case ParsedAttr::AT_HLSLSV_GroupIndex: + break; + default: + llvm_unreachable("invalid HLSL Semantic"); + break; } - Attrs.addNew(II, Loc, nullptr, SourceLocation(), nullptr, 0, - ParsedAttr::AS_HLSLSemantic); + + Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(), + ArgExprs.size(), ParsedAttr::AS_HLSLSemantic); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2889,6 +2889,9 @@ S.mergeHLSLNumThreadsAttr(D, *NT, NT->getX(), NT->getY(), NT->getZ()); else if (const auto *SA = dyn_cast(Attr)) NewAttr = S.mergeHLSLShaderAttr(D, *SA, SA->getType()); + else if (const auto *RB = dyn_cast(Attr)) + NewAttr = + S.mergeHLSLResourceBindingAttr(D, *RB, RB->getSlot(), RB->getSpace()); else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr)) NewAttr = cast(Attr->clone(S.Context)); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6934,6 +6934,92 @@ return HLSLShaderAttr::Create(Context, ShaderType, AL); } +static void handleHLSLResourceBindingAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + StringRef Space = "space0"; + StringRef Slot = ""; + + StringRef Str; + SourceLocation ArgLoc; + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIdentifier; + return; + } + + IdentifierLoc *Loc = AL.getArgAsIdent(0); + Str = Loc->Ident->getName(); + ArgLoc = Loc->Loc; + + SourceLocation SpaceArgLoc; + if (AL.getNumArgs() == 2) { + Slot = Str; + if (!AL.isArgIdent(1)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_type) + << AL << AANT_ArgumentIdentifier; + return; + } + + IdentifierLoc *Loc = AL.getArgAsIdent(1); + Space = Loc->Ident->getName(); + SpaceArgLoc = Loc->Loc; + } else { + Slot = Str; + } + + // Validate. + if (!Slot.empty()) { + switch (Slot[0]) { + case 'u': + case 'b': + case 's': + case 't': + break; + default: + S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_type) + << Slot.substr(0, 1); + return; + } + + StringRef SlotNum = Slot.substr(1); + unsigned Num = 0; + if (SlotNum.getAsInteger(10, Num)) { + S.Diag(ArgLoc, diag::err_hlsl_unsupported_register_number); + return; + } + } + + if (!Space.startswith_insensitive("space")) { + S.Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space; + return; + } + StringRef SpaceNum = Space.substr(5); + unsigned Num = 0; + if (SpaceNum.getAsInteger(10, Num)) { + S.Diag(SpaceArgLoc, diag::err_hlsl_expected_space) << Space; + return; + } + + // FIXME: check reg type match decl. + HLSLResourceBindingAttr *NewAttr = + S.mergeHLSLResourceBindingAttr(D, AL, Slot, Space); + if (NewAttr) + D->addAttr(NewAttr); +} + +HLSLResourceBindingAttr * +Sema::mergeHLSLResourceBindingAttr(Decl *D, const AttributeCommonInfo &AL, + StringRef Slot, StringRef Space) { + if (HLSLResourceBindingAttr *NT = D->getAttr()) { + if (NT->getSlot() != Slot || NT->getSpace() != Space) { + Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; + Diag(AL.getLoc(), diag::note_conflicting_attribute); + } + return nullptr; + } + return HLSLResourceBindingAttr::Create(Context, Slot, Space, AL); +} + static void handleMSInheritanceAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!S.LangOpts.CPlusPlus) { S.Diag(AL.getLoc(), diag::err_attribute_not_supported_in_lang) @@ -8913,6 +8999,9 @@ case ParsedAttr::AT_HLSLShader: handleHLSLShaderAttr(S, D, AL); break; + case ParsedAttr::AT_HLSLResourceBinding: + handleHLSLResourceBindingAttr(S, D, AL); + break; case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); diff --git a/clang/test/AST/HLSL/resource_binding_attr.hlsl b/clang/test/AST/HLSL/resource_binding_attr.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/resource_binding_attr.hlsl @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s + +// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:6:9 cbuffer CB +// CHECK-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} "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-NEXT:HLSLResourceBindingAttr 0x{{[0-9a-f]+}} "t2" "space1" +// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float' +tbuffer TB : register(t2, space1) { + float b; +} + +float foo() { +// CHECK: BinaryOperator 0x{{[0-9a-f]+}} 'float' '+' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[A]] 'a' 'float' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[B]] 'b' 'float' + return a + b; +} diff --git a/clang/test/SemaHLSL/resource_binding_attr_error.hlsl b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/resource_binding_attr_error.hlsl @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify + +// expected-error@+5 {{expected ';' after top level declarator}} +// expected-error@+4 {{expected ')'}} +// expected-note@+3 {{to match this '('}} +// expected-error@+2 {{a type specifier is required for all declarations}} +// expected-error@+1 {{illegal storage class on file-scoped variable}} +float a : register(c0, space1); + +// expected-error@+1 {{invalid resource class specifier 'i' used; expected 'b', 's', 't', or 'u'}} +cbuffer a : register(i0) { + +} +// expected-error@+1 {{invalid space specifier 's2' used; expected 'space' followed by an integer, like space1}} +cbuffer a : register(b0, s2) { + +} +// expected-error@+1 {{register number should be an integer}} +cbuffer a : register(bf, s2) { + +} +// expected-error@+1 {{invalid space specifier 'spaces' used; expected 'space' followed by an integer, like space1}} +cbuffer a : register(b2, spaces) { + +} + +// expected-error@+1 {{expected identifier}} +cbuffer A : register() {} + +// expected-error@+1 {{register number should be an integer}} +cbuffer B : register(space1) {}