diff --git a/docs/DXIL.rst b/docs/DXIL.rst index d70535220..6fd1f54d7 100644 --- a/docs/DXIL.rst +++ b/docs/DXIL.rst @@ -3134,6 +3134,7 @@ INSTR.SAMPLERMODEFORSAMPLEC sample_c_*/gather_c instructions requi INSTR.SIGNATUREOPERATIONNOTINENTRY Dxil operation for input output signature must be in entryPoints. INSTR.STATUS Resource status should only be used by CheckAccessFullyMapped. INSTR.STRUCTBITCAST Bitcast on struct types is not allowed. +INSTR.SVCONFLICTINGLAUNCHMODE Input system values are compatible with node shader launch mode. INSTR.TEXTUREOFFSET offset texture instructions must take offset which can resolve to integer literal in the range -8 to 7. INSTR.TGSMRACECOND Race condition writing to shared memory detected, consider making this write conditional. INSTR.UNDEFINEDVALUEFORUAVSTORE Assignment of undefined values to UAV. diff --git a/lib/HLSL/DxilValidation.cpp b/lib/HLSL/DxilValidation.cpp index b89ff2440..04d546e27 100644 --- a/lib/HLSL/DxilValidation.cpp +++ b/lib/HLSL/DxilValidation.cpp @@ -2134,6 +2134,19 @@ static void ValidateBarrierFlagArg(ValidationContext &ValCtx, CallInst *CI, } } +std::string GetLaunchTypeStr(DXIL::NodeLaunchType LT) { + switch (LT) { + case DXIL::NodeLaunchType::Broadcasting: + return "Broadcasting"; + case DXIL::NodeLaunchType::Coalescing: + return "Coalescing"; + case DXIL::NodeLaunchType::Thread: + return "Thread"; + default: + return "Invalid"; + } +} + static void ValidateDxilOperationCallInProfile(CallInst *CI, DXIL::OpCode opcode, const ShaderModel *pSM, @@ -2141,10 +2154,15 @@ static void ValidateDxilOperationCallInProfile(CallInst *CI, DXIL::ShaderKind shaderKind = pSM ? pSM->GetKind() : DXIL::ShaderKind::Invalid; llvm::Function *F = CI->getParent()->getParent(); + DXIL::NodeLaunchType nodeLaunchType = DXIL::NodeLaunchType::Invalid; if (DXIL::ShaderKind::Library == shaderKind) { - if (ValCtx.DxilMod.HasDxilFunctionProps(F)) + if (ValCtx.DxilMod.HasDxilFunctionProps(F)) { + DxilEntryProps &entryProps = ValCtx.DxilMod.GetDxilEntryProps(F); shaderKind = ValCtx.DxilMod.GetDxilFunctionProps(F).shaderKind; - else if (ValCtx.DxilMod.IsPatchConstantShader(F)) + if (shaderKind == DXIL::ShaderKind::Node) + nodeLaunchType = entryProps.props.Node.LaunchType; + + } else if (ValCtx.DxilMod.IsPatchConstantShader(F)) shaderKind = DXIL::ShaderKind::Hull; } @@ -2346,6 +2364,65 @@ static void ValidateDxilOperationCallInProfile(CallInst *CI, {"CreateHandle", "Shader model 6.5 and below"}); } break; + + case DXIL::OpCode::ThreadId: // SV_DispatchThreadID + if (shaderKind != DXIL::ShaderKind::Node) { + break; + } + + if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting) + break; + + ValCtx.EmitInstrFormatError( + CI, ValidationRule::InstrSVConflictingLaunchMode, + {"ThreadId", "SV_DispatchThreadID", GetLaunchTypeStr(nodeLaunchType)}); + break; + + case DXIL::OpCode::GroupId: // SV_GroupId + if (shaderKind != DXIL::ShaderKind::Node) { + break; + } + + if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting) + break; + + ValCtx.EmitInstrFormatError( + CI, ValidationRule::InstrSVConflictingLaunchMode, + {"GroupId", "SV_GroupId", GetLaunchTypeStr(nodeLaunchType)}); + break; + + case DXIL::OpCode::ThreadIdInGroup: // SV_GroupThreadID + if (shaderKind != DXIL::ShaderKind::Node) { + break; + } + + if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting || + nodeLaunchType == DXIL::NodeLaunchType::Coalescing) + break; + + ValCtx.EmitInstrFormatError(CI, + ValidationRule::InstrSVConflictingLaunchMode, + {"ThreadIdInGroup", "SV_GroupThreadID", + GetLaunchTypeStr(nodeLaunchType)}); + + break; + + case DXIL::OpCode::FlattenedThreadIdInGroup: // SV_GroupIndex + if (shaderKind != DXIL::ShaderKind::Node) { + break; + } + + if (nodeLaunchType == DXIL::NodeLaunchType::Broadcasting || + nodeLaunchType == DXIL::NodeLaunchType::Coalescing) + break; + + ValCtx.EmitInstrFormatError(CI, + ValidationRule::InstrSVConflictingLaunchMode, + {"FlattenedThreadIdInGroup", "SV_GroupIndex", + GetLaunchTypeStr(nodeLaunchType)}); + + break; + default: // TODO: make sure every opcode is checked. // Skip opcodes don't need special check. @@ -3520,7 +3597,6 @@ static void ValidateFunction(Function &F, ValidationContext &ValCtx) { {std::to_string(arg.getArgNo()), F.getName()}); }; - // Validate parameter type. unsigned numArgs = 0; for (auto &arg : F.args()) { Type *argTy = arg.getType(); diff --git a/tools/clang/test/HLSLFileCheck/dxil/intrinsics/launch_types_coalescing.ll b/tools/clang/test/HLSLFileCheck/dxil/intrinsics/launch_types_coalescing.ll new file mode 100644 index 000000000..6b99f2d58 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/dxil/intrinsics/launch_types_coalescing.ll @@ -0,0 +1,51 @@ +; RUN: %dxv %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +; CHECK: Function: entry3: error: Call to DXIL intrinsic ThreadId (SV_DispatchThreadID) is not allowed in node shader launch type Coalescing +; CHECK: note: at '%1 = call i32 @dx.op.threadId.i32(i32 93, i32 1)' in block '#0' of function 'entry3'. +; CHECK: Function: entry3: error: Call to DXIL intrinsic GroupId (SV_GroupId) is not allowed in node shader launch type Coalescing +; CHECK: note: at '%2 = call i32 @dx.op.groupId.i32(i32 94, i32 2)' in block '#0' of function 'entry3'. + +define void @entry3() { + %1 = call i32 @dx.op.threadId.i32(i32 93, i32 1) + %2 = call i32 @dx.op.groupId.i32(i32 94, i32 2) + %3 = call i32 @dx.op.threadIdInGroup.i32(i32 95, i32 1) + %4 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96) + ret void +} + +!llvm.ident = !{!0} +!dx.version = !{!1} +!dx.valver = !{!2} +!dx.shaderModel = !{!3} +!dx.typeAnnotations = !{!4} +!dx.entryPoints = !{!8, !9} + +!0 = !{!"dxc(private) 1.7.0.14317 (main, e3c311409675)"} +!1 = !{i32 1, i32 3} +!2 = !{i32 1, i32 8} +!3 = !{!"lib", i32 6, i32 3} +!4 = !{i32 1, void ()* @entry3, !5} +!5 = !{!6} +!6 = !{i32 0, !7, !7} +!7 = !{} +!8 = !{null, !"", null, null, null} +!9 = !{void ()* @entry3, !"entry3", null, null, !10} +!10 = !{i32 8, i32 15, i32 13, i32 2, i32 15, !11, i32 16, i32 -1, i32 4, !12, i32 5, !13} +!11 = !{!"entry3", i32 0} +!12 = !{i32 1, i32 1, i32 1} +!13 = !{i32 0} + +; Function Attrs: nounwind readnone +declare i32 @dx.op.threadId.i32(i32, i32) #1 + +; Function Attrs: nounwind readnone +declare i32 @dx.op.groupId.i32(i32, i32) #1 + +; Function Attrs: nounwind readnone +declare i32 @dx.op.threadIdInGroup.i32(i32, i32) #0 + +; Function Attrs: nounwind readnone +declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32) #0 diff --git a/tools/clang/test/HLSLFileCheck/dxil/intrinsics/launch_types_thread.ll b/tools/clang/test/HLSLFileCheck/dxil/intrinsics/launch_types_thread.ll new file mode 100644 index 000000000..50986a6bb --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/dxil/intrinsics/launch_types_thread.ll @@ -0,0 +1,55 @@ +; RUN: %dxv %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +; CHECK: Function: entry: error: Call to DXIL intrinsic ThreadId (SV_DispatchThreadID) is not allowed in node shader launch type Thread +; CHECK: note: at '%1 = call i32 @dx.op.threadId.i32(i32 93, i32 1)' in block '#0' of function 'entry'. +; CHECK: Function: entry: error: Call to DXIL intrinsic GroupId (SV_GroupId) is not allowed in node shader launch type Thread +; CHECK: note: at '%2 = call i32 @dx.op.groupId.i32(i32 94, i32 2)' in block '#0' of function 'entry'. +; CHECK: Function: entry: error: Call to DXIL intrinsic ThreadIdInGroup (SV_GroupThreadID) is not allowed in node shader launch type Thread +; CHECK: note: at '%3 = call i32 @dx.op.threadIdInGroup.i32(i32 95, i32 1)' in block '#0' of function 'entry'. +; CHECK: Function: entry: error: Call to DXIL intrinsic FlattenedThreadIdInGroup (SV_GroupIndex) is not allowed in node shader launch type Thread +; CHECK: note: at '%4 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96)' in block '#0' of function 'entry'. + +define void @entry() { + %1 = call i32 @dx.op.threadId.i32(i32 93, i32 1) + %2 = call i32 @dx.op.groupId.i32(i32 94, i32 2) + %3 = call i32 @dx.op.threadIdInGroup.i32(i32 95, i32 1) + %4 = call i32 @dx.op.flattenedThreadIdInGroup.i32(i32 96) + ret void +} + +!llvm.ident = !{!0} +!dx.version = !{!1} +!dx.valver = !{!2} +!dx.shaderModel = !{!3} +!dx.typeAnnotations = !{!4} +!dx.entryPoints = !{!8, !9} + +!0 = !{!"dxc(private) 1.7.0.14317 (main, e3c311409675)"} +!1 = !{i32 1, i32 3} +!2 = !{i32 1, i32 8} +!3 = !{!"lib", i32 6, i32 3} +!4 = !{i32 1, void ()* @entry, !5} +!5 = !{!6} +!6 = !{i32 0, !7, !7} +!7 = !{} +!8 = !{null, !"", null, null, null} +!9 = !{void ()* @entry, !"entry", null, null, !10} +!10 = !{i32 8, i32 15, i32 13, i32 3, i32 15, !11, i32 16, i32 -1, i32 4, !12, i32 5, !13} +!11 = !{!"entry", i32 0} +!12 = !{i32 1, i32 1, i32 1} +!13 = !{i32 0} + +; Function Attrs: nounwind readnone +declare i32 @dx.op.threadId.i32(i32, i32) #1 + +; Function Attrs: nounwind readnone +declare i32 @dx.op.groupId.i32(i32, i32) #1 + +; Function Attrs: nounwind readnone +declare i32 @dx.op.threadIdInGroup.i32(i32, i32) #0 + +; Function Attrs: nounwind readnone +declare i32 @dx.op.flattenedThreadIdInGroup.i32(i32) #0 diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 6a4cce3d9..f233b43d0 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -7559,6 +7559,11 @@ class db_dxil(object): self.add_valrule( "Instr.AtomicIntrinNonUAV", "Non-UAV destination to atomic intrinsic." ) + self.add_valrule_msg( + "Instr.SVConflictingLaunchMode", + "Input system values are compatible with node shader launch mode.", + "Call to DXIL intrinsic %0 (%1) is not allowed in node shader launch type %2" + ) self.add_valrule("Instr.AtomicConst", "Constant destination to atomic.") # Work-Graphs