diff --git a/docs/DXIL.rst b/docs/DXIL.rst index a5f0536c4e..8871d3d8c9 100644 --- a/docs/DXIL.rst +++ b/docs/DXIL.rst @@ -3098,6 +3098,8 @@ ID Name Description 2147483678 LinAlgMatrixReserved0 reserved 2147483679 LinAlgMatrixReserved1 reserved 2147483680 LinAlgMatrixReserved2 reserved +2147483681 DebugBreak triggers a breakpoint if a debugger is attached +2147483682 IsDebuggerPresent returns true if a debugger is attached ========== ======================================== =================================================================================================================== diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index c14b4ce258..056e37312f 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -35,6 +35,10 @@ The included licenses apply to the following files: - GetGroupWaveCount: New intrinsic for Compute, Mesh, Amplification and Node shaders which returns the total number of waves executing within the thread group. +- Added `DebugBreak()` and `dx::IsDebuggerPresent()` intrinsics for shader debugging (experimental Shader Model 6.10). + - `DebugBreak()` triggers a breakpoint if a debugger is attached. + - `dx::IsDebuggerPresent()` returns true if a debugger is attached. + - SPIR-V: `DebugBreak()` emits `NonSemantic.DebugBreak` extended instruction; `IsDebuggerPresent()` is not supported. #### Noteble SPIR-V updates @@ -80,7 +84,6 @@ The included licenses apply to the following files: - Several small bug fixes. #### Other Changes - - Fixed regression: [#7510](https://github.com/microsoft/DirectXShaderCompiler/issues/7510) crash when calling `sizeof` on templated type. - Fixed regression: [#7508](https://github.com/microsoft/DirectXShaderCompiler/issues/7508) crash when calling `Load` with `status`. - Header file `dxcpix.h` was added to the release package. diff --git a/include/dxc/DXIL/DxilConstants.h b/include/dxc/DXIL/DxilConstants.h index 8d44e58487..eb5f6ac1dd 100644 --- a/include/dxc/DXIL/DxilConstants.h +++ b/include/dxc/DXIL/DxilConstants.h @@ -516,6 +516,10 @@ enum class OpCode : unsigned { LinAlgMatrixReserved1 = 31, // reserved LinAlgMatrixReserved2 = 32, // reserved + // Debugging + DebugBreak = 33, // triggers a breakpoint if a debugger is attached + IsDebuggerPresent = 34, // returns true if a debugger is attached + // Group Wave Ops GetGroupWaveCount = 2, // returns the number of waves in the thread group GetGroupWaveIndex = 1, // returns the index of the wave in the thread group @@ -580,7 +584,7 @@ enum class OpCode : unsigned { HitObject_TriangleObjectPosition = 10, // returns triangle vertices in object space as <9 x float> - NumOpCodes = 33, // exclusive last value of enumeration + NumOpCodes = 35, // exclusive last value of enumeration }; } // namespace ExperimentalOps static const unsigned NumOpCodeTables = 2; @@ -1324,6 +1328,12 @@ enum class OpCode : unsigned { EXP_OPCODE(ExperimentalOps, LinAlgMatrixReserved1), // reserved // LinAlgMatrixReserved2 = 0x80000020, 2147483680U, -2147483616 EXP_OPCODE(ExperimentalOps, LinAlgMatrixReserved2), // reserved + // DebugBreak = 0x80000021, 2147483681U, -2147483615 + EXP_OPCODE(ExperimentalOps, + DebugBreak), // triggers a breakpoint if a debugger is attached + // IsDebuggerPresent = 0x80000022, 2147483682U, -2147483614 + EXP_OPCODE(ExperimentalOps, + IsDebuggerPresent), // returns true if a debugger is attached }; // OPCODE-ENUM:END #undef EXP_OPCODE @@ -1386,6 +1396,10 @@ enum class OpCodeClass : unsigned { IndexNodeHandle, createNodeOutputHandle, + // Debugging + DebugBreak, + IsDebuggerPresent, + // Derivatives CalculateLOD, Unary, @@ -1688,7 +1702,7 @@ enum class OpCodeClass : unsigned { NodeOutputIsValid, OutputComplete, - NumOpClasses = 223, // exclusive last value of enumeration + NumOpClasses = 225, // exclusive last value of enumeration }; // OPCODECLASS-ENUM:END diff --git a/include/dxc/DXIL/DxilInstructions.h b/include/dxc/DXIL/DxilInstructions.h index 4646a0b872..a4dab455bf 100644 --- a/include/dxc/DXIL/DxilInstructions.h +++ b/include/dxc/DXIL/DxilInstructions.h @@ -11137,5 +11137,44 @@ struct DxilInst_MatrixOuterProduct { llvm::Value *get_vectorB() const { return Instr->getOperand(3); } void set_vectorB(llvm::Value *val) { Instr->setOperand(3, val); } }; + +/// This instruction triggers a breakpoint if a debugger is attached +struct DxilInst_DebugBreak { + llvm::Instruction *Instr; + // Construction and identification + DxilInst_DebugBreak(llvm::Instruction *pInstr) : Instr(pInstr) {} + operator bool() const { + return hlsl::OP::IsDxilOpFuncCallInst(Instr, hlsl::OP::OpCode::DebugBreak); + } + // Validation support + bool isAllowed() const { return true; } + bool isArgumentListValid() const { + if (1 != llvm::dyn_cast(Instr)->getNumArgOperands()) + return false; + return true; + } + // Metadata + bool requiresUniformInputs() const { return false; } +}; + +/// This instruction returns true if a debugger is attached +struct DxilInst_IsDebuggerPresent { + llvm::Instruction *Instr; + // Construction and identification + DxilInst_IsDebuggerPresent(llvm::Instruction *pInstr) : Instr(pInstr) {} + operator bool() const { + return hlsl::OP::IsDxilOpFuncCallInst(Instr, + hlsl::OP::OpCode::IsDebuggerPresent); + } + // Validation support + bool isAllowed() const { return true; } + bool isArgumentListValid() const { + if (1 != llvm::dyn_cast(Instr)->getNumArgOperands()) + return false; + return true; + } + // Metadata + bool requiresUniformInputs() const { return false; } +}; // INSTR-HELPER:END } // namespace hlsl diff --git a/include/dxc/HlslIntrinsicOp.h b/include/dxc/HlslIntrinsicOp.h index 3eb605fe84..1ebb2df35b 100644 --- a/include/dxc/HlslIntrinsicOp.h +++ b/include/dxc/HlslIntrinsicOp.h @@ -16,6 +16,7 @@ enum class IntrinsicOp { IOP_ClusterID = 397, IOP_CreateResourceFromHeap = 8, IOP_D3DCOLORtoUBYTE4 = 9, + IOP_DebugBreak = 425, IOP_DeviceMemoryBarrier = 10, IOP_DeviceMemoryBarrierWithGroupSync = 11, IOP_DispatchMesh = 12, @@ -399,6 +400,7 @@ enum class IntrinsicOp { MOP_DxHitObject_SetShaderTableIndex = 388, MOP_DxHitObject_TraceRay = 389, MOP_DxHitObject_TriangleObjectPositions = 404, + IOP_DxIsDebuggerPresent = 426, IOP_DxMaybeReorderThread = 359, MOP_Count = 328, MOP_FinishedCrossGroupSharing = 329, @@ -431,7 +433,7 @@ enum class IntrinsicOp { IOP_usign = 355, MOP_InterlockedUMax = 356, MOP_InterlockedUMin = 357, - Num_Intrinsics = 425, + Num_Intrinsics = 427, }; inline bool HasUnsignedIntrinsicOpcode(IntrinsicOp opcode) { switch (opcode) { diff --git a/lib/DXIL/DxilOperations.cpp b/lib/DXIL/DxilOperations.cpp index deb000e703..eb5b2a2ceb 100644 --- a/lib/DXIL/DxilOperations.cpp +++ b/lib/DXIL/DxilOperations.cpp @@ -3001,6 +3001,24 @@ static const OP::OpCodeProperty ExperimentalOps_OpCodeProps[] = { 0, {}, {}}, // Overloads: v + + // Debugging + {OC::DebugBreak, + "DebugBreak", + OCC::DebugBreak, + "debugBreak", + Attribute::NoDuplicate, + 0, + {}, + {}}, // Overloads: v + {OC::IsDebuggerPresent, + "IsDebuggerPresent", + OCC::IsDebuggerPresent, + "isDebuggerPresent", + Attribute::ReadOnly, + 0, + {}, + {}}, // Overloads: v }; static_assert(_countof(ExperimentalOps_OpCodeProps) == (size_t)DXIL::ExperimentalOps::OpCode::NumOpCodes, @@ -3919,12 +3937,14 @@ void OP::GetMinShaderModelAndMask(OpCode C, bool bWithTranslation, // CreateMatrix=2147483659, MatrixLoadFromDescriptor=2147483662, // MatrixQueryAccumulatorLayout=2147483670, MatrixVecMul=2147483673, // MatrixVecMulAdd=2147483674, MatrixAccumulateToDescriptor=2147483675, - // MatrixOuterProduct=2147483677 + // MatrixOuterProduct=2147483677, DebugBreak=2147483681, + // IsDebuggerPresent=2147483682 if ((305 <= op && op <= 308) || op == 2147483648 || (2147483652 <= op && op <= 2147483653) || (2147483656 <= op && op <= 2147483657) || op == 2147483659 || op == 2147483662 || op == 2147483670 || - (2147483673 <= op && op <= 2147483675) || op == 2147483677) { + (2147483673 <= op && op <= 2147483675) || op == 2147483677 || + (2147483681 <= op && op <= 2147483682)) { major = 6; minor = 10; return; @@ -6683,6 +6703,16 @@ Function *OP::GetOpFunc(OpCode opCode, Type *pOverloadType) { A(pV); A(pI32); break; + + // Debugging + case OpCode::DebugBreak: + A(pV); + A(pI32); + break; + case OpCode::IsDebuggerPresent: + A(pI1); + A(pI32); + break; // OPCODE-OLOAD-FUNCS:END default: DXASSERT(false, "otherwise unhandled case"); @@ -6998,6 +7028,8 @@ llvm::Type *OP::GetOverloadType(OpCode opCode, llvm::Function *F) { case OpCode::LinAlgMatrixReserved0: case OpCode::LinAlgMatrixReserved1: case OpCode::LinAlgMatrixReserved2: + case OpCode::DebugBreak: + case OpCode::IsDebuggerPresent: return Type::getVoidTy(Ctx); case OpCode::QuadVote: return IntegerType::get(Ctx, 1); diff --git a/lib/HLSL/HLOperationLower.cpp b/lib/HLSL/HLOperationLower.cpp index 2d6df1fc32..9e11ea19e7 100644 --- a/lib/HLSL/HLOperationLower.cpp +++ b/lib/HLSL/HLOperationLower.cpp @@ -7697,6 +7697,10 @@ constexpr IntrinsicLower gLowerTable[] = { DXIL::OpCode::MatrixVecMul}, {IntrinsicOp::IOP___builtin_LinAlg_MatrixVectorMultiplyAdd, EmptyLower, DXIL::OpCode::MatrixVecMulAdd}, + {IntrinsicOp::IOP_DebugBreak, TrivialNoArgOperation, + DXIL::OpCode::DebugBreak}, + {IntrinsicOp::IOP_DxIsDebuggerPresent, TranslateWaveToVal, + DXIL::OpCode::IsDebuggerPresent}, }; constexpr size_t NumLowerTableEntries = sizeof(gLowerTable) / sizeof(gLowerTable[0]); diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index 1d012568d6..122f461a5f 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -16,6 +16,7 @@ #include "clang/SPIRV/SpirvInstruction.h" #include "clang/SPIRV/SpirvModule.h" +#include "spirv/unified1/NonSemanticDebugBreak.h" #include "spirv/unified1/NonSemanticDebugPrintf.h" namespace clang { @@ -434,6 +435,10 @@ class SpirvBuilder { QualType resultType, NonSemanticDebugPrintfInstructions instId, llvm::ArrayRef operands, SourceLocation); + /// \brief Creates an OpExtInst instruction for the NonSemantic.DebugBreak + /// extension set. Returns the resulting instruction pointer. + SpirvInstruction *createNonSemanticDebugBreakExtInst(SourceLocation); + SpirvInstruction *createIsNodePayloadValid(SpirvInstruction *payloadArray, SpirvInstruction *nodeIndex, SourceLocation); diff --git a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp index 389dc6eed4..20e9b2b45e 100644 --- a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp +++ b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp @@ -794,6 +794,9 @@ bool CapabilityVisitor::visit(SpirvExtInstImport *instr) { "NonSemantic.Shader.DebugInfo.100") { addExtension(Extension::KHR_non_semantic_info, "Shader.DebugInfo.100", /*SourceLocation*/ {}); + } else if (instr->getExtendedInstSetName() == "NonSemantic.DebugBreak") { + addExtension(Extension::KHR_non_semantic_info, "DebugBreak", + /*SourceLocation*/ {}); } return true; } diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index 82c0c8bbf9..5169ee7b13 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -894,6 +894,16 @@ SpirvInstruction *SpirvBuilder::createNonSemanticDebugPrintfExtInst( return extInst; } +SpirvInstruction * +SpirvBuilder::createNonSemanticDebugBreakExtInst(SourceLocation loc) { + assert(insertPoint && "null insert point"); + auto *extInst = new (context) SpirvExtInst( + astContext.VoidTy, loc, getExtInstSet("NonSemantic.DebugBreak"), + NonSemanticDebugBreakDebugBreak, {}); + insertPoint->addInstruction(extInst); + return extInst; +} + SpirvInstruction * SpirvBuilder::createIsNodePayloadValid(SpirvInstruction *payloadArray, SpirvInstruction *nodeIndex, diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index acf90f9853..c41e300975 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -9345,6 +9345,9 @@ SpirvEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { /*groupSync*/ true, /*isAllBarrier*/ true); break; + case hlsl::IntrinsicOp::IOP_DebugBreak: + retVal = spvBuilder.createNonSemanticDebugBreakExtInst(srcLoc); + break; case hlsl::IntrinsicOp::IOP_GetRemainingRecursionLevels: retVal = processIntrinsicGetRemainingRecursionLevels(callExpr); break; @@ -9565,6 +9568,7 @@ SpirvEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { retVal = processWaveQuadAnyAll(callExpr, hlslOpcode); break; case hlsl::IntrinsicOp::IOP_abort: + case hlsl::IntrinsicOp::IOP_DxIsDebuggerPresent: case hlsl::IntrinsicOp::IOP_GetRenderTargetSampleCount: case hlsl::IntrinsicOp::IOP_GetRenderTargetSamplePosition: { emitError("no equivalent for %0 intrinsic function in Vulkan", srcLoc) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index d83632e897..e6902898a2 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -3634,6 +3634,9 @@ class HLSLExternalSource : public ExternalSemaSource { case LICOMPTYPE_UINT: paramTypes.push_back(context.UnsignedIntTy); break; + case LICOMPTYPE_BOOL: + paramTypes.push_back(context.BoolTy); + break; case LICOMPTYPE_VOID: paramTypes.push_back(context.VoidTy); break; diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.debugbreak.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.debugbreak.hlsl new file mode 100644 index 0000000000..b9b60aebb0 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.debugbreak.hlsl @@ -0,0 +1,12 @@ +// RUN: %dxc -T cs_6_10 -spirv %s | FileCheck %s + +// CHECK: OpExtension "SPV_KHR_non_semantic_info" +// CHECK: OpExtInstImport "NonSemantic.DebugBreak" +// CHECK: OpExtInst %void {{%[0-9]+}} 1 + +[numthreads(8, 8, 1)] +void main(uint3 threadId : SV_DispatchThreadID) { + if (threadId.x == 0) { + DebugBreak(); + } +} diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.isdebugerpresent.error.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.isdebugerpresent.error.hlsl new file mode 100644 index 0000000000..e82df70065 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.isdebugerpresent.error.hlsl @@ -0,0 +1,12 @@ +// RUN: not %dxc -T cs_6_10 -spirv %s 2>&1 | FileCheck %s + +// CHECK: :9:9: error: no equivalent for IsDebuggerPresent intrinsic function in Vulkan + +RWStructuredBuffer Output : register(u0); + +[numthreads(8, 8, 1)] +void main(uint3 threadId : SV_DispatchThreadID) { + if (dx::IsDebuggerPresent()) { + Output[threadId.x] = 1; + } +} diff --git a/tools/clang/test/DXC/Passes/DxilGen/debugbreak.ll b/tools/clang/test/DXC/Passes/DxilGen/debugbreak.ll new file mode 100644 index 0000000000..a3b53dbf19 --- /dev/null +++ b/tools/clang/test/DXC/Passes/DxilGen/debugbreak.ll @@ -0,0 +1,59 @@ +; REQUIRES: dxil-1-10 +; RUN: %dxopt %s -hlsl-passes-resume -dxilgen -S | FileCheck %s + +; CHECK: call void @dx.op.debugBreak(i32 -2147483615) + +; Generated from: +; dxc -T cs_6_10 -fcgl tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/debugbreak.hlsl +; Debug info manually stripped. + +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" + +; Function Attrs: nounwind +define void @main(<3 x i32> %threadId) #0 { +entry: + %0 = extractelement <3 x i32> %threadId, i32 0 + %cmp = icmp eq i32 %0, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: + call void @"dx.hl.op..void (i32)"(i32 425) + br label %if.end + +if.end: + ret void +} + +; Function Attrs: nounwind +declare void @"dx.hl.op..void (i32)"(i32) #0 + +attributes #0 = { nounwind } + +!pauseresume = !{!0} +!llvm.ident = !{!1} +!dx.version = !{!2} +!dx.valver = !{!2} +!dx.shaderModel = !{!3} +!dx.typeAnnotations = !{!4} +!dx.entryPoints = !{!10} +!dx.fnprops = !{!14} +!dx.options = !{!15, !16} + +!0 = !{!"hlsl-hlemit", !"hlsl-hlensure"} +!1 = !{!"dxc(private) 1.9.0.5167 (Debug-Break, c89129305)"} +!2 = !{i32 1, i32 10} +!3 = !{!"cs", i32 6, i32 10} +!4 = !{i32 1, void (<3 x i32>)* @main, !5} +!5 = !{!6, !8} +!6 = !{i32 1, !7, !7} +!7 = !{} +!8 = !{i32 0, !9, !7} +!9 = !{i32 4, !"SV_DispatchThreadID", i32 7, i32 5, i32 13, i32 3} +!10 = !{void (<3 x i32>)* @main, !"main", null, !11, null} +!11 = !{null, null, null, null} +!12 = !{} +!13 = !{i32 0} +!14 = !{void (<3 x i32>)* @main, i32 5, i32 8, i32 8, i32 1} +!15 = !{i32 -2147483584} +!16 = !{i32 -1} diff --git a/tools/clang/test/DXC/Passes/DxilGen/isdebuggerpresent.ll b/tools/clang/test/DXC/Passes/DxilGen/isdebuggerpresent.ll new file mode 100644 index 0000000000..fa77e8f3c8 --- /dev/null +++ b/tools/clang/test/DXC/Passes/DxilGen/isdebuggerpresent.ll @@ -0,0 +1,97 @@ +; REQUIRES: dxil-1-10 +; RUN: %dxopt %s -hlsl-passes-resume -dxilgen -S | FileCheck %s + +; CHECK: call i1 @dx.op.isDebuggerPresent(i32 -2147483614) + +; Generated from: +; dxc -T cs_6_10 -fcgl tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/isdebuggerpresent.hlsl +; Debug info manually stripped. + +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" + +%"class.RWStructuredBuffer" = type { i32 } +%dx.types.Handle = type { i8* } +%dx.types.ResourceProperties = type { i32, i32 } + +@"\01?Output@@3V?$RWStructuredBuffer@I@@A" = external global %"class.RWStructuredBuffer", align 4 + +; Function Attrs: nounwind +define void @main(<3 x i32> %threadId) #0 { +entry: + %0 = call i1 @"dx.hl.op.ro.i1 (i32)"(i32 426) + br i1 %0, label %if.then, label %if.else + +if.then: + %1 = extractelement <3 x i32> %threadId, i32 0 + %2 = load %"class.RWStructuredBuffer", %"class.RWStructuredBuffer"* @"\01?Output@@3V?$RWStructuredBuffer@I@@A" + %3 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.RWStructuredBuffer\22)"(i32 0, %"class.RWStructuredBuffer" %2) + %4 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.RWStructuredBuffer\22)"(i32 14, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 4108, i32 4 }, %"class.RWStructuredBuffer" undef) + %5 = call i32* @"dx.hl.subscript.[].rn.i32* (i32, %dx.types.Handle, i32)"(i32 0, %dx.types.Handle %4, i32 %1) + store i32 1, i32* %5 + br label %if.end + +if.else: + %6 = extractelement <3 x i32> %threadId, i32 0 + %7 = load %"class.RWStructuredBuffer", %"class.RWStructuredBuffer"* @"\01?Output@@3V?$RWStructuredBuffer@I@@A" + %8 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.RWStructuredBuffer\22)"(i32 0, %"class.RWStructuredBuffer" %7) + %9 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.RWStructuredBuffer\22)"(i32 14, %dx.types.Handle %8, %dx.types.ResourceProperties { i32 4108, i32 4 }, %"class.RWStructuredBuffer" undef) + %10 = call i32* @"dx.hl.subscript.[].rn.i32* (i32, %dx.types.Handle, i32)"(i32 0, %dx.types.Handle %9, i32 %6) + store i32 0, i32* %10 + br label %if.end + +if.end: + ret void +} + +; Function Attrs: nounwind readonly +declare i1 @"dx.hl.op.ro.i1 (i32)"(i32) #1 + +; Function Attrs: nounwind readnone +declare i32* @"dx.hl.subscript.[].rn.i32* (i32, %dx.types.Handle, i32)"(i32, %dx.types.Handle, i32) #2 + +; Function Attrs: nounwind readnone +declare %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.RWStructuredBuffer\22)"(i32, %"class.RWStructuredBuffer") #2 + +; Function Attrs: nounwind readnone +declare %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.RWStructuredBuffer\22)"(i32, %dx.types.Handle, %dx.types.ResourceProperties, %"class.RWStructuredBuffer") #2 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readonly } +attributes #2 = { nounwind readnone } + +!pauseresume = !{!0} +!llvm.ident = !{!1} +!dx.version = !{!2} +!dx.valver = !{!2} +!dx.shaderModel = !{!3} +!dx.typeAnnotations = !{!4, !10} +!dx.entryPoints = !{!16} +!dx.fnprops = !{!22} +!dx.options = !{!23, !24} + +!0 = !{!"hlsl-hlemit", !"hlsl-hlensure"} +!1 = !{!"dxc(private) 1.9.0.5167 (Debug-Break, c89129305)"} +!2 = !{i32 1, i32 10} +!3 = !{!"cs", i32 6, i32 10} +!4 = !{i32 0, %"class.RWStructuredBuffer" undef, !5} +!5 = !{i32 4, !6, !7} +!6 = !{i32 6, !"h", i32 3, i32 0, i32 7, i32 5} +!7 = !{i32 0, !8} +!8 = !{!9} +!9 = !{i32 0, i32 undef} +!10 = !{i32 1, void (<3 x i32>)* @main, !11} +!11 = !{!12, !14} +!12 = !{i32 1, !13, !13} +!13 = !{} +!14 = !{i32 0, !15, !13} +!15 = !{i32 4, !"SV_DispatchThreadID", i32 7, i32 5, i32 13, i32 3} +!16 = !{void (<3 x i32>)* @main, !"main", null, !17, null} +!17 = !{null, !18, null, null} +!18 = !{!19} +!19 = !{i32 0, %"class.RWStructuredBuffer"* @"\01?Output@@3V?$RWStructuredBuffer@I@@A", !"Output", i32 0, i32 0, i32 1, i32 12, i1 false, i1 false, i1 false, !20} +!20 = !{i32 1, i32 4} +!21 = !{i32 0} +!22 = !{void (<3 x i32>)* @main, i32 5, i32 8, i32 8, i32 1} +!23 = !{i32 -2147483584} +!24 = !{i32 -1} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/debugbreak.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/debugbreak.hlsl new file mode 100644 index 0000000000..7147490437 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/debugbreak.hlsl @@ -0,0 +1,20 @@ +// REQUIRES: dxil-1-10 + +// RUN: %dxc -T cs_6_10 %s | FileCheck %s +// RUN: %dxc -T cs_6_10 -fcgl %s | FileCheck %s --check-prefix=FCGL +// RUN: %dxc -T cs_6_10 -ast-dump %s | FileCheck %s --check-prefix=AST + +// CHECK: call void @dx.op.debugBreak(i32 -2147483615) ; DebugBreak() +// FCGL: call void @"dx.hl.op..void (i32)"(i32 425) + +// AST: CallExpr {{.*}} 'void' +// AST-NEXT: `-ImplicitCastExpr {{.*}} 'void (*)()' +// AST-NEXT: `-DeclRefExpr {{.*}} 'DebugBreak' 'void ()' + + +[numthreads(8, 8, 1)] +void main(uint3 threadId : SV_DispatchThreadID) { + if (threadId.x == 0) { + DebugBreak(); + } +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/isdebuggerpresent.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/isdebuggerpresent.hlsl new file mode 100644 index 0000000000..ad58d814b7 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/intrinsics/basic/isdebuggerpresent.hlsl @@ -0,0 +1,24 @@ +// REQUIRES: dxil-1-10 + +// RUN: %dxc -T cs_6_10 %s | FileCheck %s +// RUN: %dxc -T cs_6_10 -fcgl %s | FileCheck %s --check-prefix=FCGL +// RUN: %dxc -T cs_6_10 -ast-dump %s | FileCheck %s --check-prefix=AST + +// CHECK: call i1 @dx.op.isDebuggerPresent(i32 -2147483614) ; IsDebuggerPresent() + +// FCGL: call i1 @"dx.hl.op.ro.i1 (i32)"(i32 426) + +// AST: CallExpr {{.*}} 'bool' +// AST-NEXT: `-ImplicitCastExpr {{.*}} 'bool (*)()' +// AST-NEXT: `-DeclRefExpr {{.*}} 'IsDebuggerPresent' 'bool ()' + +RWStructuredBuffer Output : register(u0); + +[numthreads(8, 8, 1)] +void main(uint3 threadId : SV_DispatchThreadID) { + if (dx::IsDebuggerPresent()) { + Output[threadId.x] = 1; + } else { + Output[threadId.x] = 0; + } +} diff --git a/utils/hct/gen_intrin_main.txt b/utils/hct/gen_intrin_main.txt index 55085f7216..5f61b5b6ab 100644 --- a/utils/hct/gen_intrin_main.txt +++ b/utils/hct/gen_intrin_main.txt @@ -119,6 +119,7 @@ $type1 [[rn]] ddx_fine(in float_like<> x); $type1 [[rn]] ddy(in float_like<> x); $type1 [[rn]] ddy_coarse(in float_like<> x); $type1 [[rn]] ddy_fine(in float_like<> x); +void [[min_sm=6.10]] DebugBreak(); $type1 [[rn]] degrees(in float_like<> x); $match<0, 1> float_like [[rn]] determinant(in float_like x); void [[]] DeviceMemoryBarrier() : syncdevicememory_ug; @@ -1180,6 +1181,7 @@ namespace DxHitObjectMethods { } namespace namespace DxIntrinsics { +bool [[ro,min_sm=6.10]] IsDebuggerPresent(); void [[min_sm=6.9]] MaybeReorderThread(in DxHitObject HitObject); void [[min_sm=6.9]] MaybeReorderThread(in uint CoherenceHint, in uint NumCoherenceHintBitsFromLSB); void [[min_sm=6.9]] MaybeReorderThread(in DxHitObject HitObject, in uint CoherenceHint, in uint NumCoherenceHintBitsFromLSB); diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 61b13c72bb..dd03d00450 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -1183,6 +1183,10 @@ def populate_categories_and_models_ExperimentalOps(self): "mesh", "amplification", ) + + for i in insts("DebugBreak", "IsDebuggerPresent"): + i.category = "Debugging" + i.shader_model = experimental_sm def populate_llvm_instructions(self): # Add instructions that map to LLVM instructions. @@ -6215,6 +6219,8 @@ def populate_ExperimentalOps(self): ) add_dxil_op = op_table.add_dxil_op + retvoid_param = db_dxil_param(0, "v", "", "no return value") + # Add Nop to test experimental table infrastructure. add_dxil_op( "ExperimentalNop", @@ -6702,6 +6708,28 @@ def populate_ExperimentalOps(self): op_table.reserve_dxil_op_range("LinAlgMatrixReserved", 3) + # Debugging intrinsics + add_dxil_op( + "DebugBreak", + "DebugBreak", + "triggers a breakpoint if a debugger is attached", + "v", + "nd", + [ + retvoid_param, + ], + ) + add_dxil_op( + "IsDebuggerPresent", + "IsDebuggerPresent", + "returns true if a debugger is attached", + "v", + "ro", + [ + db_dxil_param(0, "i1", "", "true if a debugger is attached"), + ], + ) + def finalize_dxil_operations(self): "Finalize DXIL operations by setting properties and verifying consistency." diff --git a/utils/hct/hlsl_intrinsic_opcodes.json b/utils/hct/hlsl_intrinsic_opcodes.json index b07789ed2c..8d06153c2d 100644 --- a/utils/hct/hlsl_intrinsic_opcodes.json +++ b/utils/hct/hlsl_intrinsic_opcodes.json @@ -1,6 +1,6 @@ { "IntrinsicOpCodes": { - "Num_Intrinsics": 425, + "Num_Intrinsics": 427, "IOP_AcceptHitAndEndSearch": 0, "IOP_AddUint64": 1, "IOP_AllMemoryBarrier": 2, @@ -425,6 +425,8 @@ "IOP___builtin_LinAlg_MatrixAccumulateToMemory": 421, "IOP___builtin_LinAlg_MatrixOuterProduct": 422, "IOP___builtin_LinAlg_MatrixVectorMultiply": 423, - "IOP___builtin_LinAlg_MatrixVectorMultiplyAdd": 424 + "IOP___builtin_LinAlg_MatrixVectorMultiplyAdd": 424, + "IOP_DebugBreak": 425, + "IOP_DxIsDebuggerPresent": 426 } }