[HLSL] Fix MSFT Attribute parsing, add numthreads

HLSL uses Microsoft-style attributes `[attr]`, which clang mostly
ignores. For HLSL we need to handle known Microsoft attributes, and to
maintain C/C++ as-is we ignore unknown attributes.

To utilize this new code path, this change adds the HLSL `numthreads`
attribute.

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D122627
This commit is contained in:
Chris Bieneman 2022-03-29 15:47:49 -05:00
parent fe8b2236ef
commit 94189b42cc
9 changed files with 170 additions and 3 deletions

View File

@ -336,6 +336,8 @@ def ObjCAutoRefCount : LangOpt<"ObjCAutoRefCount">;
def ObjCNonFragileRuntime
: LangOpt<"", "LangOpts.ObjCRuntime.allowsClassStubs()">;
def HLSL : LangOpt<"HLSL">;
// Language option for CMSE extensions
def Cmse : LangOpt<"Cmse">;
@ -3937,3 +3939,11 @@ def Error : InheritableAttr {
let Subjects = SubjectList<[Function], ErrorDiag>;
let Documentation = [ErrorAttrDocs];
}
def HLSLNumThreads: InheritableAttr {
let Spellings = [Microsoft<"numthreads">];
let Args = [IntArgument<"X">, IntArgument<"Y">, IntArgument<"Z">];
let Subjects = SubjectList<[Function]>;
let LangOpts = [HLSL];
let Documentation = [NumThreadsDocs];
}

View File

@ -6368,3 +6368,14 @@ flag.
.. _Return-Oriented Programming: https://en.wikipedia.org/wiki/Return-oriented_programming
}];
}
def NumThreadsDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
The ``numthreads`` attribute applies to HLSL shaders where explcit thread counts
are required. The ``X``, ``Y``, and ``Z`` values provided to the attribute
dictate the thread id. Total number of threads executed is ``X * Y * Z``.
The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-numthreads
}];
}

View File

@ -11564,4 +11564,12 @@ def err_std_source_location_impl_not_found : Error<
"'std::source_location::__impl' was not found; it must be defined before '__builtin_source_location' is called">;
def err_std_source_location_impl_malformed : Error<
"'std::source_location::__impl' must be standard-layout and have only two 'const char *' fields '_M_file_name' and '_M_function_name', and two integral fields '_M_line' and '_M_column'">;
// HLSL Diagnostics
def err_hlsl_attr_unsupported_in_stage : Error<"attribute %0 is unsupported in %select{Pixel|Vertex|Geometry|Hull|Domain|Compute|Library|RayGeneration|Intersection|AnyHit|ClosestHit|Miss|Callable|Mesh|Amplification|Invalid}1 shaders, requires %2">;
def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">;
def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">;
} // end of sema component.

View File

@ -2783,7 +2783,8 @@ private:
const IdentifierInfo *EnclosingScope = nullptr);
void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
if (getLangOpts().MicrosoftExt && Tok.is(tok::l_square)) {
if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&
Tok.is(tok::l_square)) {
ParsedAttributes AttrsWithRange(AttrFactory);
ParseMicrosoftAttributes(AttrsWithRange);
Attrs.takeAllFrom(AttrsWithRange);

View File

@ -4302,10 +4302,19 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName,
ParsedAttr::Syntax Syntax =
LO.CPlusPlus ? ParsedAttr::AS_CXX11 : ParsedAttr::AS_C2x;
// Try parsing microsoft attributes
if (getLangOpts().MicrosoftExt || getLangOpts().HLSL) {
if (hasAttribute(AttrSyntax::Microsoft, ScopeName, AttrName,
getTargetInfo(), getLangOpts()))
Syntax = ParsedAttr::AS_Microsoft;
}
// If the attribute isn't known, we will not attempt to parse any
// arguments.
if (!hasAttribute(LO.CPlusPlus ? AttrSyntax::CXX : AttrSyntax::C, ScopeName,
if (Syntax != ParsedAttr::AS_Microsoft &&
!hasAttribute(LO.CPlusPlus ? AttrSyntax::CXX : AttrSyntax::C, ScopeName,
AttrName, getTargetInfo(), getLangOpts())) {
if (getLangOpts().MicrosoftExt || getLangOpts().HLSL) {}
// Eat the left paren, then skip to the ending right paren.
ConsumeParen();
SkipUntil(tok::r_paren);
@ -4688,8 +4697,17 @@ void Parser::ParseMicrosoftAttributes(ParsedAttributes &Attrs) {
break;
if (Tok.getIdentifierInfo()->getName() == "uuid")
ParseMicrosoftUuidAttributeArgs(Attrs);
else
else {
IdentifierInfo *II = Tok.getIdentifierInfo();
SourceLocation NameLoc = Tok.getLocation();
ConsumeToken();
if (Tok.is(tok::l_paren)) {
CachedTokens OpenMPTokens;
ParseCXX11AttributeArgs(II, NameLoc, Attrs, &EndLoc, nullptr,
SourceLocation(), OpenMPTokens);
ReplayOpenMPAttributeTokens(OpenMPTokens);
} // FIXME: handle attributes that don't have arguments
}
}
T.consumeClose();

View File

@ -11323,6 +11323,11 @@ void Sema::CheckMain(FunctionDecl* FD, const DeclSpec& DS) {
return;
}
// Functions named main in hlsl are default entries, but don't have specific
// signatures they are required to conform to.
if (getLangOpts().HLSL)
return;
QualType T = FD->getType();
assert(T->isFunctionType() && "function decl is not of function type");
const FunctionType* FT = T->castAs<FunctionType>();

View File

@ -24,6 +24,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/DarwinSDKInfo.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetBuiltins.h"
@ -6836,6 +6837,64 @@ static void handleUuidAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(UA);
}
static void handleHLSLNumThreadsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
using llvm::Triple;
Triple Target = S.Context.getTargetInfo().getTriple();
if (!llvm::is_contained({Triple::Compute, Triple::Mesh, Triple::Amplification,
Triple::Library},
Target.getEnvironment())) {
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, Amplification, Mesh or Library";
return;
}
llvm::VersionTuple SMVersion = Target.getOSVersion();
uint32_t ZMax = 1024;
uint32_t ThreadMax = 1024;
if (SMVersion.getMajor() <= 4) {
ZMax = 1;
ThreadMax = 768;
} else if (SMVersion.getMajor() == 5) {
ZMax = 64;
ThreadMax = 1024;
}
uint32_t X;
if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), X))
return;
if (X > 1024) {
S.Diag(AL.getArgAsExpr(0)->getExprLoc(),
diag::err_hlsl_numthreads_argument_oor) << 0 << 1024;
return;
}
uint32_t Y;
if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(1), Y))
return;
if (Y > 1024) {
S.Diag(AL.getArgAsExpr(1)->getExprLoc(),
diag::err_hlsl_numthreads_argument_oor) << 1 << 1024;
return;
}
uint32_t Z;
if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(2), Z))
return;
if (Z > ZMax) {
S.Diag(AL.getArgAsExpr(2)->getExprLoc(),
diag::err_hlsl_numthreads_argument_oor) << 2 << ZMax;
return;
}
if (X * Y * Z > ThreadMax) {
S.Diag(AL.getLoc(), diag::err_hlsl_numthreads_invalid) << ThreadMax;
return;
}
D->addAttr(::new (S.Context) HLSLNumThreadsAttr(S.Context, AL, X, Y, Z));
}
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)
@ -8697,6 +8756,11 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_Thread:
handleDeclspecThreadAttr(S, D, AL);
break;
// HLSL attributes:
case ParsedAttr::AT_HLSLNumThreads:
handleHLSLNumThreadsAttr(S, D, AL);
break;
case ParsedAttr::AT_AbiTag:
handleAbiTagAttr(S, D, AL);

View File

@ -0,0 +1 @@
config.suffixes = ['.hlsl']

View File

@ -0,0 +1,49 @@
// 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 -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-amplification -x hlsl -ast-dump -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-pixel -x hlsl -ast-dump -o - %s -verify
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-vertex -x hlsl -ast-dump -o - %s -verify
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-hull -x hlsl -ast-dump -o - %s -verify
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-domain -x hlsl -ast-dump -o - %s -verify
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -o - %s -DFAIL -verify
// RUN: %clang_cc1 -triple dxil-pc-shadermodel5.0-compute -x hlsl -ast-dump -o - %s -DFAIL -verify
// RUN: %clang_cc1 -triple dxil-pc-shadermodel4.0-compute -x hlsl -ast-dump -o - %s -DFAIL -verify
#if __SHADER_TARGET_STAGE == __SHADER_STAGE_COMPUTE || __SHADER_TARGET_STAGE == __SHADER_STAGE_MESH || __SHADER_TARGET_STAGE == __SHADER_STAGE_AMPLIFICATION || __SHADER_TARGET_STAGE == __SHADER_STAGE_LIBRARY
#ifdef FAIL
#if __SHADER_TARGET_MAJOR == 6
// expected-error@+1 {{'numthreads' attribute requires an integer constant}}
[numthreads("1",2,3)]
// expected-error@+1 {{argument 'X' to numthreads attribute cannot exceed 1024}}
[numthreads(-1,2,3)]
// expected-error@+1 {{argument 'Y' to numthreads attribute cannot exceed 1024}}
[numthreads(1,-2,3)]
// expected-error@+1 {{argument 'Z' to numthreads attribute cannot exceed 1024}}
[numthreads(1,2,-3)]
// expected-error@+1 {{total number of threads cannot exceed 1024}}
[numthreads(1024,1024,1024)]
#elif __SHADER_TARGET_MAJOR == 5
// expected-error@+1 {{argument 'Z' to numthreads attribute cannot exceed 64}}
[numthreads(1,2,68)]
#else
// expected-error@+1 {{argument 'Z' to numthreads attribute cannot exceed 1}}
[numthreads(1,2,2)]
// expected-error@+1 {{total number of threads cannot exceed 768}}
[numthreads(1024,1,1)]
#endif
#endif
// CHECK: HLSLNumThreadsAttr 0x{{[0-9a-fA-F]+}} <line:{{[0-9]+}}:2, col:18> 1 2 1
[numthreads(1,2,1)]
int entry() {
return 1;
}
#else
// expected-error-re@+1 {{attribute 'numthreads' is unsupported in {{[A-Za-z]+}} shaders, requires Compute, Amplification, Mesh or Library}}
[numthreads(1,1,1)]
int main() {
return 1;
}
#endif