Skip to content

Commit

Permalink
[MachineDebugify] Insert synthetic DBG_VALUE instructions
Browse files Browse the repository at this point in the history
Summary:
Teach MachineDebugify how to insert DBG_VALUE instructions.  This can
help find bugs causing CodeGen differences when debug info is present.
DBG_VALUE instructions are only emitted when -debugify-level is set to
locations+variables.

There is essentially no attempt made to match up DBG_VALUE register
operands with the local variables they ought to correspond to. I'm not
sure how to improve the situation. In some cases (MachineMemOperand?)
it's possible to find the IR instruction a MachineInstr corresponds to,
but in general this seems to call for "undoing" the work done by ISel.

Reviewers: dsanders, aprantl

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D78135
  • Loading branch information
vedantk committed Apr 23, 2020
1 parent b424b0b commit 2a5675f
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 20 deletions.
90 changes: 87 additions & 3 deletions llvm/lib/CodeGen/MachineDebugify.cpp
Expand Up @@ -7,16 +7,23 @@
//===----------------------------------------------------------------------===//
///
/// \file This pass attaches synthetic debug info to everything. It can be used
/// to create targeted tests for debug info preservation.
/// to create targeted tests for debug info preservation, or test for CodeGen
/// differences with vs. without debug info.
///
/// This isn't intended to have feature parity with Debugify.
//===----------------------------------------------------------------------===//

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/InitializePasses.h"
#include "llvm/Transforms/Utils/Debugify.h"

Expand All @@ -31,13 +38,15 @@ bool applyDebugifyMetadataToMachineFunction(MachineModuleInfo &MMI,
if (!MaybeMF)
return false;
MachineFunction &MF = *MaybeMF;
const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();

DISubprogram *SP = F.getSubprogram();
assert(SP && "IR Debugify just created it?");

LLVMContext &Ctx = F.getParent()->getContext();
unsigned NextLine = SP->getLine();
Module &M = *F.getParent();
LLVMContext &Ctx = M.getContext();

unsigned NextLine = SP->getLine();
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : MBB) {
// This will likely emit line numbers beyond the end of the imagined
Expand All @@ -48,6 +57,81 @@ bool applyDebugifyMetadataToMachineFunction(MachineModuleInfo &MMI,
}
}

// Find local variables defined by debugify. No attempt is made to match up
// MIR-level regs to the 'correct' IR-level variables: there isn't a simple
// way to do that, and it isn't necessary to find interesting CodeGen bugs.
// Instead, simply keep track of one variable per line. Later, we can insert
// DBG_VALUE insts that point to these local variables. Emitting DBG_VALUEs
// which cover a wide range of lines can help stress the debug info passes:
// if we can't do that, fall back to using the local variable which precedes
// all the others.
Function *DbgValF = M.getFunction("llvm.dbg.value");
DbgValueInst *EarliestDVI = nullptr;
DenseMap<unsigned, DILocalVariable *> Line2Var;
DIExpression *Expr = nullptr;
if (DbgValF) {
for (const Use &U : DbgValF->uses()) {
auto *DVI = dyn_cast<DbgValueInst>(U.getUser());
if (!DVI || DVI->getFunction() != &F)
continue;
unsigned Line = DVI->getDebugLoc().getLine();
assert(Line != 0 && "debugify should not insert line 0 locations");
Line2Var[Line] = DVI->getVariable();
if (!EarliestDVI || Line < EarliestDVI->getDebugLoc().getLine())
EarliestDVI = DVI;
Expr = DVI->getExpression();
}
}
if (Line2Var.empty())
return true;

// Now, try to insert a DBG_VALUE instruction after each real instruction.
// Do this by introducing debug uses of each register definition. If that is
// not possible (e.g. we have a phi or a meta instruction), emit a constant.
uint64_t NextImm = 0;
const MCInstrDesc &DbgValDesc = TII.get(TargetOpcode::DBG_VALUE);
for (MachineBasicBlock &MBB : MF) {
MachineBasicBlock::iterator FirstNonPHIIt = MBB.getFirstNonPHI();
for (auto I = MBB.begin(), E = MBB.end(); I != E; ) {
MachineInstr &MI = *I;
++I;

// `I` may point to a DBG_VALUE created in the previous loop iteration.
if (MI.isDebugInstr())
continue;

// It's not allowed to insert DBG_VALUEs after a terminator.
if (MI.isTerminator())
continue;

// Find a suitable insertion point for the DBG_VALUE.
auto InsertBeforeIt = MI.isPHI() ? FirstNonPHIIt : I;

// Find a suitable local variable for the DBG_VALUE.
unsigned Line = MI.getDebugLoc().getLine();
if (!Line2Var.count(Line))
Line = EarliestDVI->getDebugLoc().getLine();
DILocalVariable *LocalVar = Line2Var[Line];
assert(LocalVar && "No variable for current line?");

// Emit DBG_VALUEs for register definitions.
SmallVector<MachineOperand *, 4> RegDefs;
for (MachineOperand &MO : MI.operands())
if (MO.isReg() && MO.isDef() && MO.getReg())
RegDefs.push_back(&MO);
for (MachineOperand *MO : RegDefs)
BuildMI(MBB, InsertBeforeIt, MI.getDebugLoc(), DbgValDesc,
/*IsIndirect=*/false, *MO, LocalVar, Expr);

// OK, failing that, emit a constant DBG_VALUE.
if (RegDefs.empty()) {
auto ImmOp = MachineOperand::CreateImm(NextImm++);
BuildMI(MBB, InsertBeforeIt, MI.getDebugLoc(), DbgValDesc,
/*IsIndirect=*/false, ImmOp, LocalVar, Expr);
}
}
}

return true;
}

Expand Down
40 changes: 31 additions & 9 deletions llvm/lib/Transforms/Utils/Debugify.cpp
Expand Up @@ -75,6 +75,7 @@ bool llvm::applyDebugifyMetadata(

DIBuilder DIB(M);
LLVMContext &Ctx = M.getContext();
auto *Int32Ty = Type::getInt32Ty(Ctx);

// Get a DIType which corresponds to Ty.
DenseMap<uint64_t, DIType *> TypeCache;
Expand All @@ -99,6 +100,7 @@ bool llvm::applyDebugifyMetadata(
if (isFunctionSkipped(F))
continue;

bool InsertedDbgVal = false;
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
DISubprogram::DISPFlags SPFlags =
DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized;
Expand All @@ -107,6 +109,23 @@ bool llvm::applyDebugifyMetadata(
auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine,
SPType, NextLine, DINode::FlagZero, SPFlags);
F.setSubprogram(SP);

// Helper that inserts a dbg.value before \p InsertBefore, copying the
// location (and possibly the type, if it's non-void) from \p TemplateInst.
auto insertDbgVal = [&](Instruction &TemplateInst,
Instruction *InsertBefore) {
std::string Name = utostr(NextVar++);
Value *V = &TemplateInst;
if (TemplateInst.getType()->isVoidTy())
V = ConstantInt::get(Int32Ty, 0);
const DILocation *Loc = TemplateInst.getDebugLoc().get();
auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),
getCachedDIType(V->getType()),
/*AlwaysPreserve=*/true);
DIB.insertDbgValueIntrinsic(V, LocalVar, DIB.createExpression(), Loc,
InsertBefore);
};

for (BasicBlock &BB : F) {
// Attach debug locations.
for (Instruction &I : BB)
Expand Down Expand Up @@ -141,15 +160,19 @@ bool llvm::applyDebugifyMetadata(
if (!isa<PHINode>(I) && !I->isEHPad())
InsertBefore = I->getNextNode();

std::string Name = utostr(NextVar++);
const DILocation *Loc = I->getDebugLoc().get();
auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),
getCachedDIType(I->getType()),
/*AlwaysPreserve=*/true);
DIB.insertDbgValueIntrinsic(I, LocalVar, DIB.createExpression(), Loc,
InsertBefore);
insertDbgVal(*I, InsertBefore);
InsertedDbgVal = true;
}
}
// Make sure we emit at least one dbg.value, otherwise MachineDebugify may
// not have anything to work with as it goes about inserting DBG_VALUEs.
// (It's common for MIR tests to be written containing skeletal IR with
// empty functions -- we're still interested in debugifying the MIR within
// those tests, and this helps with that.)
if (DebugifyLevel == Level::LocationsAndVariables && !InsertedDbgVal) {
auto *Term = findTerminatingInstruction(F.getEntryBlock());
insertDbgVal(*Term, Term);
}
if (ApplyToMF)
ApplyToMF(DIB, F);
DIB.finalizeSubprogram(SP);
Expand All @@ -158,10 +181,9 @@ bool llvm::applyDebugifyMetadata(

// Track the number of distinct lines and variables.
NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify");
auto *IntTy = Type::getInt32Ty(Ctx);
auto addDebugifyOperand = [&](unsigned N) {
NMD->addOperand(MDNode::get(
Ctx, ValueAsMetadata::getConstant(ConstantInt::get(IntTy, N))));
Ctx, ValueAsMetadata::getConstant(ConstantInt::get(Int32Ty, N))));
};
addDebugifyOperand(NextLine - 1); // Original number of lines.
addDebugifyOperand(NextVar - 1); // Original number of variables.
Expand Down
33 changes: 33 additions & 0 deletions llvm/test/CodeGen/AArch64/GlobalISel/constant-mir-debugify.mir
@@ -0,0 +1,33 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -debugify-and-strip-all-safe=0 -run-pass=mir-debugify -verify-machineinstrs -mtriple aarch64-unknown-unknown %s -o - | FileCheck %s
...
---
name: fconstant_to_constant_s32
alignment: 4
tracksRegLiveness: true
frameInfo:
maxAlignment: 1
machineFunctionInfo: {}
body: |
bb.0:
liveins: $x0
; CHECK-LABEL: name: fconstant_to_constant_s32
; CHECK: liveins: $x0
; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0, debug-location !10
; CHECK: DBG_VALUE [[COPY]](p0), $noreg, !8, !DIExpression(), debug-location !10
; CHECK: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 0x3FA99999A0000000, debug-location !DILocation(line: 2, column: 1, scope: !5)
; CHECK: DBG_VALUE [[C]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 2, column: 1, scope: !5)
; CHECK: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 524, debug-location !DILocation(line: 3, column: 1, scope: !5)
; CHECK: DBG_VALUE [[C1]](s64), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 3, column: 1, scope: !5)
; CHECK: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[COPY]], [[C1]](s64), debug-location !DILocation(line: 4, column: 1, scope: !5)
; CHECK: DBG_VALUE [[PTR_ADD]](p0), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !5)
; CHECK: G_STORE [[C]](s32), [[PTR_ADD]](p0), debug-location !DILocation(line: 5, column: 1, scope: !5) :: (store 4)
; CHECK: DBG_VALUE 0, $noreg, !8, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !5)
; CHECK: RET_ReallyLR debug-location !DILocation(line: 6, column: 1, scope: !5)
%0:_(p0) = COPY $x0
%3:_(s32) = G_FCONSTANT float 0x3FA99999A0000000
%1:_(s64) = G_CONSTANT i64 524
%2:_(p0) = G_PTR_ADD %0, %1(s64)
G_STORE %3(s32), %2(p0) :: (store 4)
RET_ReallyLR
...
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -O0 -mtriple=aarch64-unknown-unknown -verify-machineinstrs -run-pass=legalizer %s -o - | FileCheck %s
# RUN: llc -debugify-and-strip-all-safe -O0 -mtriple=aarch64-unknown-unknown -verify-machineinstrs -run-pass=legalizer %s -o - | FileCheck %s
--- |
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

Expand Down
106 changes: 106 additions & 0 deletions llvm/test/CodeGen/AArch64/GlobalISel/phi-mir-debugify.mir
@@ -0,0 +1,106 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -debugify-and-strip-all-safe=0 -run-pass=mir-debugify -verify-machineinstrs %s -o - | FileCheck %s
--- |
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-unknown"

define i32 @legalize_phi(i32 %argc) {
entry:
ret i32 0
}

...
---
name: legalize_phi
alignment: 4
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
tracksRegLiveness: true
registers:
- { id: 0, class: _, preferred-register: '' }
- { id: 1, class: _, preferred-register: '' }
- { id: 2, class: _, preferred-register: '' }
- { id: 3, class: _, preferred-register: '' }
- { id: 4, class: _, preferred-register: '' }
- { id: 5, class: _, preferred-register: '' }
- { id: 6, class: _, preferred-register: '' }
- { id: 7, class: _, preferred-register: '' }
- { id: 8, class: _, preferred-register: '' }
- { id: 9, class: _, preferred-register: '' }
- { id: 10, class: _, preferred-register: '' }
liveins:
body: |
; CHECK-LABEL: name: legalize_phi
; CHECK: bb.0:
; CHECK: successors: %bb.1(0x40000000), %bb.2(0x40000000)
; CHECK: liveins: $w0
; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $w0, debug-location !10
; CHECK: DBG_VALUE [[COPY]](s32), $noreg, !8, !DIExpression(), debug-location !10
; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0, debug-location !DILocation(line: 2, column: 1, scope: !5)
; CHECK: DBG_VALUE [[C]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 2, column: 1, scope: !5)
; CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1, debug-location !DILocation(line: 3, column: 1, scope: !5)
; CHECK: DBG_VALUE [[C1]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 3, column: 1, scope: !5)
; CHECK: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2, debug-location !DILocation(line: 4, column: 1, scope: !5)
; CHECK: DBG_VALUE [[C2]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !5)
; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(ugt), [[COPY]](s32), [[C]], debug-location !DILocation(line: 5, column: 1, scope: !5)
; CHECK: DBG_VALUE [[ICMP]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !5)
; CHECK: G_BRCOND [[ICMP]](s1), %bb.1, debug-location !DILocation(line: 6, column: 1, scope: !5)
; CHECK: G_BR %bb.2, debug-location !DILocation(line: 7, column: 1, scope: !5)
; CHECK: bb.1:
; CHECK: successors: %bb.3(0x80000000)
; CHECK: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[C1]], debug-location !DILocation(line: 8, column: 1, scope: !5)
; CHECK: DBG_VALUE [[ADD]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 8, column: 1, scope: !5)
; CHECK: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ADD]](s32), debug-location !DILocation(line: 9, column: 1, scope: !5)
; CHECK: DBG_VALUE [[TRUNC]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 9, column: 1, scope: !5)
; CHECK: G_BR %bb.3, debug-location !DILocation(line: 10, column: 1, scope: !5)
; CHECK: bb.2:
; CHECK: successors: %bb.3(0x80000000)
; CHECK: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[C2]], debug-location !DILocation(line: 11, column: 1, scope: !5)
; CHECK: DBG_VALUE [[ADD1]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 11, column: 1, scope: !5)
; CHECK: [[TRUNC1:%[0-9]+]]:_(s1) = G_TRUNC [[ADD1]](s32), debug-location !DILocation(line: 12, column: 1, scope: !5)
; CHECK: DBG_VALUE [[TRUNC1]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 12, column: 1, scope: !5)
; CHECK: bb.3:
; CHECK: [[PHI:%[0-9]+]]:_(s1) = G_PHI [[TRUNC]](s1), %bb.1, [[TRUNC1]](s1), %bb.2, debug-location !DILocation(line: 13, column: 1, scope: !5)
; CHECK: [[PHI1:%[0-9]+]]:_(s1) = G_PHI [[TRUNC]](s1), %bb.1, [[TRUNC1]](s1), %bb.2, debug-location !DILocation(line: 14, column: 1, scope: !5)
; CHECK: DBG_VALUE [[PHI]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 13, column: 1, scope: !5)
; CHECK: DBG_VALUE [[PHI1]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 14, column: 1, scope: !5)
; CHECK: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[PHI]](s1), debug-location !DILocation(line: 15, column: 1, scope: !5)
; CHECK: DBG_VALUE [[ZEXT]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 15, column: 1, scope: !5)
; CHECK: $w0 = COPY [[ZEXT]](s32), debug-location !DILocation(line: 16, column: 1, scope: !5)
; CHECK: DBG_VALUE $w0, $noreg, !8, !DIExpression(), debug-location !DILocation(line: 16, column: 1, scope: !5)
; CHECK: RET_ReallyLR implicit $w0, debug-location !DILocation(line: 17, column: 1, scope: !5)
bb.0:
successors: %bb.1(0x40000000), %bb.2(0x40000000)
liveins: $w0
%0(s32) = COPY $w0
%1(s32) = G_CONSTANT i32 0
%3(s32) = G_CONSTANT i32 1
%6(s32) = G_CONSTANT i32 2
%2(s1) = G_ICMP intpred(ugt), %0(s32), %1
G_BRCOND %2(s1), %bb.1
G_BR %bb.2
bb.1:
successors: %bb.3(0x80000000)
%4(s32) = G_ADD %0, %3
%5(s1) = G_TRUNC %4(s32)
G_BR %bb.3
bb.2:
successors: %bb.3(0x80000000)
%7(s32) = G_ADD %0, %6
%8(s1) = G_TRUNC %7(s32)
bb.3:
%9(s1) = G_PHI %5(s1), %bb.1, %8(s1), %bb.2
%11:_(s1) = G_PHI %5(s1), %bb.1, %8(s1), %bb.2
%10(s32) = G_ZEXT %9(s1)
$w0 = COPY %10(s32)
RET_ReallyLR implicit $w0
...
Expand Up @@ -17,11 +17,13 @@
ret i32 %sub
}

; CHECK: !llvm.dbg.cu = !{!0}
; CHECK: !llvm.debugify =
; CHECK: !llvm.module.flags = !{![[VERSION:[0-9]+]]}
; CHECK: !0 = distinct !DICompileUnit(
; CHECK: ![[VERSION]] = !{i32 2, !"Debug Info Version", i32 3}
; ALL: !llvm.dbg.cu = !{!0}
; ALL: !llvm.debugify =
; ALL: !llvm.module.flags = !{![[VERSION:[0-9]+]]}
; ALL: !0 = distinct !DICompileUnit(
; ALL: ![[VERSION]] = !{i32 2, !"Debug Info Version", i32 3}
; VALUE: [[VAR1:![0-9]+]] = !DILocalVariable(name: "1"
; VALUE: [[VAR2:![0-9]+]] = !DILocalVariable(name: "2"

...
---
Expand All @@ -37,8 +39,13 @@ body: |
; source file anyway. These first three coincide with IR-level information
; and therefore use metadata references.
; ALL: %0:_(s32) = IMPLICIT_DEF debug-location [[L1]]
; VALUE: DBG_VALUE %0(s32), $noreg, [[VAR1]], !DIExpression(), debug-location [[L1]]
; ALL: %1:_(s32) = IMPLICIT_DEF debug-location [[L2]]
; VALUE: DBG_VALUE %1(s32), $noreg, [[VAR2]], !DIExpression(), debug-location [[L2]]
; ALL: %2:_(s32) = G_CONSTANT i32 2, debug-location [[L3]]
; VALUE: DBG_VALUE %2(s32), $noreg, [[VAR1]], !DIExpression(), debug-location [[L3]]
; ALL: %3:_(s32) = G_ADD %0, %2, debug-location !DILocation(line: 4, column: 1, scope: !6)
; VALUE: DBG_VALUE %3(s32), $noreg, [[VAR1]], !DIExpression(), debug-location !DILocation(line: 4
; ALL: %4:_(s32) = G_SUB %3, %1, debug-location !DILocation(line: 5, column: 1, scope: !6)
; VALUE: DBG_VALUE %4(s32), $noreg, [[VAR1]], !DIExpression(), debug-location !DILocation(line: 5
...

0 comments on commit 2a5675f

Please sign in to comment.