Browse Source

Fix input vectors not having debug info (#2038)

We were systematically losing input vector debug info because of HLSignatureLower, which would replace them by per-element loadInput calls and then re-creating the vector through an insertelement chain. The llvm.dbg.value intrinsic was preserved for the recomposed vector, but this is not the authoritative source of data anymore - its elements are. Further optimizations would read past the insertelements and eventually we'd drop the vector and its debug info. To fix this, I'm now migrating the debug info to the individual vector elements as loaded from loadInput.

Also standardized on bitpieceexpression arguments being in bits everywhere, because it was becoming too confusing. The header for createBitPieceExpression had OffsetInBits while the source file had OffsetInBytes and various places converted counter-intuitively between the two.
Tristan Labelle 6 years ago
parent
commit
4431087506

+ 52 - 2
lib/HLSL/HLSignatureLower.cpp

@@ -25,8 +25,10 @@
 
 
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfo.h"
-#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/Utils/Local.h"
 
 
 using namespace llvm;
 using namespace llvm;
 using namespace hlsl;
 using namespace hlsl;
@@ -607,6 +609,29 @@ Value *replaceLdWithLdInput(Function *loadInput, LoadInst *ldInst,
   }
   }
 }
 }
 
 
+static DbgValueInst *findDbgValueInst(Value *Val) {
+  if (auto *ValAsMD = LocalAsMetadata::getIfExists(Val)) {
+    if (auto *ValMDAsVal = MetadataAsValue::getIfExists(Val->getContext(), ValAsMD)) {
+      for (User *ValMDUser : ValMDAsVal->users()) {
+        if (DbgValueInst *DbgValInst = dyn_cast<DbgValueInst>(ValMDUser))
+          return DbgValInst;
+      }
+    }
+  }
+  return nullptr;
+}
+
+static DIExpression *createNestedBitPieceExpression(DIBuilder &DbgInfoBuilder,
+  DIExpression* ParentDIExpr, unsigned OffsetInBits, unsigned SizeInBits) {
+  if (ParentDIExpr && ParentDIExpr->isBitPiece()) {
+    assert(OffsetInBits + SizeInBits <= ParentDIExpr->getBitPieceSize()
+      && "Nested bit piece expression exceeds bounds of its parent.");
+    OffsetInBits += ParentDIExpr->getBitPieceOffset();
+  }
+
+  return DbgInfoBuilder.createBitPieceExpression(OffsetInBits, SizeInBits);
+}
+
 void replaceDirectInputParameter(Value *param, Function *loadInput,
 void replaceDirectInputParameter(Value *param, Function *loadInput,
                                  unsigned cols, MutableArrayRef<Value *> args,
                                  unsigned cols, MutableArrayRef<Value *> args,
                                  bool bCast, OP *hlslOP, IRBuilder<> &Builder) {
                                  bool bCast, OP *hlslOP, IRBuilder<> &Builder) {
@@ -617,13 +642,38 @@ void replaceDirectInputParameter(Value *param, Function *loadInput,
   if (VectorType *VT = dyn_cast<VectorType>(Ty)) {
   if (VectorType *VT = dyn_cast<VectorType>(Ty)) {
     Value *newVec = llvm::UndefValue::get(VT);
     Value *newVec = llvm::UndefValue::get(VT);
     DXASSERT(cols == VT->getNumElements(), "vec size must match");
     DXASSERT(cols == VT->getNumElements(), "vec size must match");
+
+    // We're about to essentially SROA a vector,
+    // since we'll load elements individually and reconstruct it.
+    // We need to migrate the debug information from the vector
+    // to the constituing scalars since those are the original
+    // source of the data. Optimizations will later make the vector disappear,
+    // and we'll lose debug information applied to it.
+    DbgValueInst *DbgValInst = findDbgValueInst(param);
+    DIBuilder DbgInfoBuilder(*loadInput->getParent());
+    unsigned ElemSizeInBits = loadInput->getParent()->getDataLayout()
+      .getTypeAllocSize(VT->getElementType()) * 8;
+
     for (unsigned col = 0; col < cols; col++) {
     for (unsigned col = 0; col < cols; col++) {
       Value *colIdx = hlslOP->GetU8Const(col);
       Value *colIdx = hlslOP->GetU8Const(col);
       args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx;
       args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx;
       Value *input =
       Value *input =
           GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy);
           GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy);
       newVec = Builder.CreateInsertElement(newVec, input, col);
       newVec = Builder.CreateInsertElement(newVec, input, col);
+      if (DbgValInst) {
+        unsigned OffsetInBits = col * ElemSizeInBits;
+        DIExpression *DIExpr = createNestedBitPieceExpression(DbgInfoBuilder,
+          DbgValInst->getExpression(), OffsetInBits, ElemSizeInBits);
+        DbgInfoBuilder.insertDbgValueIntrinsic(input, 0, DbgValInst->getVariable(), DIExpr,
+          DbgValInst->getDebugLoc(), cast<Instruction>(newVec));
+      }
     }
     }
+
+    // This will relocate the DbgValueInst to the newly created vector.
+    // We will eventually lose it as optimizations realize that the
+    // new authoritative source of data are the loadInput instructions
+    // that we generated above, but this is fine since we created
+    // DbgValueInsts for them too.
     param->replaceAllUsesWith(newVec);
     param->replaceAllUsesWith(newVec);
   } else if (!Ty->isArrayTy() && !HLMatrixType::isa(Ty)) {
   } else if (!Ty->isArrayTy() && !HLMatrixType::isa(Ty)) {
     DXASSERT(cols == 1, "only support scalar here");
     DXASSERT(cols == 1, "only support scalar here");
@@ -631,7 +681,7 @@ void replaceDirectInputParameter(Value *param, Function *loadInput,
     args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx;
     args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx;
     Value *input =
     Value *input =
         GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy);
         GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy);
-    param->replaceAllUsesWith(input);
+    param->replaceAllUsesWith(input); // Will properly relocate any DbgValueInst
   } else if (HLMatrixType::isa(Ty)) {
   } else if (HLMatrixType::isa(Ty)) {
     if (param->use_empty()) return;
     if (param->use_empty()) return;
     DXASSERT(param->hasOneUse(),
     DXASSERT(param->hasOneUse(),

+ 5 - 7
lib/IR/DIBuilder.cpp

@@ -623,13 +623,11 @@ DIExpression *DIBuilder::createExpression(ArrayRef<int64_t> Signed) {
   return createExpression(Addr);
   return createExpression(Addr);
 }
 }
 
 
-DIExpression *DIBuilder::createBitPieceExpression(unsigned OffsetInBytes,
-                                                  unsigned SizeInBytes) {
-#if 0 // HLSL Change
-  uint64_t Addr[] = {dwarf::DW_OP_bit_piece, OffsetInBytes, SizeInBytes};
-#else // HLSL Change
-  uint64_t Addr[] = {dwarf::DW_OP_bit_piece, OffsetInBytes*8, SizeInBytes*8};
-#endif // HLSL Change
+// HLSL Begin Change: Match -InBits suffixes from header
+DIExpression *DIBuilder::createBitPieceExpression(unsigned OffsetInBits, 
+                                                  unsigned SizeInBits) {
+  uint64_t Addr[] = {dwarf::DW_OP_bit_piece, OffsetInBits, SizeInBits};
+// HLSL End Change
   return DIExpression::get(VMContext, Addr);
   return DIExpression::get(VMContext, Addr);
 }
 }
 
 

+ 0 - 4
lib/Transforms/Scalar/SROA.cpp

@@ -4327,11 +4327,7 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
             continue;
             continue;
           Size = std::min(Size, AbsEnd - Start);
           Size = std::min(Size, AbsEnd - Start);
         }
         }
-#if 0 // HLSL Change
         PieceExpr = DIB.createBitPieceExpression(Start, Size);
         PieceExpr = DIB.createBitPieceExpression(Start, Size);
-#else // HLSL Change
-        PieceExpr = DIB.createBitPieceExpression(Start / 8, Size / 8);
-#endif // HLSL Change
       }
       }
 
 
       // Remove any existing dbg.declare intrinsic describing the same alloca.
       // Remove any existing dbg.declare intrinsic describing the same alloca.

+ 4 - 6
lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp

@@ -1685,11 +1685,10 @@ bool SROA_HLSL::performScalarRepl(Function &F, DxilTypeSystem &typeSys) {
 
 
             DIExpression *DDIExp = nullptr;
             DIExpression *DDIExp = nullptr;
             if (parentOffset+debugOffset == 0 && DL.getTypeAllocSize(AI->getAllocatedType()) == size) {
             if (parentOffset+debugOffset == 0 && DL.getTypeAllocSize(AI->getAllocatedType()) == size) {
-              std::vector<uint64_t> args;
-              DDIExp = DIB.createExpression(args);
+              DDIExp = DIB.createExpression();
             }
             }
             else {
             else {
-              DDIExp = DIB.createBitPieceExpression(parentOffset+debugOffset, size);
+              DDIExp = DIB.createBitPieceExpression((parentOffset+debugOffset) * 8, size * 8);
             }
             }
             OffsetMap[Elt] = parentOffset+debugOffset;
             OffsetMap[Elt] = parentOffset+debugOffset;
 #endif // HLSL Change
 #endif // HLSL Change
@@ -5692,11 +5691,10 @@ void SROA_Parameter_HLSL::flattenArgument(
           argTy = argTy->getPointerElementType();
           argTy = argTy->getPointerElementType();
         DIExpression *DDIExp = nullptr;
         DIExpression *DDIExp = nullptr;
         if (debugOffset == 0 && DL.getTypeAllocSize(argTy) == size) {
         if (debugOffset == 0 && DL.getTypeAllocSize(argTy) == size) {
-          std::vector<uint64_t> Addr;
-          DDIExp = DIB.createExpression(Addr);
+          DDIExp = DIB.createExpression();
         }
         }
         else {
         else {
-          DDIExp = DIB.createBitPieceExpression(debugOffset, size);
+          DDIExp = DIB.createBitPieceExpression(debugOffset * 8, size * 8);
         }
         }
 #endif // HLSL Change
 #endif // HLSL Change
         debugOffset += size;
         debugOffset += size;

+ 5 - 5
lib/Transforms/Scalar/Scalarizer.cpp

@@ -693,21 +693,21 @@ bool Scalarizer::finish() {
           Type *Ty = Op->getType();
           Type *Ty = Op->getType();
           unsigned Count = Ty->getVectorNumElements();
           unsigned Count = Ty->getVectorNumElements();
           Type *EltTy = Ty->getVectorElementType();
           Type *EltTy = Ty->getVectorElementType();
-          unsigned EltSize = DL.getTypeSizeInBits(EltTy);
+          unsigned EltSizeInBits = DL.getTypeSizeInBits(EltTy);
           for (User *U : DINode->users())
           for (User *U : DINode->users())
             if (DbgValueInst *DVI = dyn_cast<DbgValueInst>(U)) {
             if (DbgValueInst *DVI = dyn_cast<DbgValueInst>(U)) {
               DIBuilder DIB(M, /*AllowUnresolved*/ false);
               DIBuilder DIB(M, /*AllowUnresolved*/ false);
               auto *VarInfo = DVI->getVariable();
               auto *VarInfo = DVI->getVariable();
               DebugLoc DbgLoc = DVI->getDebugLoc();
               DebugLoc DbgLoc = DVI->getDebugLoc();
-              unsigned Offset = 0;
+              unsigned OffsetInBits = 0;
               for (unsigned I = 0; I < Count; ++I) {
               for (unsigned I = 0; I < Count; ++I) {
                 // TODO: need to use DIExpression::createFragmentExpression for
                 // TODO: need to use DIExpression::createFragmentExpression for
                 // case DVI->getExpression is already bit piece.
                 // case DVI->getExpression is already bit piece.
                 DIExpression *EltExpr =
                 DIExpression *EltExpr =
-                    DIB.createBitPieceExpression(Offset / 8, EltSize / 8);
-                Offset += EltSize;
+                    DIB.createBitPieceExpression(OffsetInBits, EltSizeInBits);
+                OffsetInBits += EltSizeInBits;
 
 
-                DIB.insertDbgValueIntrinsic(CV[I], Offset, VarInfo, EltExpr,
+                DIB.insertDbgValueIntrinsic(CV[I], OffsetInBits, VarInfo, EltExpr,
                                             DbgLoc, DVI);
                                             DbgLoc, DVI);
               }
               }
             }
             }

+ 18 - 0
tools/clang/test/CodeGenHLSL/debug/input_vector.hlsl

@@ -0,0 +1,18 @@
+// RUN: %dxc -E main -T vs_6_0 -Zi %s | FileCheck %s
+
+// Test that an input vector's debug information is preserved,
+// especially through its lowering to per-element loadInputs.
+
+// CHECK: %[[xval:.*]] = call i32 @dx.op.loadInput.i32(i32 4, i32 0, i32 0, i8 0, i32 undef)
+// CHECK: call void @llvm.dbg.value(metadata i32 %[[xval]], i64 0, metadata ![[var:.*]], metadata ![[xexpr:.*]])
+// CHECK: %[[yval:.*]] = call i32 @dx.op.loadInput.i32(i32 4, i32 0, i32 0, i8 1, i32 undef)
+// CHECK: call void @llvm.dbg.value(metadata i32 %[[yval]], i64 0, metadata ![[var]], metadata ![[yexpr:.*]])
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 %[[xval]])
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 1, i32 %[[yval]])
+// CHECK: ret void
+
+// CHECK: ![[var]] = !DILocalVariable(tag: DW_TAG_arg_variable, name: "a", arg: 1
+// CHECK: ![[xexpr]] = !DIExpression(DW_OP_bit_piece, 0, 32)
+// CHECK: ![[yexpr]] = !DIExpression(DW_OP_bit_piece, 32, 32)
+
+int2 main(int2 a : IN) : OUT { return a; }

+ 16 - 0
tools/clang/test/CodeGenHLSL/debug/input_vector_in_struct.hlsl

@@ -0,0 +1,16 @@
+// RUN: %dxc -E main -T vs_6_0 -Zi %s | FileCheck %s
+
+// Test that debug offsets are correct after lowering a vector
+// input to loadInput calls, when the vector input itself was
+// offset in a struct.
+
+// CHECK: %[[val:.*]] = call i32 @dx.op.loadInput.i32(i32 4, i32 1, i32 0, i8 1, i32 undef)
+// CHECK: call void @llvm.dbg.value(metadata i32 %[[val]], i64 0, metadata ![[var:.*]], metadata ![[expr:.*]])
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 %[[val]])
+// CHECK: ret void
+
+// CHECK: ![[var]] = !DILocalVariable(tag: DW_TAG_arg_variable, name: "s", arg: 1
+// CHECK: ![[expr]] = !DIExpression(DW_OP_bit_piece, 96, 32)
+
+struct S { int2 a, b; };
+int main(S s : IN) : OUT { return s.b.y; }