python3kgae created this revision. python3kgae added reviewers: svenvh, asavonic, beanz, pow2clk, Anastasia, aaron.ballman. Herald added a project: All. python3kgae requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
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 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D130033 Files: clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/lib/Parse/ParseHLSL.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/test/AST/HLSL/resource_binding_attr.hlsl clang/test/SemaHLSL/resource_binding_attr_error.hlsl
Index: clang/test/SemaHLSL/resource_binding_attr_error.hlsl =================================================================== --- /dev/null +++ clang/test/SemaHLSL/resource_binding_attr_error.hlsl @@ -0,0 +1,25 @@ +// 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 {{register type is unsupported - available types are 'b', 's', 't', 'u'}} +cbuffer a : register(i0) { + +} +// expected-error@+1 {{expected space definition with syntax 'spaceX', where X is an integral value}} +cbuffer a : register(b0, s2) { + +} +// expected-error@+1 {{register number should be an integral numeric string}} +cbuffer a : register(bf, s2) { + +} +// expected-error@+1 {{space number should be an integral numeric string}} +cbuffer a : register(b2, spaces) { + +} Index: clang/test/AST/HLSL/resource_binding_attr.hlsl =================================================================== --- /dev/null +++ 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]+}} <col:14> CBuffer 3 2 +// 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]+}} <col:14> SRV 2 1 +// 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]+}} <col:10, col:14> 'float' '+' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue> +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'float' lvalue Var 0x[[A]] 'a' 'float' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue> +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:14> 'float' lvalue Var 0x[[B]] 'b' 'float' + return a + b; +} Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6931,6 +6931,63 @@ return HLSLShaderAttr::Create(Context, ShaderType, AL); } +static void handleHLSLResourceBindingAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + StringRef Str; + SourceLocation ArgLoc; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) + return; + + HLSLResourceBindingAttr::ResourceClass RC; + if (!HLSLResourceBindingAttr::ConvertStrToResourceClass(Str, RC)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL << Str << ArgLoc; + return; + } + if (!AL.isArgExpr(1)) { + return; + } + if (!AL.isArgExpr(2)) { + return; + } + + auto *Arg1 = AL.getArgAsExpr(1); + auto *CArg1 = dyn_cast<IntegerLiteral>(Arg1); + if (!CArg1) { + return; + } + auto *Arg2 = AL.getArgAsExpr(2); + auto *CArg2 = dyn_cast<IntegerLiteral>(Arg2); + if (!CArg2) { + return; + } + + unsigned RegID = CArg1->getValue().getLimitedValue(); + + unsigned SpaceID = CArg2->getValue().getLimitedValue(); + // FIXME: check function match the shader stage. + + HLSLResourceBindingAttr *NewAttr = + S.mergeHLSLResourceBindingAttr(D, AL, RC, RegID, SpaceID); + if (NewAttr) + D->addAttr(NewAttr); +} + +HLSLResourceBindingAttr * +Sema::mergeHLSLResourceBindingAttr(Decl *D, const AttributeCommonInfo &AL, + HLSLResourceBindingAttr::ResourceClass RC, + int RegID, int SpaceID) { + if (HLSLResourceBindingAttr *NT = D->getAttr<HLSLResourceBindingAttr>()) { + if (NT->getType() != RC || NT->getSpace() != SpaceID || + NT->getID() != RegID) { + Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; + Diag(AL.getLoc(), diag::note_conflicting_attribute); + } + return nullptr; + } + return HLSLResourceBindingAttr::Create(Context, RC, RegID, SpaceID, 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) @@ -8907,6 +8964,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); Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -2807,6 +2807,9 @@ S.mergeHLSLNumThreadsAttr(D, *NT, NT->getX(), NT->getY(), NT->getZ()); else if (const auto *SA = dyn_cast<HLSLShaderAttr>(Attr)) NewAttr = S.mergeHLSLShaderAttr(D, *SA, SA->getType()); + else if (const auto *RB = dyn_cast<HLSLResourceBindingAttr>(Attr)) + NewAttr = S.mergeHLSLResourceBindingAttr(D, *RB, RB->getType(), RB->getID(), + RB->getSpace()); else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr)) NewAttr = cast<InheritableAttr>(Attr->clone(S.Context)); Index: clang/lib/Parse/ParseHLSL.cpp =================================================================== --- clang/lib/Parse/ParseHLSL.cpp +++ 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); + MaybeParseHLSLResourceBinding(Attrs); + 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,6 +58,7 @@ BufferScope.Exit(); Actions.ActOnFinishHLSLBuffer(D, DeclEnd); + Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs); return D; } @@ -83,3 +86,109 @@ Attrs.addNew(II, Loc, nullptr, SourceLocation(), nullptr, 0, ParsedAttr::AS_HLSLSemantic); } + +void Parser::ParseHLSLResourceBinding(ParsedAttributes &Attrs, + SourceLocation *EndLoc) { + // : register (Type#[subcomponent] [,spaceX]) + ConsumeToken(); // consume colon. + + auto KwLoc = Tok.getLocation(); + ConsumeToken(); // consume kw_register. + + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after, + "register")) { + 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; + } + + auto ResourceLoc = Tok.getLocation(); + StringRef Text = Tok.getIdentifierInfo()->getName(); + if (Text.empty()) { + Diag(ResourceLoc, diag::err_hlsl_unsupported_register_type); + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + + // validate text. + unsigned RegID = 0; + // It's valid to omit the register number. + if (Text.size() > 1) { + StringRef NumName = Text.substr(1); + if (NumName.getAsInteger(10, RegID)) { + Diag(ResourceLoc, diag::err_hlsl_unsupported_register_number); + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + } + + StringRef ResourceType = Text.substr(0, 1); + HLSLResourceBindingAttr::ResourceClass RC; + if (!HLSLResourceBindingAttr::ConvertStrToResourceClass(ResourceType, RC)) { + Diag(ResourceLoc, diag::err_hlsl_unsupported_register_type); + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + ConsumeToken(); // consume register (type'#') + + unsigned SpaceID = 0; + 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; + } + auto SpaceLoc = Tok.getLocation(); + StringRef Text = Tok.getIdentifierInfo()->getName(); + // validate space. + if (!Text.startswith_insensitive("space")) { + Diag(SpaceLoc, diag::err_hlsl_expected_space); + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + StringRef NumName = Text.substr(5); + if (NumName.getAsInteger(10, SpaceID)) { + Diag(SpaceLoc, diag::err_hlsl_unsupported_space_number); + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + + ConsumeToken(); // consume space + } + + if (ExpectAndConsume(tok::r_paren, diag::err_expected)) { + SkipUntil(tok::r_paren, StopAtSemi); // skip through ) + return; + } + + auto &Ctx = Actions.Context; + QualType CharTy = Ctx.CharTy.withConst(); + llvm::APInt Size(Ctx.getTypeSize(Ctx.getSizeType()), 2); + QualType ArrayTy = + Ctx.getConstantArrayType(CharTy, Size, nullptr, ArrayType::Normal, 0); + + StringLiteral *SResourceType = + StringLiteral::Create(Ctx, ResourceType, StringLiteral::Ordinary, + /*Pascal*/ false, ArrayTy, ResourceLoc); + + QualType Int64Ty = Ctx.LongLongTy; + auto *CRegID = IntegerLiteral::Create(Ctx, llvm::APSInt::get(RegID), Int64Ty, + SourceLocation()); + + auto *CSpaceID = IntegerLiteral::Create(Ctx, llvm::APSInt::get(SpaceID), + Int64Ty, SourceLocation()); + + ArgsUnion Args[3] = {SResourceType, CRegID, CSpaceID}; + + Attrs.addNew(PP.getIdentifierInfo("register"), KwLoc, nullptr, + SourceLocation(), Args, 3, + ParsedAttr::AS_ContextSensitiveKeyword); + + return; +} Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -3572,6 +3572,10 @@ int X, int Y, int Z); HLSLShaderAttr *mergeHLSLShaderAttr(Decl *D, const AttributeCommonInfo &AL, HLSLShaderAttr::ShaderType ShaderType); + HLSLResourceBindingAttr * + mergeHLSLResourceBindingAttr(Decl *D, const AttributeCommonInfo &AL, + HLSLResourceBindingAttr::ResourceClass, + int RegID, int SpaceID); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2817,6 +2817,15 @@ void ParseHLSLSemantics(ParsedAttributes &Attrs, SourceLocation *EndLoc = nullptr); + + void MaybeParseHLSLResourceBinding(ParsedAttributes &Attrs, + SourceLocation *EndLoc = nullptr) { + if (getLangOpts().HLSL && Tok.is(tok::colon)) + ParseHLSLResourceBinding(Attrs, EndLoc); + } + void ParseHLSLResourceBinding(ParsedAttributes &Attrs, + SourceLocation *EndLoc = nullptr); + Decl *ParseHLSLBuffer(SourceLocation &DeclEnd, SourceLocation InlineLoc = SourceLocation()); Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1604,6 +1604,11 @@ def err_expected_semantic_identifier : Error< "expected HLSL Semantic identifier">; def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">; +def err_hlsl_unsupported_register_type : Error<"register type is unsupported - available types are 'b', 's', 't', 'u'">; +def err_hlsl_unsupported_register_number : Error<"register number should be an integral numeric string">; +def err_hlsl_expected_space : Error<"expected space definition with syntax 'spaceX', where X is an integral value">; +def err_hlsl_unsupported_space_number : Error<"space number should be an integral numeric string">; + def ext_hlsl_access_specifiers : ExtWarn< "access specifiers are a clang HLSL extension">, InGroup<HLSLExtension>; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -6510,6 +6510,23 @@ }]; } +def HLSLResourceBindingDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +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 + }]; +} + def AnnotateTypeDocs : Documentation { let Category = DocCatType; let Heading = "annotate_type"; Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -145,6 +145,9 @@ : SubsetSubject<Function, [{S->isExternallyVisible() && !isa<CXXMethodDecl>(S)}], "global functions">; +def HLSLBufferObj : SubsetSubject<HLSLBuffer, + [{isa<HLSLBufferDecl>(S)}], + "global functions">; def ClassTmpl : SubsetSubject<CXXRecord, [{S->getDescribedClassTemplate()}], "class templates">; @@ -4005,6 +4008,17 @@ let Documentation = [HLSLSV_GroupIndexDocs]; } +def HLSLResourceBinding: InheritableAttr { + let Spellings = [Keyword<"register">]; + let Subjects = SubjectList<[HLSLBufferObj, GlobalVar]>; + let LangOpts = [HLSL]; + let Args = [EnumArgument<"Type", "ResourceClass", + ["t", "u", "b", "s"], + ["SRV", "UAV", "CBuffer", "Sampler"] + >, DefaultIntArgument<"ID", 0>, DefaultIntArgument<"Space", 0>]; + let Documentation = [HLSLResourceBindingDocs]; +} + def HLSLShader : InheritableAttr { let Spellings = [Microsoft<"shader">]; let Subjects = SubjectList<[HLSLEntry]>;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits