Skip to content

Commit

Permalink
[LiveDebugValues] Emit the debug entry values
Browse files Browse the repository at this point in the history
Emit replacements for clobbered parameters location if the parameter
has unmodified value throughout the funciton. This is basic scenario
where we can use the debug entry values.

([12/13] Introduce the debug entry values.)

Co-authored-by: Ananth Sowda <asowda@cisco.com>
Co-authored-by: Nikola Prica <nikola.prica@rt-rk.com>
Co-authored-by: Ivan Baev <ibaev@cisco.com>

Differential Revision: https://reviews.llvm.org/D58042

llvm-svn: 364553
  • Loading branch information
djtodoro committed Jun 27, 2019
1 parent 92b32a9 commit d6a46af
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 20 deletions.
159 changes: 139 additions & 20 deletions llvm/lib/CodeGen/LiveDebugValues.cpp
Expand Up @@ -39,6 +39,7 @@
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/Config/llvm-config.h"
Expand Down Expand Up @@ -185,7 +186,8 @@ class LiveDebugValues : public MachineFunctionPass {
InvalidKind = 0,
RegisterKind,
SpillLocKind,
ImmediateKind
ImmediateKind,
EntryValueKind
} Kind = InvalidKind;

/// The value location. Stored separately to avoid repeatedly
Expand All @@ -199,14 +201,18 @@ class LiveDebugValues : public MachineFunctionPass {
const ConstantInt *CImm;
} Loc;

VarLoc(const MachineInstr &MI, LexicalScopes &LS)
: Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) {
VarLoc(const MachineInstr &MI, LexicalScopes &LS,
VarLocKind K = InvalidKind)
: Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS), Kind(K) {
static_assert((sizeof(Loc) == sizeof(uint64_t)),
"hash does not cover all members of Loc");
assert(MI.isDebugValue() && "not a DBG_VALUE");
assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE");
if (int RegNo = isDbgValueDescribedByReg(MI)) {
Kind = RegisterKind;
assert((!EntryValueKind || RegNo) &&
"entry values must be register locations");
if (Kind != EntryValueKind)
Kind = RegisterKind;
Loc.RegNo = RegNo;
} else if (MI.getOperand(0).isImm()) {
Kind = ImmediateKind;
Expand Down Expand Up @@ -241,6 +247,10 @@ class LiveDebugValues : public MachineFunctionPass {
return 0;
}

bool isEntryValueKind() const {
return Kind == EntryValueKind;
}

/// Determine whether the lexical scope of this value's debug location
/// dominates MBB.
bool dominates(MachineBasicBlock &MBB) const { return UVS.dominates(&MBB); }
Expand All @@ -256,12 +266,12 @@ class LiveDebugValues : public MachineFunctionPass {

/// This operator guarantees that VarLocs are sorted by Variable first.
bool operator<(const VarLoc &Other) const {
if (Var == Other.Var)
return Loc.Hash < Other.Loc.Hash;
return Var < Other.Var;
return std::tie(Var, Kind, Loc.Hash) <
std::tie(Other.Var, Other.Kind, Other.Loc.Hash);
}
};

using DebugParamMap = SmallDenseMap<const DILocalVariable *, MachineInstr *>;
using VarLocMap = UniqueVector<VarLoc>;
using VarLocSet = SparseBitVector<>;
using VarLocInMBB = SmallDenseMap<const MachineBasicBlock *, VarLocSet>;
Expand Down Expand Up @@ -347,17 +357,23 @@ class LiveDebugValues : public MachineFunctionPass {
VarLocMap &VarLocIDs);
void transferSpillOrRestoreInst(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs, TransferMap &Transfers);
void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs, TransferMap &Transfers,
DebugParamMap &DebugEntryVals,
SparseBitVector<> &KillSet);
void transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs, TransferMap &Transfers);
void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges,
const VarLocMap &VarLocIDs);
VarLocMap &VarLocIDs, TransferMap &Transfers,
DebugParamMap &DebugEntryVals);
bool transferTerminatorInst(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs);

bool process(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
TransferMap &Transfers, bool transferChanges,
OverlapMap &OverlapFragments, VarToFragments &SeenFragments);
TransferMap &Transfers, DebugParamMap &DebugEntryVals,
bool transferChanges, OverlapMap &OverlapFragments,
VarToFragments &SeenFragments);

void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments,
OverlapMap &OLapMap);
Expand Down Expand Up @@ -548,13 +564,26 @@ void LiveDebugValues::transferDebugValue(const MachineInstr &MI,
OpenRanges.erase(V);

// Add the VarLoc to OpenRanges from this DBG_VALUE.
VarLoc::VarLocKind Kind = VarLoc::InvalidKind;
unsigned ID;
if (isDbgValueDescribedByReg(MI) || MI.getOperand(0).isImm() ||
MI.getOperand(0).isFPImm() || MI.getOperand(0).isCImm()) {

auto FinishVarLoc = [&] (const MachineInstr &MI, VarLoc::VarLocKind &Kind) {
// Use normal VarLoc constructor for registers and immediates.
VarLoc VL(MI, LS);
VarLoc VL(MI, LS, Kind);
ID = VarLocIDs.insert(VL);
OpenRanges.insert(ID, VL.Var);
};

if (isDbgValueDescribedByReg(MI)) {
Kind = VarLoc::RegisterKind;
const DIExpression *Expr = MI.getDebugExpression();
if (Expr->isEntryValue())
Kind = VarLoc::EntryValueKind;
FinishVarLoc(MI, Kind);
} else if (MI.getOperand(0).isImm() ||
MI.getOperand(0).isFPImm() || MI.getOperand(0).isCImm()) {
Kind = VarLoc::ImmediateKind;
FinishVarLoc(MI, Kind);
} else if (MI.hasOneMemOperand()) {
// It's a stack spill -- fetch spill base and offset.
VarLoc::SpillLoc SpillLocation = extractSpillBaseRegAndOffset(MI);
Expand All @@ -568,6 +597,44 @@ void LiveDebugValues::transferDebugValue(const MachineInstr &MI,
}
}

void LiveDebugValues::emitEntryValues(MachineInstr &MI,
OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs,
TransferMap &Transfers,
DebugParamMap &DebugEntryVals,
SparseBitVector<> &KillSet) {
MachineFunction *MF = MI.getParent()->getParent();
for (unsigned ID : KillSet) {
if (!VarLocIDs[ID].Var.getVar()->isParameter())
continue;

const MachineInstr *CurrDebugInstr = &VarLocIDs[ID].MI;

// If parameter's DBG_VALUE is not in the map that means we can't
// generate parameter's entry value.
if (!DebugEntryVals.count(CurrDebugInstr->getDebugVariable()))
continue;

auto ParamDebugInstr = DebugEntryVals[CurrDebugInstr->getDebugVariable()];
DIExpression *NewExpr = DIExpression::prepend(
ParamDebugInstr->getDebugExpression(), DIExpression::EntryValue);
MachineInstr *EntryValDbgMI =
BuildMI(*MF, ParamDebugInstr->getDebugLoc(), ParamDebugInstr->getDesc(),
ParamDebugInstr->isIndirectDebugValue(),
ParamDebugInstr->getOperand(0).getReg(),
ParamDebugInstr->getDebugVariable(), NewExpr);

if (ParamDebugInstr->isIndirectDebugValue())
EntryValDbgMI->getOperand(1).setImm(
ParamDebugInstr->getOperand(1).getImm());

Transfers.push_back({&MI, EntryValDbgMI});
VarLoc VL(*EntryValDbgMI, LS, VarLoc::EntryValueKind);
unsigned EntryValLocID = VarLocIDs.insert(VL);
OpenRanges.insert(EntryValLocID, VL.Var);
}
}

/// Create new TransferDebugPair and insert it in \p Transfers. The VarLoc
/// with \p OldVarID should be deleted form \p OpenRanges and replaced with
/// new VarLoc. If \p NewReg is different than default zero value then the
Expand Down Expand Up @@ -658,9 +725,9 @@ void LiveDebugValues::insertTransferDebugPair(
}

/// A definition of a register may mark the end of a range.
void LiveDebugValues::transferRegisterDef(MachineInstr &MI,
OpenRangesSet &OpenRanges,
const VarLocMap &VarLocIDs) {
void LiveDebugValues::transferRegisterDef(
MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs,
TransferMap &Transfers, DebugParamMap &DebugEntryVals) {
MachineFunction *MF = MI.getMF();
const TargetLowering *TLI = MF->getSubtarget().getTargetLowering();
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
Expand Down Expand Up @@ -690,6 +757,13 @@ void LiveDebugValues::transferRegisterDef(MachineInstr &MI,
}
}
OpenRanges.erase(KillSet, VarLocIDs);

if (auto *TPC = getAnalysisIfAvailable<TargetPassConfig>()) {
auto &TM = TPC->getTM<TargetMachine>();
if (TM.Options.EnableDebugEntryValues)
emitEntryValues(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals,
KillSet);
}
}

/// Decide if @MI is a spill instruction and return true if it is. We use 2
Expand Down Expand Up @@ -941,12 +1015,14 @@ void LiveDebugValues::accumulateFragmentMap(MachineInstr &MI,
/// This routine creates OpenRanges and OutLocs.
bool LiveDebugValues::process(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
TransferMap &Transfers, bool transferChanges,
TransferMap &Transfers, DebugParamMap &DebugEntryVals,
bool transferChanges,
OverlapMap &OverlapFragments,
VarToFragments &SeenFragments) {
bool Changed = false;
transferDebugValue(MI, OpenRanges, VarLocIDs);
transferRegisterDef(MI, OpenRanges, VarLocIDs);
transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers,
DebugEntryVals);
if (transferChanges) {
transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers);
transferSpillOrRestoreInst(MI, OpenRanges, VarLocIDs, Transfers);
Expand Down Expand Up @@ -1100,16 +1176,58 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {

enum : bool { dontTransferChanges = false, transferChanges = true };

// Besides parameter's modification, check whether a DBG_VALUE is inlined
// in order to deduce whether the variable that it tracks comes from
// a different function. If that is the case we can't track its entry value.
auto IsUnmodifiedFuncParam = [&](const MachineInstr &MI) {
auto *DIVar = MI.getDebugVariable();
return DIVar->isParameter() && DIVar->isNotModified() &&
!MI.getDebugLoc()->getInlinedAt();
};

const TargetLowering *TLI = MF.getSubtarget().getTargetLowering();
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
unsigned FP = TRI->getFrameRegister(MF);
auto IsRegOtherThanSPAndFP = [&](const MachineOperand &Op) -> bool {
return Op.isReg() && Op.getReg() != SP && Op.getReg() != FP;
};

// Working set of currently collected debug variables mapped to DBG_VALUEs
// representing candidates for production of debug entry values.
DebugParamMap DebugEntryVals;

MachineBasicBlock &First_MBB = *(MF.begin());
// Only in the case of entry MBB collect DBG_VALUEs representing
// function parameters in order to generate debug entry values for them.
// Currently, we generate debug entry values only for parameters that are
// unmodified throughout the function and located in a register.
// TODO: Add support for parameters that are described as fragments.
// TODO: Add support for modified arguments that can be expreseed
// by using its entry value.
for (auto &MI : First_MBB)
if (MI.isDebugValue() && IsUnmodifiedFuncParam(MI) &&
!MI.isIndirectDebugValue() && IsRegOtherThanSPAndFP(MI.getOperand(0)) &&
!DebugEntryVals.count(MI.getDebugVariable()) &&
!MI.getDebugExpression()->isFragment())
DebugEntryVals[MI.getDebugVariable()] = &MI;

// Initialize every mbb with OutLocs.
// We are not looking at any spill instructions during the initial pass
// over the BBs. The LiveDebugVariables pass has already created DBG_VALUE
// instructions for spills of registers that are known to be user variables
// within the BB in which the spill occurs.
for (auto &MBB : MF) {
for (auto &MI : MBB) {
process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers,
process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers, DebugEntryVals,
dontTransferChanges, OverlapFragments, SeenFragments);
}
// Add any entry DBG_VALUE instructions necessitated by parameter
// clobbering.
for (auto &TR : Transfers) {
MBB.insertAfter(MachineBasicBlock::iterator(*TR.TransferInst),
TR.DebugInst);
}
Transfers.clear();
}

auto hasNonArtificialLocation = [](const MachineInstr &MI) -> bool {
Expand Down Expand Up @@ -1158,7 +1276,8 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
for (auto &MI : *MBB)
OLChanged |=
process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers,
transferChanges, OverlapFragments, SeenFragments);
DebugEntryVals, transferChanges, OverlapFragments,
SeenFragments);

// Add any DBG_VALUE instructions necessitated by spills.
for (auto &TR : Transfers)
Expand Down
79 changes: 79 additions & 0 deletions llvm/test/DebugInfo/MIR/X86/dbginfo-entryvals.mir
@@ -0,0 +1,79 @@
# RUN: llc -debug-entry-values -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s
#
#extern void fn2(int);
#
#__attribute__((noinline))
#void
#fn1 (int x, int y) {
# int u = x + y;
# if (x > 1)
# u += 1;
# else
# u += 2;
# int a = 7;
# fn2 (a);
# u --;
#}
# CHECK: DBG_VALUE $edi, $noreg, !14, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}}
# CHECK: DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}}

--- |
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

; Function Attrs: noinline nounwind uwtable
define dso_local void @fn1(i32 %x, i32 %y) local_unnamed_addr !dbg !9 {
entry:
call void @llvm.dbg.value(metadata i32 %x, metadata !14, metadata !DIExpression()), !dbg !18
call void @llvm.dbg.value(metadata i32 %y, metadata !15, metadata !DIExpression()), !dbg !18
call void @llvm.dbg.value(metadata i32 7, metadata !17, metadata !DIExpression()), !dbg !18
tail call void @fn2(i32 7), !dbg !18
ret void, !dbg !18
}

declare !dbg !4 dso_local void @fn2(i32) local_unnamed_addr

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata)


!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5, !6, !7}
!llvm.ident = !{!8}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
!1 = !DIFile(filename: "test.c", directory: "/dir")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "fn2", scope: !1, file: !1, line: 11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !{i32 2, !"Dwarf Version", i32 4}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = !{i32 1, !"wchar_size", i32 4}
!8 = !{!"clang version 9.0.0"}
!9 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 15, type: !10, scopeLine: 15, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13)
!10 = !DISubroutineType(types: !11)
!11 = !{null, !12, !12}
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !{!14, !15, !16, !17}
!14 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !1, line: 15, type: !12, flags: DIFlagArgumentNotModified)
!15 = !DILocalVariable(name: "y", arg: 2, scope: !9, file: !1, line: 15, type: !12, flags: DIFlagArgumentNotModified)
!16 = !DILocalVariable(name: "u", scope: !9, file: !1, line: 16, type: !12, flags: DIFlagArgumentNotModified)
!17 = !DILocalVariable(name: "a", scope: !9, file: !1, line: 21, type: !12, flags: DIFlagArgumentNotModified)
!18 = !DILocation(line: 15, column: 10, scope: !9)

...
---
name: fn1
alignment: 4
tracksRegLiveness: true
liveins: []
body: |
bb.0.entry:
DBG_VALUE $edi, $noreg, !14, !DIExpression(), debug-location !18
DBG_VALUE $esi, $noreg, !15, !DIExpression(), debug-location !18
DBG_VALUE 7, $noreg, !17, !DIExpression(), debug-location !18
$edi = MOV32ri 7, debug-location !18
TAILJMPd64 @fn2, csr_64, implicit $rsp, implicit $ssp, implicit $rsp, implicit $ssp, implicit killed $edi, debug-location !18
...

0 comments on commit d6a46af

Please sign in to comment.