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 @@ -300,6 +300,9 @@ bit AllowInC = allowInC; } +// HLSL Semantic spellings +class HLSLSemantic : Spelling; + class Accessor spellings> { string Name = name; list Spellings = spellings; @@ -3958,6 +3961,13 @@ let Documentation = [NumThreadsDocs]; } +def HLSLSV_GroupIndex: InheritableAttr { + let Spellings = [HLSLSemantic<"SV_GroupIndex">]; + let Subjects = SubjectList<[ParmVar, GlobalVar]>; + let LangOpts = [HLSL]; + let Documentation = [HLSLSV_GroupIndexDocs]; +} + def RandomizeLayout : InheritableAttr { let Spellings = [GCC<"randomize_layout">]; let Subjects = SubjectList<[Record]>; 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 @@ -6411,3 +6411,14 @@ randomized. }]; } + +def HLSLSV_GroupIndexDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``SV_GroupIndex`` semantic, when applied to an input parameter, specifies a +data binding to map the group index to the specified parameter. This attribute +is only supported in compute shaders. + +The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sv-groupindex + }]; +} diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -48,6 +48,9 @@ // without adding related code to TableGen/ClangAttrEmitter.cpp. /// Context-sensitive version of a keyword attribute. AS_ContextSensitiveKeyword, + + /// : + AS_HLSLSemantic, }; enum Kind { #define PARSED_ATTR(NAME) AT_##NAME, diff --git a/clang/include/clang/Basic/Attributes.h b/clang/include/clang/Basic/Attributes.h --- a/clang/include/clang/Basic/Attributes.h +++ b/clang/include/clang/Basic/Attributes.h @@ -28,7 +28,9 @@ // Is the identifier known as a C-style attribute? C, // Is the identifier known as a pragma attribute? - Pragma + Pragma, + // Is the identifier known as a HLSL semantic? + HLSLSemantic, }; /// Return the version number associated with the attribute if we 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 @@ -1594,4 +1594,10 @@ def note_max_tokens_total_override : Note<"total token limit set here">; +// HLSL Parser Diagnostics + +def err_expected_semantic_identifier : Error< + "expected HLSL Semantic identifier">; +def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">; + } // end of Parser diagnostics diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2786,6 +2786,15 @@ Sema::AttributeCompletion Completion = Sema::AttributeCompletion::None, const IdentifierInfo *EnclosingScope = nullptr); + void MaybeParseHLSLSemantics(ParsedAttributes &Attrs, + SourceLocation *EndLoc = nullptr) { + if (getLangOpts().HLSL && Tok.is(tok::colon)) + ParseHLSLSemantics(Attrs, EndLoc); + } + + void ParseHLSLSemantics(ParsedAttributes &Attrs, + SourceLocation *EndLoc = nullptr); + void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) { if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) && Tok.is(tok::l_square)) { diff --git a/clang/lib/Parse/CMakeLists.txt b/clang/lib/Parse/CMakeLists.txt --- a/clang/lib/Parse/CMakeLists.txt +++ b/clang/lib/Parse/CMakeLists.txt @@ -12,6 +12,7 @@ ParseDeclCXX.cpp ParseExpr.cpp ParseExprCXX.cpp + ParseHLSL.cpp ParseInit.cpp ParseObjc.cpp ParseOpenMP.cpp diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6965,6 +6965,7 @@ // Parse GNU attributes, if present. MaybeParseGNUAttributes(ParmDeclarator); + MaybeParseHLSLSemantics(DS.getAttributes()); if (Tok.is(tok::kw_requires)) { // User tried to define a requires clause in a parameter declaration, diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Parse/ParseHLSL.cpp @@ -0,0 +1,42 @@ +//===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the parsing logic for HLSL language features. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/AttributeCommonInfo.h" +#include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" + +using namespace clang; + +void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs, + SourceLocation *EndLoc) { + assert(Tok.is(tok::colon) && "Not a HLSL Semantic"); + ConsumeToken(); + + if (!Tok.is(tok::identifier)) { + 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) { + Diag(Loc, diag::err_unknown_hlsl_semantic) << II; + return; + } + Attrs.addNew(II, Loc, nullptr, SourceLocation(), nullptr, 0, + ParsedAttr::AS_HLSLSemantic); +} 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 @@ -6925,6 +6925,21 @@ return ::new (Context) HLSLNumThreadsAttr(Context, AL, X, Y, Z); } +static void handleHLSLSVGroupIndexAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + using llvm::Triple; + Triple Target = S.Context.getTargetInfo().getTriple(); + if (Target.getEnvironment() != Triple::Compute) { + uint32_t Pipeline = + (uint32_t)S.Context.getTargetInfo().getTriple().getEnvironment() - + (uint32_t)llvm::Triple::Pixel; + S.Diag(AL.getLoc(), diag::err_hlsl_attr_unsupported_in_stage) + << AL << Pipeline << "Compute"; + return; + } + + D->addAttr(::new (S.Context) HLSLSV_GroupIndexAttr(S.Context, 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) @@ -8797,6 +8812,9 @@ case ParsedAttr::AT_HLSLNumThreads: handleHLSLNumThreadsAttr(S, D, AL); break; + case ParsedAttr::AT_HLSLSV_GroupIndex: + handleHLSLSVGroupIndexAttr(S, D, AL); + break; case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); diff --git a/clang/test/ParserHLSL/lit.local.cfg b/clang/test/ParserHLSL/lit.local.cfg new file mode 100644 --- /dev/null +++ b/clang/test/ParserHLSL/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.hlsl'] diff --git a/clang/test/ParserHLSL/semantic_parsing.hlsl b/clang/test/ParserHLSL/semantic_parsing.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/ParserHLSL/semantic_parsing.hlsl @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s -verify + +// expected-error@+1 {{expected HLSL Semantic identifier}} +void Entry(int GI : ) { } + +// expected-error@+1 {{unknown HLSL semantic 'SV_IWantAPony'}} +void Pony(int GI : SV_IWantAPony) { } diff --git a/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-mesh -x hlsl -ast-dump -verify -o - %s + +[numthreads(8,8, 1)] +// expected-error@+1 {{attribute 'SV_GroupIndex' is unsupported in Mesh shaders, requires Compute}} +void CSMain(int GI : SV_GroupIndex) { +// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:6 CSMain 'void (int)' +// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:17 GI 'int' +// CHECK-NEXT: HLSLSV_GroupIndexAttr +} diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1493,6 +1493,9 @@ Spelling += Namespace; Spelling += " "; } + } else if (Variety == "HLSLSemantic") { + Prefix = ":"; + Suffix = ""; } else { llvm_unreachable("Unknown attribute syntax variety!"); } @@ -3300,7 +3303,7 @@ // Separate all of the attributes out into four group: generic, C++11, GNU, // and declspecs. Then generate a big switch statement for each of them. std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); - std::vector Declspec, Microsoft, GNU, Pragma; + std::vector Declspec, Microsoft, GNU, Pragma, HLSLSemantic; std::map> CXX, C2x; // Walk over the list of all attributes, and split them out based on the @@ -3321,6 +3324,8 @@ C2x[SI.nameSpace()].push_back(R); else if (Variety == "Pragma") Pragma.push_back(R); + else if (Variety == "HLSLSemantic") + HLSLSemantic.push_back(R); } } @@ -3338,6 +3343,9 @@ OS << "case AttrSyntax::Pragma:\n"; OS << " return llvm::StringSwitch(Name)\n"; GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma"); + OS << "case AttrSyntax::HLSLSemantic:\n"; + OS << " return llvm::StringSwitch(Name)\n"; + GenerateHasAttrSpellingStringSwitch(HLSLSemantic, OS, "HLSLSemantic"); auto fn = [&OS](const char *Spelling, const char *Variety, const std::map> &List) { OS << "case AttrSyntax::" << Variety << ": {\n"; @@ -4286,7 +4294,7 @@ std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector GNU, Declspec, Microsoft, CXX11, - Keywords, Pragma, C2x; + Keywords, Pragma, C2x, HLSLSemantic; std::set Seen; for (const auto *A : Attrs) { const Record &Attr = *A; @@ -4338,6 +4346,8 @@ Matches = &Keywords; else if (Variety == "Pragma") Matches = &Pragma; + else if (Variety == "HLSLSemantic") + Matches = &HLSLSemantic; assert(Matches && "Unsupported spelling variety found"); @@ -4373,6 +4383,8 @@ StringMatcher("Name", Keywords, OS).Emit(); OS << " } else if (AttributeCommonInfo::AS_Pragma == Syntax) {\n"; StringMatcher("Name", Pragma, OS).Emit(); + OS << " } else if (AttributeCommonInfo::AS_HLSLSemantic == Syntax) {\n"; + StringMatcher("Name", HLSLSemantic, OS).Emit(); OS << " }\n"; OS << " return AttributeCommonInfo::UnknownAttribute;\n" << "}\n"; @@ -4482,7 +4494,7 @@ } } -enum class SpellingKind { +enum class SpellingKind : size_t { GNU, CXX11, C2x, @@ -4490,8 +4502,10 @@ Microsoft, Keyword, Pragma, + HLSLSemantic, + NumSpellingKinds }; -static const size_t NumSpellingKinds = (size_t)SpellingKind::Pragma + 1; +static const size_t NumSpellingKinds = (size_t)SpellingKind::NumSpellingKinds; class SpellingList { std::vector Spellings[NumSpellingKinds]; @@ -4509,7 +4523,8 @@ .Case("Declspec", SpellingKind::Declspec) .Case("Microsoft", SpellingKind::Microsoft) .Case("Keyword", SpellingKind::Keyword) - .Case("Pragma", SpellingKind::Pragma); + .Case("Pragma", SpellingKind::Pragma) + .Case("HLSLSemantic", SpellingKind::HLSLSemantic); std::string Name; if (!Spelling.nameSpace().empty()) { switch (Kind) { @@ -4610,8 +4625,8 @@ // List what spelling syntaxes the attribute supports. OS << ".. csv-table:: Supported Syntaxes\n"; OS << " :header: \"GNU\", \"C++11\", \"C2x\", \"``__declspec``\","; - OS << " \"Keyword\", \"``#pragma``\", \"``#pragma clang attribute``\"\n\n"; - OS << " \""; + OS << " \"Keyword\", \"``#pragma``\", \"``#pragma clang attribute``\","; + OS << " \"HLSL Semantic\"\n\n \""; for (size_t Kind = 0; Kind != NumSpellingKinds; ++Kind) { SpellingKind K = (SpellingKind)Kind; // TODO: List Microsoft (IDL-style attribute) spellings once we fully