-
Notifications
You must be signed in to change notification settings - Fork 850
[PIX] Instrument DebugBreak() calls for PIX #8349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
henchhinglimbu
wants to merge
4
commits into
microsoft:main
Choose a base branch
from
henchhinglimbu:pix-debugbreak-hit-instrumentation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
7211c61
Add DebgBreak hits DXIL PIX pass
henchhinglimbu 124c098
Update DxilDebugBreakInstrumentation.cpp to make clang-format compliant
henchhinglimbu e8c883e
Address feedback
henchhinglimbu 3603875
Make DxilDebugBreakInstrumentation.cpp clang-format compliant
henchhinglimbu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| /////////////////////////////////////////////////////////////////////////////// | ||
| // // | ||
| // DxilDebugBreakInstrumentation.cpp // | ||
| // Copyright (C) Microsoft Corporation. All rights reserved. // | ||
| // This file is distributed under the University of Illinois Open Source // | ||
| // License. See LICENSE.TXT for details. // | ||
| // // | ||
| // Provides a pass to instrument DebugBreak() calls for PIX. Each // | ||
| // DebugBreak call is replaced with a UAV bit-write so PIX can detect // | ||
| // which DebugBreak locations were hit without halting the GPU. // | ||
| // // | ||
| /////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| #include "PixPassHelpers.h" | ||
| #include "dxc/DXIL/DxilOperations.h" | ||
| #include "dxc/DxilPIXPasses/DxilPIXPasses.h" | ||
| #include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h" | ||
| #include "dxc/Support/Global.h" | ||
| #include "llvm/IR/Module.h" | ||
| #include "llvm/Support/FormattedStream.h" | ||
|
|
||
| using namespace llvm; | ||
| using namespace hlsl; | ||
|
|
||
| class DxilDebugBreakInstrumentation : public ModulePass { | ||
|
|
||
| public: | ||
| static char ID; // Pass identification, replacement for typeid | ||
| explicit DxilDebugBreakInstrumentation() : ModulePass(ID) {} | ||
| StringRef getPassName() const override { | ||
| return "DXIL DebugBreak Instrumentation"; | ||
| } | ||
| bool runOnModule(Module &M) override; | ||
| }; | ||
|
|
||
| bool DxilDebugBreakInstrumentation::runOnModule(Module &M) { | ||
| DxilModule &DM = M.GetOrCreateDxilModule(); | ||
| LLVMContext &Ctx = M.getContext(); | ||
| OP *HlslOP = DM.GetOP(); | ||
|
|
||
| hlsl::DxilResource *PixUAVResource = nullptr; | ||
|
|
||
| UndefValue *UndefArg = UndefValue::get(Type::getInt32Ty(Ctx)); | ||
|
|
||
| // Atomic operation to use for writing to the result UAV resource | ||
| Function *AtomicOpFunc = | ||
| HlslOP->GetOpFunc(OP::OpCode::AtomicBinOp, Type::getInt32Ty(Ctx)); | ||
| Constant *AtomicBinOpcode = | ||
| HlslOP->GetU32Const((uint32_t)OP::OpCode::AtomicBinOp); | ||
| Constant *AtomicOr = HlslOP->GetU32Const((uint32_t)DXIL::AtomicBinOpCode::Or); | ||
|
|
||
| std::map<Function *, CallInst *> FunctionToUAVHandle; | ||
|
|
||
| // Collect all DebugBreak calls first, then modify. | ||
| // This avoids invalidating iterators during modification. | ||
| std::vector<CallInst *> DebugBreakCalls; | ||
|
|
||
| Function *DebugBreakFunc = | ||
| HlslOP->GetOpFunc(OP::OpCode::DebugBreak, Type::getVoidTy(Ctx)); | ||
| for (const Use &U : DebugBreakFunc->uses()) { | ||
| DebugBreakCalls.push_back(cast<CallInst>(U.getUser())); | ||
| } | ||
|
|
||
| for (CallInst *CI : DebugBreakCalls) { | ||
| if (!PixUAVResource) | ||
| PixUAVResource = | ||
| PIXPassHelpers::CreateGlobalUAVResource(DM, 0, "PixUAVResource"); | ||
|
|
||
| Function *F = CI->getParent()->getParent(); | ||
|
|
||
| CallInst *PixUAVHandle = nullptr; | ||
| const auto FunctionToUAVHandleIter = FunctionToUAVHandle.lower_bound(F); | ||
|
|
||
| if ((FunctionToUAVHandleIter != FunctionToUAVHandle.end()) && | ||
| (FunctionToUAVHandleIter->first == F)) { | ||
| PixUAVHandle = FunctionToUAVHandleIter->second; | ||
| } else { | ||
| IRBuilder<> Builder(F->getEntryBlock().getFirstInsertionPt()); | ||
|
|
||
| PixUAVHandle = PIXPassHelpers::CreateHandleForResource( | ||
| DM, Builder, PixUAVResource, "PixUAVHandle"); | ||
|
|
||
| FunctionToUAVHandle.insert(FunctionToUAVHandleIter, {F, PixUAVHandle}); | ||
| } | ||
|
|
||
| IRBuilder<> Builder(CI); | ||
|
|
||
| uint32_t InstructionNumber = 0; | ||
| if (!pix_dxil::PixDxilInstNum::FromInst(CI, &InstructionNumber)) { | ||
| DXASSERT(false, "Failed to extract PIX instruction number metadata from " | ||
| "DebugBreak call"); | ||
| } | ||
|
|
||
| // The output UAV is treated as a bit array where each bit corresponds | ||
| // to an instruction number. | ||
| const uint32_t InstructionNumByteOffset = | ||
| (InstructionNumber / 32u) * sizeof(uint32_t); | ||
| const uint32_t InstructionNumBitPosition = (InstructionNumber % 32u); | ||
| const uint32_t InstructionNumBitMask = 1u << InstructionNumBitPosition; | ||
|
|
||
| Constant *UAVByteOffsetArg = HlslOP->GetU32Const(InstructionNumByteOffset); | ||
| Constant *BitMaskArg = HlslOP->GetU32Const(InstructionNumBitMask); | ||
|
|
||
| // Write a 1 bit at the position corresponding to this DebugBreak's | ||
| // instruction number, indicating it was hit. | ||
| Builder.CreateCall( | ||
| AtomicOpFunc, | ||
| { | ||
| AtomicBinOpcode, // i32, ; opcode | ||
| PixUAVHandle, // %dx.types.Handle, ; resource handle | ||
| AtomicOr, // i32, ; binary operation code | ||
| UAVByteOffsetArg, // i32, ; coordinate c0: byte offset | ||
| UndefArg, // i32, ; coordinate c1 (unused) | ||
| UndefArg, // i32, ; coordinate c2 (unused) | ||
| BitMaskArg // i32); value | ||
| }, | ||
| "DebugBreakBitSet"); | ||
|
|
||
| // Remove the original DebugBreak call to prevent GPU halt | ||
| CI->eraseFromParent(); | ||
| } | ||
|
|
||
| // Clean up the now-unused declaration. Not strictly required for | ||
| // correctness, but keeps the module free of dead references. | ||
| if (DebugBreakFunc->use_empty()) | ||
| DebugBreakFunc->eraseFromParent(); | ||
|
|
||
| const bool modified = (PixUAVResource != nullptr); | ||
|
|
||
| if (modified) { | ||
| DM.ReEmitDxilResources(); | ||
|
|
||
| if (OSOverride != nullptr) { | ||
| formatted_raw_ostream FOS(*OSOverride); | ||
| FOS << "\nFoundDebugBreak\n"; | ||
| } | ||
| } | ||
|
|
||
| return modified; | ||
| } | ||
|
|
||
| char DxilDebugBreakInstrumentation::ID = 0; | ||
|
|
||
| ModulePass *llvm::createDxilDebugBreakInstrumentationPass() { | ||
| return new DxilDebugBreakInstrumentation(); | ||
| } | ||
|
|
||
| INITIALIZE_PASS(DxilDebugBreakInstrumentation, | ||
| "hlsl-dxil-debugbreak-instrumentation", | ||
| "HLSL DXIL DebugBreak instrumentation for PIX", false, false) | ||
15 changes: 15 additions & 0 deletions
15
tools/clang/test/HLSLFileCheck/pix/DebugBreakInstrumentation.hlsl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| // RUN: %dxc -Emain -Tcs_6_10 %s | %opt -S -dxil-annotate-with-virtual-regs -hlsl-dxil-debugbreak-instrumentation | %FileCheck %s | ||
|
|
||
| // Verify the PIX UAV handle is created for DebugBreak instrumentation: | ||
| // CHECK: %PixUAVHandle = call %dx.types.Handle @dx.op.createHandleFromBinding( | ||
|
|
||
| // Verify an AtomicBinOp (opcode 78) was emitted to record the DebugBreak hit: | ||
| // CHECK: %DebugBreakBitSet = call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle | ||
|
|
||
| // Verify the original DebugBreak call was removed: | ||
| // CHECK-NOT: @dx.op.debugBreak | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main() { | ||
| DebugBreak(); | ||
| } |
24 changes: 24 additions & 0 deletions
24
tools/clang/test/HLSLFileCheck/pix/DebugBreakInstrumentationMultiple.hlsl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| // RUN: %dxc -Emain -Tcs_6_10 %s | %opt -S -dxil-annotate-with-virtual-regs -hlsl-dxil-debugbreak-instrumentation | %FileCheck %s | ||
|
|
||
| // Verify the PIX UAV handle is created: | ||
| // CHECK: %PixUAVHandle = call %dx.types.Handle @dx.op.createHandleFromBinding( | ||
|
|
||
| // Verify two AtomicBinOp calls were emitted (one per DebugBreak): | ||
| // CHECK: DebugBreakBitSet{{.*}} = call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle | ||
| // CHECK: DebugBreakBitSet{{.*}} = call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle | ||
|
|
||
| // Verify no DebugBreak calls remain: | ||
| // CHECK-NOT: @dx.op.debugBreak | ||
|
|
||
| RWByteAddressBuffer buf : register(u0); | ||
|
|
||
| [numthreads(1, 1, 1)] | ||
| void main(uint3 tid : SV_DispatchThreadID) { | ||
| if (tid.x == 0) | ||
| DebugBreak(); | ||
|
|
||
| buf.Store(0, tid.x); | ||
|
|
||
| if (tid.x == 1) | ||
| DebugBreak(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are still uses of this, does this mean that something actually went wrong with our attempt to remove all uses? Is this something we'd want to know about via an assert or failure condition/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more of a cleanup step. The DebugBreak calls have already been replaced with atomic UAV writes above, so the pass works correctly without this. We just remove the leftover declaration to keep the module clean. I can update the comment to better reflect this.