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 @@ -4004,6 +4004,13 @@ let Documentation = [HLSLResourceBindingDocs]; } +def HLSLSV_DispatchThreadID: HLSLAnnotationAttr { + let Spellings = [HLSLSemantic<"SV_DispatchThreadID">]; + let Subjects = SubjectList<[ParmVar, Field]>; + let LangOpts = [HLSL]; + let Documentation = [HLSLSV_DispatchThreadIDDocs]; +} + 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 @@ -6618,6 +6618,21 @@ }]; } +def HLSLSV_DispatchThreadIDDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``SV_DispatchThreadID`` semantic, when applied to an input parameter, +specifies a data binding to map the global thread offset within the Dispatch +call (per dimension of the group) to the specified parameter. +When applied to a field of a struct, the data binding is specified to the field +when the struct is used as a parameter type. +The semantic on the field is ignored when not used as a 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-dispatchthreadid + }]; +} + def AnnotateTypeDocs : Documentation { let Category = DocCatType; let Heading = "annotate_type"; 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 @@ -11685,7 +11685,10 @@ // 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_attr_invalid_type : Error< + "attribute %0 only applies to a field or parameter of type '%1'">; +def err_hlsl_attr_invalid_ast_node : Error< + "attribute %0 only applies to %1">; 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">; def err_hlsl_missing_numthreads : Error<"missing numthreads attribute for %0 shader entry">; diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -39,7 +39,8 @@ uint32_t ResourceCounters[static_cast( hlsl::ResourceClass::NumClasses)] = {0}; - llvm::Value *emitInputSemantic(llvm::IRBuilder<> &B, const ParmVarDecl &D); + llvm::Value *emitInputSemantic(llvm::IRBuilder<> &B, const ParmVarDecl &D, + llvm::Type *Ty); public: CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {} diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -117,14 +117,31 @@ } } +static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) { + if (const auto *VT = dyn_cast(Ty)) { + Value *Result = PoisonValue::get(Ty); + for (unsigned I = 0; I < VT->getNumElements(); ++I) { + Value *Elt = B.CreateCall(F, {B.getInt32(I)}); + Result = B.CreateInsertElement(Result, Elt, I); + } + return Result; + } + return B.CreateCall(F, {B.getInt32(0)}); +} + llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B, - const ParmVarDecl &D) { + const ParmVarDecl &D, + llvm::Type *Ty) { assert(D.hasAttrs() && "Entry parameter missing annotation attribute!"); if (D.hasAttr()) { llvm::Function *DxGroupIndex = CGM.getIntrinsic(Intrinsic::dx_flattened_thread_id_in_group); return B.CreateCall(FunctionCallee(DxGroupIndex)); } + if (D.hasAttr()) { + llvm::Function *DxThreadID = CGM.getIntrinsic(Intrinsic::dx_thread_id); + return buildVectorInput(B, DxThreadID, Ty); + } assert(false && "Unhandled parameter attribute"); return nullptr; } @@ -152,8 +169,17 @@ llvm::SmallVector Args; // FIXME: support struct parameters where semantics are on members. // See: https://github.com/llvm/llvm-project/issues/57874 - for (const auto *Param : FD->parameters()) { - Args.push_back(emitInputSemantic(B, *Param)); + unsigned SRetOffset = 0; + for (const auto &Param : Fn->args()) { + if (Param.hasStructRetAttr()) { + // FIXME: support output. + // See: https://github.com/llvm/llvm-project/issues/57874 + SRetOffset = 1; + Args.emplace_back(PoisonValue::get(Param.getType())); + continue; + } + const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset); + Args.push_back(emitInputSemantic(B, *PD, Param.getType())); } CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args); 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 @@ -186,6 +186,7 @@ Diag(Loc, diag::err_unknown_hlsl_semantic) << II; return; case ParsedAttr::AT_HLSLSV_GroupIndex: + case ParsedAttr::AT_HLSLSV_DispatchThreadID: break; default: llvm_unreachable("invalid HLSL Semantic"); 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 @@ -6907,6 +6907,51 @@ D->addAttr(::new (S.Context) HLSLSV_GroupIndexAttr(S.Context, AL)); } +static bool isLegalTypeForHLSLSV_DispatchThreadID(QualType T) { + if (!T->hasUnsignedIntegerRepresentation()) + return false; + if (const auto *VT = T->getAs()) + return VT->getNumElements() <= 3; + return true; +} + +static void handleHLSLSV_DispatchThreadIDAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + using llvm::Triple; + Triple Target = S.Context.getTargetInfo().getTriple(); + // FIXME: it is OK for a compute shader entry and pixel shader entry live in + // same HLSL file.Issue https://github.com/llvm/llvm-project/issues/57880. + if (Target.getEnvironment() != Triple::Compute && + Target.getEnvironment() != Triple::Library) { + 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; + } + + // FIXME: report warning and ignore semantic when cannot apply on the Decl. + // See https://github.com/llvm/llvm-project/issues/57916. + + // FIXME: support semantic on field. + // See https://github.com/llvm/llvm-project/issues/57889. + if (isa(D)) { + S.Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_ast_node) + << AL << "parameter"; + return; + } + + auto *VD = cast(D); + if (!isLegalTypeForHLSLSV_DispatchThreadID(VD->getType())) { + S.Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) + << AL << "uint/uint2/uint3"; + return; + } + + D->addAttr(::new (S.Context) HLSLSV_DispatchThreadIDAttr(S.Context, AL)); +} + static void handleHLSLShaderAttr(Sema &S, Decl *D, const ParsedAttr &AL) { StringRef Str; SourceLocation ArgLoc; @@ -8992,6 +9037,9 @@ case ParsedAttr::AT_HLSLSV_GroupIndex: handleHLSLSVGroupIndexAttr(S, D, AL); break; + case ParsedAttr::AT_HLSLSV_DispatchThreadID: + handleHLSLSV_DispatchThreadIDAttr(S, D, AL); + break; case ParsedAttr::AT_HLSLShader: handleHLSLShaderAttr(S, D, AL); break; diff --git a/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl b/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s + +// Make sure SV_DispatchThreadID translated into dx.thread.id. + +const RWBuffer In; +RWBuffer Out; + +// CHECK: define void @foo() +// CHECK: %[[ID:[0-9a-zA-Z]+]] = call i32 @llvm.dx.thread.id(i32 0) +// CHECK: call void @"?foo@@YAXH@Z"(i32 %[[ID]]) +[shader("compute")] +[numthreads(8,8,1)] +void foo(uint Idx : SV_DispatchThreadID) { + Out[Idx] = In[Idx]; +} + +// CHECK: define void @bar() +// CHECK: %[[ID_X:[0-9a-zA-Z]+]] = call i32 @llvm.dx.thread.id(i32 0) +// CHECK: %[[ID_X_:[0-9a-zA-Z]+]] = insertelement <2 x i32> poison, i32 %[[ID_X]], i64 0 +// CHECK: %[[ID_Y:[0-9a-zA-Z]+]] = call i32 @llvm.dx.thread.id(i32 1) +// CHECK: %[[ID_XY:[0-9a-zA-Z]+]] = insertelement <2 x i32> %[[ID_X_]], i32 %[[ID_Y]], i64 1 +// CHECK: call void @"?bar@@YAXT?$__vector@H$01@__clang@@@Z"(<2 x i32> %[[ID_XY]]) +[shader("compute")] +[numthreads(8,8,1)] +void bar(uint2 Idx : SV_DispatchThreadID) { + Out[Idx.y] = In[Idx.x]; +} + diff --git a/clang/test/CodeGenHLSL/sret_output.hlsl b/clang/test/CodeGenHLSL/sret_output.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenHLSL/sret_output.hlsl @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \ +// RUN: dxil-pc-shadermodel6.3-library %s \ +// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s + +// FIXME: add semantic to a. +// See https://github.com/llvm/llvm-project/issues/57874 +struct S { + float a; +}; + + +// Make sure sret parameter is generated. +// CHECK:define internal void @"?ps_main@@YA?AUS@@XZ"(ptr noalias sret(%struct.S) align 4 %agg.result) +// FIXME: change it to real value instead of poison value once semantic is add to a. +// Make sure the function with sret is called. +// CHECK:call void @"?ps_main@@YA?AUS@@XZ"(ptr poison) +[shader("pixel")] +S ps_main() { + S s; + s.a = 0; + return s; +}; diff --git a/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl --- a/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl +++ b/clang/test/SemaHLSL/Semantics/entry_parameter.hlsl @@ -1,10 +1,13 @@ -// 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 +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -finclude-default-header -ast-dump -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-mesh -x hlsl -ast-dump -finclude-default-header -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)' +[numthreads(8,8,1)] +// expected-error@+2 {{attribute 'SV_GroupIndex' is unsupported in Mesh shaders, requires Compute}} +// expected-error@+1 {{attribute 'SV_DispatchThreadID' is unsupported in Mesh shaders, requires Compute}} +void CSMain(int GI : SV_GroupIndex, uint ID : SV_DispatchThreadID) { +// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:6 CSMain 'void (int, uint)' // CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:17 GI 'int' // CHECK-NEXT: HLSLSV_GroupIndexAttr +// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:42 ID 'uint' +// CHECK-NEXT: HLSLSV_DispatchThreadIDAttr } diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -ast-dump -verify -o - %s + +[numthreads(8,8,1)] +// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +void CSMain(float ID : SV_DispatchThreadID) { + +} + +struct ST { + int a; + float b; +}; +[numthreads(8,8,1)] +// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +void CSMain2(ST ID : SV_DispatchThreadID) { + +} + +void foo() { +// expected-warning@+1 {{'SV_DispatchThreadID' attribute only applies to parameters and non-static data members}} + uint V : SV_DispatchThreadID; + +} + +struct ST2 { +// expected-error@+1 {{use of undeclared identifier 'SV_DispatchThreadID'}} + static uint X : SV_DispatchThreadID; +// expected-error@+1 {{use of undeclared identifier 'SV_DispatchThreadID'}} + uint s : SV_DispatchThreadID; +}; diff --git a/clang/test/SemaHLSL/Semantics/valid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/valid_entry_parameter.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/valid_entry_parameter.hlsl @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -finclude-default-header -ast-dump -o - %s | FileCheck %s + +[numthreads(8,8,1)] +void CSMain(uint ID : SV_DispatchThreadID) { +// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:6 CSMain 'void (uint)' +// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:18 ID 'uint' +// CHECK-NEXT: HLSLSV_DispatchThreadIDAttr +} +[numthreads(8,8,1)] +void CSMain1(uint2 ID : SV_DispatchThreadID) { +// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:6 CSMain1 'void (uint2)' +// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:20 ID 'uint2' +// CHECK-NEXT: HLSLSV_DispatchThreadIDAttr +} +[numthreads(8,8,1)] +void CSMain2(uint3 ID : SV_DispatchThreadID) { +// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:6 CSMain2 'void (uint3)' +// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:20 ID 'uint3' +// CHECK-NEXT: HLSLSV_DispatchThreadIDAttr +} +[numthreads(8,8,1)] +void CSMain3(uint3 : SV_DispatchThreadID) { +// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:6 CSMain3 'void (uint3)' +// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:20 'uint3' +// CHECK-NEXT: HLSLSV_DispatchThreadIDAttr +}