Skip to content

Commit

Permalink
[DebugInstrRef] Create DBG_INSTR_REFs in SelectionDAG
Browse files Browse the repository at this point in the history
When given the -experimental-debug-variable-locations option (via -Xclang
or to llc), have SelectionDAG generate DBG_INSTR_REF instructions instead
of DBG_VALUE. For now, this only happens in a limited circumstance: when
the value referred to is not a PHI and is defined in the current block.
Other situations introduce interesting problems, addresed in later patches.

Practically, this patch hooks into InstrEmitter and if it can find a
defining instruction for a value, gives it an instruction number, and
points the DBG_INSTR_REF at that <instr, operand> pair.

Differential Revision: https://reviews.llvm.org/D85747
  • Loading branch information
jmorse committed Oct 14, 2020
1 parent e6b4179 commit c4e7857
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 5 deletions.
68 changes: 66 additions & 2 deletions llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,11 @@ InstrEmitter::EmitDbgValue(SDDbgValue *SD,
return &*MIB;
}

// Attempt to produce a DBG_INSTR_REF if we've been asked to.
if (EmitDebugInstrRefs)
if (auto *InstrRef = EmitDbgInstrRef(SD, VRBaseMap))
return InstrRef;

if (SD->getKind() == SDDbgValue::FRAMEIX) {
// Stack address; this needs to be lowered in target-dependent fashion.
// EmitTargetCodeForFrameDebugValue is responsible for allocation.
Expand Down Expand Up @@ -761,6 +766,63 @@ InstrEmitter::EmitDbgValue(SDDbgValue *SD,
return &*MIB;
}

MachineInstr *
InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD,
DenseMap<SDValue, Register> &VRBaseMap) {
// Instruction referencing is still in a prototype state: for now we're only
// going to support SDNodes within a block. Copies are not supported, they
// don't actually define a value.
if (SD->getKind() != SDDbgValue::SDNODE)
return nullptr;

SDNode *Node = SD->getSDNode();
SDValue Op = SDValue(Node, SD->getResNo());
DenseMap<SDValue, Register>::iterator I = VRBaseMap.find(Op);
if (I==VRBaseMap.end())
return nullptr; // undef value: let EmitDbgValue produce a DBG_VALUE $noreg.

MDNode *Var = SD->getVariable();
MDNode *Expr = SD->getExpression();
DebugLoc DL = SD->getDebugLoc();

// Try to pick out a defining instruction at this point.
unsigned VReg = getVR(Op, VRBaseMap);
MachineInstr *ResultInstr = nullptr;

// No definition corresponds to scenarios where a vreg is live-in to a block,
// and doesn't have a defining instruction (yet). This can be patched up
// later; at this early stage of implementation, fall back to using DBG_VALUE.
if (!MRI->hasOneDef(VReg))
return nullptr;

MachineInstr &DefMI = *MRI->def_instr_begin(VReg);
// Some target specific opcodes can become copies. As stated above, we're
// ignoring those for now.
if (DefMI.isCopy() || DefMI.getOpcode() == TargetOpcode::SUBREG_TO_REG)
return nullptr;

const MCInstrDesc &RefII = TII->get(TargetOpcode::DBG_INSTR_REF);
auto MIB = BuildMI(*MF, DL, RefII);

// Find the operand which defines the specified VReg.
unsigned OperandIdx = 0;
for (const auto &MO : DefMI.operands()) {
if (MO.isReg() && MO.isDef() && MO.getReg() == VReg)
break;
++OperandIdx;
}
assert(OperandIdx < DefMI.getNumOperands());

// Make the DBG_INSTR_REF refer to that instruction, and that operand.
unsigned InstrNum = DefMI.getDebugInstrNum();
MIB.addImm(InstrNum);
MIB.addImm(OperandIdx);
MIB.addMetadata(Var);
MIB.addMetadata(Expr);
ResultInstr = &*MIB;
return ResultInstr;
}

MachineInstr *
InstrEmitter::EmitDbgLabel(SDDbgLabel *SD) {
MDNode *Label = SD->getLabel();
Expand Down Expand Up @@ -1177,10 +1239,12 @@ EmitSpecialNode(SDNode *Node, bool IsClone, bool IsCloned,

/// InstrEmitter - Construct an InstrEmitter and set it to start inserting
/// at the given position in the given block.
InstrEmitter::InstrEmitter(MachineBasicBlock *mbb,
InstrEmitter::InstrEmitter(const TargetMachine &TM, MachineBasicBlock *mbb,
MachineBasicBlock::iterator insertpos)
: MF(mbb->getParent()), MRI(&MF->getRegInfo()),
TII(MF->getSubtarget().getInstrInfo()),
TRI(MF->getSubtarget().getRegisterInfo()),
TLI(MF->getSubtarget().getTargetLowering()), MBB(mbb),
InsertPos(insertpos) {}
InsertPos(insertpos) {
EmitDebugInstrRefs = TM.Options.ValueTrackingVariableLocations;
}
11 changes: 10 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class LLVM_LIBRARY_VISIBILITY InstrEmitter {
MachineBasicBlock *MBB;
MachineBasicBlock::iterator InsertPos;

/// Should we try to produce DBG_INSTR_REF instructions?
bool EmitDebugInstrRefs;

/// EmitCopyFromReg - Generate machine code for an CopyFromReg node or an
/// implicit physical register output.
void EmitCopyFromReg(SDNode *Node, unsigned ResNo,
Expand Down Expand Up @@ -109,6 +112,11 @@ class LLVM_LIBRARY_VISIBILITY InstrEmitter {
MachineInstr *EmitDbgValue(SDDbgValue *SD,
DenseMap<SDValue, Register> &VRBaseMap);

/// Attempt to emit a dbg_value as a DBG_INSTR_REF. May fail and return
/// nullptr, in which case we fall back to plain EmitDbgValue.
MachineInstr *EmitDbgInstrRef(SDDbgValue *SD,
DenseMap<SDValue, Register> &VRBaseMap);

/// Generate machine instruction for a dbg_label node.
MachineInstr *EmitDbgLabel(SDDbgLabel *SD);

Expand All @@ -130,7 +138,8 @@ class LLVM_LIBRARY_VISIBILITY InstrEmitter {

/// InstrEmitter - Construct an InstrEmitter and set it to start inserting
/// at the given position in the given block.
InstrEmitter(MachineBasicBlock *mbb, MachineBasicBlock::iterator insertpos);
InstrEmitter(const TargetMachine &TM, MachineBasicBlock *mbb,
MachineBasicBlock::iterator insertpos);

private:
void EmitMachineNode(SDNode *Node, bool IsClone, bool IsCloned,
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/SelectionDAG/ScheduleDAGFast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ void ScheduleDAGLinearize::Schedule() {

MachineBasicBlock*
ScheduleDAGLinearize::EmitSchedule(MachineBasicBlock::iterator &InsertPos) {
InstrEmitter Emitter(BB, InsertPos);
InstrEmitter Emitter(DAG->getTarget(), BB, InsertPos);
DenseMap<SDValue, Register> VRBaseMap;

LLVM_DEBUG({ dbgs() << "\n*** Final schedule ***\n"; });
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ EmitPhysRegCopy(SUnit *SU, DenseMap<SUnit*, Register> &VRBaseMap,
/// not necessarily refer to returned BB. The emitter may split blocks.
MachineBasicBlock *ScheduleDAGSDNodes::
EmitSchedule(MachineBasicBlock::iterator &InsertPos) {
InstrEmitter Emitter(BB, InsertPos);
InstrEmitter Emitter(DAG->getTarget(), BB, InsertPos);
DenseMap<SDValue, Register> VRBaseMap;
DenseMap<SUnit*, Register> CopyVRBaseMap;
SmallVector<std::pair<unsigned, MachineInstr*>, 32> Orders;
Expand Down
54 changes: 54 additions & 0 deletions llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
; RUN: llc %s -mtriple=x86_64-unknown-unknown -o - -stop-before=finalize-isel \
; RUN: | FileCheck %s --check-prefix=NORMAL \
; RUN: --implicit-check-not=debug-instr-number \
; RUN: --implicit-check-not=DBG_INSTR_REF
; RUN: llc %s -mtriple=x86_64-unknown-unknown -o - -stop-before=finalize-isel \
; RUN: -experimental-debug-variable-locations -verify-machineinstrs \
; RUN: | FileCheck %s --check-prefix=INSTRREF \
; RUN: --implicit-check-not=DBG_VALUE

; Test that SelectionDAG produces DBG_VALUEs normally, but DBG_INSTR_REFs when
; asked.

; NORMAL: %[[REG0:[0-9]+]]:gr32 = ADD32rr
; NORMAL-NEXT: DBG_VALUE %[[REG0]]
; NORMAL-NEXT: %[[REG1:[0-9]+]]:gr32 = ADD32rr
; NORMAL-NEXT: DBG_VALUE %[[REG1]]

; Note that I'm baking in an assumption of one-based ordering here. We could
; capture and check for the instruction numbers, we'd rely on machine verifier
; ensuring there were no duplicates.

; INSTRREF: ADD32rr
; INSTRREF-SAME: debug-instr-number 1
; INSTRREF-NEXT: DBG_INSTR_REF 1, 0
; INSTRREF-NEXT: ADD32rr
; INSTRREF-SAME: debug-instr-number 2
; INSTRREF-NEXT: DBG_INSTR_REF 2, 0

declare void @llvm.dbg.value(metadata, metadata, metadata)

define i32 @foo(i32 %bar, i32 %baz, i32 %qux) !dbg !7 {
entry:
%0 = add i32 %bar, %baz, !dbg !14
call void @llvm.dbg.value(metadata i32 %0, metadata !13, metadata !DIExpression()), !dbg !14
%1 = add i32 %0, %qux
call void @llvm.dbg.value(metadata i32 %1, metadata !13, metadata !DIExpression()), !dbg !14
ret i32 %1, !dbg !14
}

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "exprconflict.c", directory: "/home/jmorse")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !{!13}
!13 = !DILocalVariable(name: "baz", scope: !7, file: !1, line: 6, type: !10)
!14 = !DILocation(line: 1, scope: !7)

0 comments on commit c4e7857

Please sign in to comment.