瀏覽代碼

PIX shader debugging: debug phis by adding new blocks that write out relevant values (#2612)

This checkin adds new basic blocks as new preceding blocks between blocks containing Phis and the previous preceding blocks. The new blocks output PIX debug instrumentation for the value named by the Phi for that edge.
Jeff Noyle 5 年之前
父節點
當前提交
c5df9d33e6

+ 72 - 0
lib/DxilPIXPasses/DxilDebugInstrumentation.cpp

@@ -948,6 +948,78 @@ bool DxilDebugInstrumentation::runOnModule(Module &M) {
   addInvocationSelectionProlog(BC, SystemValues);
   addInvocationStartMarker(BC);
 
+  // Explicitly name new blocks in order to provide stable names for testing purposes
+  int NewBlockCounter = 0;
+
+  auto Fn = DM.GetEntryFunction();
+  auto &Blocks = Fn->getBasicBlockList();
+  for (auto &CurrentBlock : Blocks) {
+    struct ValueAndPhi {
+      Value *Val;
+      PHINode *Phi;
+      unsigned Index;
+    };
+
+    std::map<BasicBlock *, std::vector<ValueAndPhi>> InsertableEdges;
+
+    auto &Is = CurrentBlock.getInstList();
+    for (auto &Inst : Is) {
+      if (Inst.getOpcode() != Instruction::OtherOps::PHI) {
+        break;
+      }
+      PHINode &PN = llvm::cast<PHINode>(Inst);
+      for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i) {
+        BasicBlock *PhiBB = PN.getIncomingBlock(i);
+        Value *PhiVal = PN.getIncomingValue(i);
+        InsertableEdges[PhiBB].push_back({PhiVal, &PN, i});
+      }
+    }
+
+    for (auto &InsertableEdge : InsertableEdges) {
+      auto *NewBlock = BasicBlock::Create(Ctx, "PIXDebug" + std::to_string(NewBlockCounter++),
+                                          InsertableEdge.first->getParent());
+      IRBuilder<> Builder(NewBlock);
+
+      auto *PreviousBlock = InsertableEdge.first;
+
+      // Modify all successor operands of the terminator in the previous block
+      // that match the current block to point to the new block:
+      TerminatorInst *terminator = PreviousBlock->getTerminator();
+      unsigned NumSuccessors = terminator->getNumSuccessors();
+      for (unsigned SuccessorIndex = 0; SuccessorIndex < NumSuccessors;
+           ++SuccessorIndex) {
+        auto *CurrentSuccessor = terminator->getSuccessor(SuccessorIndex);
+        if (CurrentSuccessor == &CurrentBlock) {
+          terminator->setSuccessor(SuccessorIndex, NewBlock);
+        }
+      }
+
+      // Modify the Phis and add debug instrumentation
+      for (auto &ValueNPhi : InsertableEdge.second) {
+        // Modify the phi to refer to the new block:
+        ValueNPhi.Phi->setIncomingBlock(ValueNPhi.Index, NewBlock);
+
+        // Add instrumentation to the new block
+        std::uint32_t RegNum;
+        if (!pix_dxil::PixDxilReg::FromInst(ValueNPhi.Phi, &RegNum)) {
+          continue;
+        }
+
+        std::uint32_t InstNum;
+        if (!pix_dxil::PixDxilInstNum::FromInst(ValueNPhi.Phi, &InstNum)) {
+          continue;
+        }
+
+        BuilderContext BC{M, DM, Ctx, HlslOP, Builder};
+        addStepDebugEntryValue(BC, InstNum, ValueNPhi.Val, RegNum,
+                               BC.Builder.getInt32(0));
+      }
+
+      // Add a branch to the new block to point to the current block
+      Builder.CreateBr(&CurrentBlock);
+    }
+  }
+
   // Instrument original instructions:
   for (auto &Inst : AllInstructions) {
     // Instrumentation goes after the instruction if it is not a terminator.

+ 48 - 24
tools/clang/test/HLSLFileCheck/pix/DebugFlowControl.hlsl

@@ -2,31 +2,54 @@
 
 // Check that flow control constructs don't break the instrumentation.
 
-// check instrumentation for one branch. 
-
 // CHECK:  %UAVIncResult2 = call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %PIX_DebugUAV_Handle, i32 0, i32 0, i32 undef, i32 undef, i32 %IncrementForThisInvocation1)
+
 // CHECK:  %MaskedForUAVLimit3 = and i32 %UAVIncResult2, 983039
+
 // CHECK:  %MultipliedForInterest4 = mul i32 %MaskedForUAVLimit3, %OffsetMultiplicand
+
 // CHECK:  %AddedForInterest5 = add i32 %MultipliedForInterest4, %OffsetAddend
-// CHECK:  call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle %PIX_DebugUAV_Handle, i32 %AddedForInterest5, i32 undef, i32 64771, i32 undef, i32 undef, i32 undef, i8 1)
-// CHECK:  switch i32
-// CHECK:    i32 0, label 
-// CHECK:    i32 32, label
-// CHECK:  ]
+
+// CHECK:  call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle %PIX_DebugUAV_Handle, i32 %AddedForInterest5, i32 undef
+
+
+struct VS_OUTPUT_ENV {
+  float4 Pos : SV_Position;
+  float2 Tex : TEXCOORD0;
+};
 
 int i32;
 float f32;
 
-float4 Vectorize(float f)
-{
-  return float4((float)f / 128.f, (float)f / 128.f, (float)f / 128.f, 1.f);
+float4 Vectorize(float f) {
+  float4 ret;
+
+  if (f < 1024) // testbreakpoint0
+    ret.x = f;
+  else
+    ret.x = f + 100;
+
+  if (f < 512)
+    ret.y = f;
+  else
+    ret.y = f + 10;
+
+  if (f < 2048)
+    ret.z = f;
+  else
+    ret.z = f + 1000;
+
+  if (f < 4096)
+    ret.w = f + f;
+  else
+    ret.w = f + 1;
+
+  return ret;
 }
 
-float4 FlowControlPS() : SV_Target
-{
-  float4 ret = { 0,0,0,1 };
-  switch (i32)
-  {
+float4 FlowControlPS(VS_OUTPUT_ENV input) : SV_Target {
+  float4 ret = {0, 0, 0, 1}; // FirstExecutableLine
+  switch (i32) {
   case 0:
     ret = float4(1, 0, 1, 1);
     break;
@@ -35,19 +58,20 @@ float4 FlowControlPS() : SV_Target
     break;
   }
 
-  if (i32 > 10)
-  {
+  if (i32 > 10) {
     ret.r += 0.1f;
-  }
-  else
-  {
+  } else {
     ret.g += 0.1f;
   }
 
-  for (uint i = 0; i < 3; ++i)
+  for (uint i = 0; i < 3; ++i) // testbreakpoint1
   {
-    ret.b += (float)i32 / 10.f;
+    ret.b += f32 / 1024.f;
   }
 
-  return ret;
-}
+  for (uint j = 0; j < i32 / 8; ++j) {
+    ret.b += f32 / 1000.f;
+  }
+
+  return ret; // LastExecutableLine
+}

+ 59 - 0
tools/clang/test/HLSLFileCheck/pix/DebugPhisFor.hlsl

@@ -0,0 +1,59 @@
+// RUN: %dxc -EForLoopPS -Tps_6_0 %s -Od | %opt -S -dxil-annotate-with-virtual-regs -hlsl-dxil-debug-instrumentation | %FileCheck %s
+
+// Ensure that the pass added at the begining of the for body:
+// CHECK: br label %PIXDebug
+// CHECK: br label %PIXDebug
+// CHECK: br label %PIXDebug
+
+// Followed by lots of new pix debug blocks:
+
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+
+struct VS_OUTPUT_ENV {
+  float4 Pos : SV_Position;
+  float2 Tex : TEXCOORD0;
+};
+
+uint i32;
+
+float4 ForLoopPS(VS_OUTPUT_ENV input) : SV_Target {
+  float4 ret = float4(0, 0, 0, 0);
+  for (uint i = 0; i < abs(input.Tex.x * 200); ++i) {
+    ret.x += (float)i32;
+    if (i + i32 == 0) {
+      break;
+    }
+    ret.y += (float)i32;
+    if (i + i32 == 1) {
+      continue;
+    }
+    ret.z += (float)i32;
+    if (i + i32 == 2) {
+      break;
+    }
+    ret.w += (float)i32;
+    if (i + i32 == 3) {
+      continue;
+    }
+  }
+  return ret;
+}

+ 27 - 0
tools/clang/test/HLSLFileCheck/pix/DebugPhisIfElse.hlsl

@@ -0,0 +1,27 @@
+// RUN: %dxc -EFlowControlPS -Tps_6_0 %s -Od | %opt -S -dxil-annotate-with-virtual-regs -hlsl-dxil-debug-instrumentation | %FileCheck %s
+
+// Ensure that the pass added a block at the end of this if/else:
+// CHECK: br label %PIXDebug
+// CHECK: br label %PIXDebug
+
+// Check that block 0 emits some debug info and returns where we expect:
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// Check that block 1 emits some debug info and returns where we expect:
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+
+float4 FlowControlPS(in uint value : value ) : SV_Target
+{
+  float4 ret = float4(0, 0, 0, 0);
+  if (value > 1) {
+    ret = float4(0, 0, 0, 2);
+  } else {
+    ret = float4(0, 0, 0, 1);
+  }
+  return ret;
+}

+ 40 - 0
tools/clang/test/HLSLFileCheck/pix/DebugPhisSwicth.hlsl

@@ -0,0 +1,40 @@
+// RUN: %dxc -EFlowControlPS -Tps_6_0 %s -Od | %opt -S -dxil-annotate-with-virtual-regs -hlsl-dxil-debug-instrumentation | %FileCheck %s
+
+// Check for a branch to a new block for each case:
+// CHECK: br label %PIXDebug
+// CHECK: br label %PIXDebug
+// CHECK: br label %PIXDebug
+
+// Check that three PIXDebug blocks emit some debug info and returns where we expect:
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// Check that three PIXDebug blocks emit some debug info and returns where we expect:
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+// Check that three PIXDebug blocks emit some debug info and returns where we expect:
+// CHECK: PIXDebug
+// CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78
+// CHECK: br label
+
+
+float4 FlowControlPS(in uint value : value ) : SV_Target
+{
+  float4 ret = float4(0, 0, 0, 0);
+  switch (value)
+  {
+  case 0:
+    ret = float4(1, 0, 0, 0);
+    break;
+  case 1:
+    ret = float4(2, 0, 0, 0);
+    break;
+  default:
+    ret = float4(3, 0, 0, 0);
+    break;
+  }
+  return ret;
+}