mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-26 12:43:36 +01:00
[DebugInstrRef] Convert DBG_INSTR_REFs into variable locations
Handle DBG_INSTR_REF instructions in LiveDebugValues, to determine and propagate variable locations. The logic is fairly straight forwards: Collect a map of debug-instruction-number to the machine value numbers generated in the first walk through the function. When building the variable value transfer function and we see a DBG_INSTR_REF, look up the instruction it refers to, and pick the machine value number it generates, That's it; the rest of LiveDebugValues continues as normal. Awkwardly, there are two kinds of instruction numbering happening here: the offset into the block (which is how machine value numbers are determined), and the numbers that we label instructions with when generating DBG_INSTR_REFs. I've also restructured the TransferTracker redefVar code a little, to separate some DBG_VALUE specific operations into its own method. The changes around redefVar should be largely NFC, while allowing DBG_INSTR_REFs to specify a value number rather than just a location. Differential Revision: https://reviews.llvm.org/D85771
This commit is contained in:
parent
45a7f324f8
commit
7b0445fed2
@ -900,12 +900,11 @@ public:
|
||||
public:
|
||||
VLocTracker() {}
|
||||
|
||||
void defVar(const MachineInstr &MI, Optional<ValueIDNum> ID) {
|
||||
// XXX skipping overlapping fragments for now.
|
||||
assert(MI.isDebugValue());
|
||||
void defVar(const MachineInstr &MI, const DbgValueProperties &Properties,
|
||||
Optional<ValueIDNum> ID) {
|
||||
assert(MI.isDebugValue() || MI.isDebugRef());
|
||||
DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
|
||||
MI.getDebugLoc()->getInlinedAt());
|
||||
DbgValueProperties Properties(MI);
|
||||
DbgValue Rec = (ID) ? DbgValue(*ID, Properties, DbgValue::Def)
|
||||
: DbgValue(Properties, DbgValue::Undef);
|
||||
|
||||
@ -917,7 +916,7 @@ public:
|
||||
}
|
||||
|
||||
void defVar(const MachineInstr &MI, const MachineOperand &MO) {
|
||||
// XXX skipping overlapping fragments for now.
|
||||
// Only DBG_VALUEs can define constant-valued variables.
|
||||
assert(MI.isDebugValue());
|
||||
DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
|
||||
MI.getDebugLoc()->getInlinedAt());
|
||||
@ -1083,50 +1082,64 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a DBG_VALUE within a block. Terminate the variables current
|
||||
/// location, and record the value its DBG_VALUE refers to, so that we can
|
||||
/// detect location transfers later on.
|
||||
/// Change a variable value after encountering a DBG_VALUE inside a block.
|
||||
void redefVar(const MachineInstr &MI) {
|
||||
DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
|
||||
MI.getDebugLoc()->getInlinedAt());
|
||||
DbgValueProperties Properties(MI);
|
||||
|
||||
const MachineOperand &MO = MI.getOperand(0);
|
||||
|
||||
// Erase any previous location,
|
||||
auto It = ActiveVLocs.find(Var);
|
||||
if (It != ActiveVLocs.end()) {
|
||||
ActiveMLocs[It->second.Loc].erase(Var);
|
||||
}
|
||||
|
||||
// Insert a new variable location. Ignore non-register locations, we don't
|
||||
// transfer those, and can't currently describe spill locs independently of
|
||||
// regs.
|
||||
// (This is because a spill location is a DBG_VALUE of the stack pointer).
|
||||
// Ignore non-register locations, we don't transfer those.
|
||||
if (!MO.isReg() || MO.getReg() == 0) {
|
||||
if (It != ActiveVLocs.end())
|
||||
auto It = ActiveVLocs.find(Var);
|
||||
if (It != ActiveVLocs.end()) {
|
||||
ActiveMLocs[It->second.Loc].erase(Var);
|
||||
ActiveVLocs.erase(It);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Register Reg = MO.getReg();
|
||||
LocIdx MLoc = MTracker->getRegMLoc(Reg);
|
||||
DbgValueProperties Properties(MI);
|
||||
LocIdx NewLoc = MTracker->getRegMLoc(Reg);
|
||||
redefVar(MI, Properties, NewLoc);
|
||||
}
|
||||
|
||||
/// Handle a change in variable location within a block. Terminate the
|
||||
/// variables current location, and record the value it now refers to, so
|
||||
/// that we can detect location transfers later on.
|
||||
void redefVar(const MachineInstr &MI, const DbgValueProperties &Properties,
|
||||
Optional<LocIdx> OptNewLoc) {
|
||||
DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
|
||||
MI.getDebugLoc()->getInlinedAt());
|
||||
|
||||
// Erase any previous location,
|
||||
auto It = ActiveVLocs.find(Var);
|
||||
if (It != ActiveVLocs.end())
|
||||
ActiveMLocs[It->second.Loc].erase(Var);
|
||||
|
||||
// If there _is_ no new location, all we had to do was erase.
|
||||
if (!OptNewLoc)
|
||||
return;
|
||||
LocIdx NewLoc = *OptNewLoc;
|
||||
|
||||
// Check whether our local copy of values-by-location in #VarLocs is out of
|
||||
// date. Wipe old tracking data for the location if it's been clobbered in
|
||||
// the meantime.
|
||||
if (MTracker->getNumAtPos(MLoc) != VarLocs[MLoc.asU64()]) {
|
||||
for (auto &P : ActiveMLocs[MLoc.asU64()]) {
|
||||
if (MTracker->getNumAtPos(NewLoc) != VarLocs[NewLoc.asU64()]) {
|
||||
for (auto &P : ActiveMLocs[NewLoc]) {
|
||||
ActiveVLocs.erase(P);
|
||||
}
|
||||
ActiveMLocs[MLoc].clear();
|
||||
VarLocs[MLoc.asU64()] = MTracker->getNumAtPos(MLoc);
|
||||
ActiveMLocs[NewLoc.asU64()].clear();
|
||||
VarLocs[NewLoc.asU64()] = MTracker->getNumAtPos(NewLoc);
|
||||
}
|
||||
|
||||
ActiveMLocs[MLoc].insert(Var);
|
||||
ActiveMLocs[NewLoc].insert(Var);
|
||||
if (It == ActiveVLocs.end()) {
|
||||
ActiveVLocs.insert(std::make_pair(Var, LocAndProperties{MLoc, Properties}));
|
||||
ActiveVLocs.insert(
|
||||
std::make_pair(Var, LocAndProperties{NewLoc, Properties}));
|
||||
} else {
|
||||
It->second.Loc = MLoc;
|
||||
It->second.Loc = NewLoc;
|
||||
It->second.Properties = Properties;
|
||||
}
|
||||
}
|
||||
@ -1272,6 +1285,13 @@ private:
|
||||
DenseMap<MachineBasicBlock *, unsigned int> BBToOrder;
|
||||
DenseMap<unsigned, unsigned> BBNumToRPO;
|
||||
|
||||
/// Pair of MachineInstr, and its 1-based offset into the containing block.
|
||||
typedef std::pair<const MachineInstr *, unsigned> InstAndNum;
|
||||
/// Map from debug instruction number to the MachineInstr labelled with that
|
||||
/// number, and its location within the function. Used to transform
|
||||
/// instruction numbers in DBG_INSTR_REFs into machine value numbers.
|
||||
std::map<uint64_t, InstAndNum> DebugInstrNumToInstr;
|
||||
|
||||
// Map of overlapping variable fragments.
|
||||
OverlapMap OverlapFragments;
|
||||
VarToFragments SeenFragments;
|
||||
@ -1304,6 +1324,10 @@ private:
|
||||
/// \returns true if MI was recognized and processed.
|
||||
bool transferDebugValue(const MachineInstr &MI);
|
||||
|
||||
/// Examines whether \p MI is a DBG_INSTR_REF and notifies trackers.
|
||||
/// \returns true if MI was recognized and processed.
|
||||
bool transferDebugInstrRef(MachineInstr &MI);
|
||||
|
||||
/// Examines whether \p MI is copy instruction, and notifies trackers.
|
||||
/// \returns true if MI was recognized and processed.
|
||||
bool transferRegisterCopy(MachineInstr &MI);
|
||||
@ -1497,6 +1521,7 @@ bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
|
||||
"Expected inlined-at fields to agree");
|
||||
|
||||
DebugVariable V(Var, Expr, InlinedAt);
|
||||
DbgValueProperties Properties(MI);
|
||||
|
||||
// If there are no instructions in this lexical scope, do no location tracking
|
||||
// at all, this variable shouldn't get a legitimate location range.
|
||||
@ -1519,9 +1544,9 @@ bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
|
||||
// Feed defVar the new variable location, or if this is a
|
||||
// DBG_VALUE $noreg, feed defVar None.
|
||||
if (MO.getReg())
|
||||
VTracker->defVar(MI, MTracker->readReg(MO.getReg()));
|
||||
VTracker->defVar(MI, Properties, MTracker->readReg(MO.getReg()));
|
||||
else
|
||||
VTracker->defVar(MI, None);
|
||||
VTracker->defVar(MI, Properties, None);
|
||||
} else if (MI.getOperand(0).isImm() || MI.getOperand(0).isFPImm() ||
|
||||
MI.getOperand(0).isCImm()) {
|
||||
VTracker->defVar(MI, MI.getOperand(0));
|
||||
@ -1535,6 +1560,116 @@ bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI) {
|
||||
if (!MI.isDebugRef())
|
||||
return false;
|
||||
|
||||
// Only handle this instruction when we are building the variable value
|
||||
// transfer function.
|
||||
if (!VTracker)
|
||||
return false;
|
||||
|
||||
unsigned InstNo = MI.getOperand(0).getImm();
|
||||
unsigned OpNo = MI.getOperand(1).getImm();
|
||||
|
||||
const DILocalVariable *Var = MI.getDebugVariable();
|
||||
const DIExpression *Expr = MI.getDebugExpression();
|
||||
const DILocation *DebugLoc = MI.getDebugLoc();
|
||||
const DILocation *InlinedAt = DebugLoc->getInlinedAt();
|
||||
assert(Var->isValidLocationForIntrinsic(DebugLoc) &&
|
||||
"Expected inlined-at fields to agree");
|
||||
|
||||
DebugVariable V(Var, Expr, InlinedAt);
|
||||
|
||||
auto *Scope = LS.findLexicalScope(MI.getDebugLoc().get());
|
||||
if (Scope == nullptr)
|
||||
return true; // Handled by doing nothing. This variable is never in scope.
|
||||
|
||||
const MachineFunction &MF = *MI.getParent()->getParent();
|
||||
|
||||
// Various optimizations may have happened to the value during codegen,
|
||||
// recorded in the value substitution table. Apply any substitutions to
|
||||
// the instruction / operand number in this DBG_INSTR_REF.
|
||||
auto Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo));
|
||||
while (Sub != MF.DebugValueSubstitutions.end()) {
|
||||
InstNo = Sub->second.first;
|
||||
OpNo = Sub->second.second;
|
||||
Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo));
|
||||
}
|
||||
|
||||
// Default machine value number is <None> -- if no instruction defines
|
||||
// the corresponding value, it must have been optimized out.
|
||||
Optional<ValueIDNum> NewID = None;
|
||||
|
||||
// Try to lookup the instruction number, and find the machine value number
|
||||
// that it defines.
|
||||
auto InstrIt = DebugInstrNumToInstr.find(InstNo);
|
||||
if (InstrIt != DebugInstrNumToInstr.end()) {
|
||||
const MachineInstr &TargetInstr = *InstrIt->second.first;
|
||||
uint64_t BlockNo = TargetInstr.getParent()->getNumber();
|
||||
|
||||
// Pick out the designated operand.
|
||||
assert(OpNo < TargetInstr.getNumOperands());
|
||||
const MachineOperand &MO = TargetInstr.getOperand(OpNo);
|
||||
|
||||
// Today, this can only be a register.
|
||||
assert(MO.isReg() && MO.isDef());
|
||||
|
||||
unsigned LocID = MTracker->getLocID(MO.getReg(), false);
|
||||
LocIdx L = MTracker->LocIDToLocIdx[LocID];
|
||||
NewID = ValueIDNum(BlockNo, InstrIt->second.second, L);
|
||||
}
|
||||
|
||||
// We, we have a value number or None. Tell the variable value tracker about
|
||||
// it. The rest of this LiveDebugValues implementation acts exactly the same
|
||||
// for DBG_INSTR_REFs as DBG_VALUEs (just, the former can refer to values that
|
||||
// aren't immediately available).
|
||||
DbgValueProperties Properties(Expr, false);
|
||||
VTracker->defVar(MI, Properties, NewID);
|
||||
|
||||
// If we're on the final pass through the function, decompose this INSTR_REF
|
||||
// into a plain DBG_VALUE.
|
||||
if (!TTracker)
|
||||
return true;
|
||||
|
||||
// Pick a location for the machine value number, if such a location exists.
|
||||
// (This information could be stored in TransferTracker to make it faster).
|
||||
Optional<LocIdx> FoundLoc = None;
|
||||
for (auto Location : MTracker->locations()) {
|
||||
LocIdx CurL = Location.Idx;
|
||||
ValueIDNum ID = MTracker->LocIdxToIDNum[CurL];
|
||||
if (NewID && ID == NewID) {
|
||||
// If this is the first location with that value, pick it. Otherwise,
|
||||
// consider whether it's a "longer term" location.
|
||||
if (!FoundLoc) {
|
||||
FoundLoc = CurL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (MTracker->isSpill(CurL))
|
||||
FoundLoc = CurL; // Spills are a longer term location.
|
||||
else if (!MTracker->isSpill(*FoundLoc) &&
|
||||
!MTracker->isSpill(CurL) &&
|
||||
!isCalleeSaved(*FoundLoc) &&
|
||||
isCalleeSaved(CurL))
|
||||
FoundLoc = CurL; // Callee saved regs are longer term than normal.
|
||||
}
|
||||
}
|
||||
|
||||
// Tell transfer tracker that the variable value has changed.
|
||||
TTracker->redefVar(MI, Properties, FoundLoc);
|
||||
|
||||
// Produce a DBG_VALUE representing what this DBG_INSTR_REF meant.
|
||||
// This DBG_VALUE is potentially a $noreg / undefined location, if
|
||||
// FoundLoc is None.
|
||||
// (XXX -- could morph the DBG_INSTR_REF in the future).
|
||||
MachineInstr *DbgMI = MTracker->emitLoc(FoundLoc, V, Properties);
|
||||
TTracker->PendingDbgValues.push_back(DbgMI);
|
||||
TTracker->flushDbgValues(MI.getIterator(), nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
|
||||
// Meta Instructions do not affect the debug liveness of any register they
|
||||
// define.
|
||||
@ -1916,6 +2051,8 @@ void InstrRefBasedLDV::process(MachineInstr &MI) {
|
||||
// definitions.
|
||||
if (transferDebugValue(MI))
|
||||
return;
|
||||
if (transferDebugInstrRef(MI))
|
||||
return;
|
||||
if (transferRegisterCopy(MI))
|
||||
return;
|
||||
if (transferSpillOrRestoreInst(MI))
|
||||
@ -1960,6 +2097,20 @@ void InstrRefBasedLDV::produceMLocTransferFunction(
|
||||
// Also accumulate fragment map.
|
||||
if (MI.isDebugValue())
|
||||
accumulateFragmentMap(MI);
|
||||
|
||||
// Create a map from the instruction number (if present) to the
|
||||
// MachineInstr and its position.
|
||||
if (MI.peekDebugInstrNum()) {
|
||||
uint64_t InstrNo = MI.peekDebugInstrNum();
|
||||
auto InstrAndPos = std::make_pair(&MI, CurInst);
|
||||
auto InsertResult =
|
||||
DebugInstrNumToInstr.insert(std::make_pair(InstrNo, InstrAndPos));
|
||||
|
||||
// There should never be duplicate instruction numbers.
|
||||
assert(InsertResult.second);
|
||||
(void)InsertResult;
|
||||
}
|
||||
|
||||
++CurInst;
|
||||
}
|
||||
|
||||
@ -3123,6 +3274,7 @@ bool InstrRefBasedLDV::ExtendRanges(MachineFunction &MF,
|
||||
OrderToBB.clear();
|
||||
BBToOrder.clear();
|
||||
BBNumToRPO.clear();
|
||||
DebugInstrNumToInstr.clear();
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
--- |
|
||||
; RUN: llc %s -march=x86-64 -run-pass=livedebugvalues -o - -experimental-debug-variable-locations | FileCheck %s -implicit-check-not=DBG_VALUE
|
||||
|
||||
define i32 @_Z8bb_to_bb() local_unnamed_addr !dbg !12 {
|
||||
entry:
|
||||
ret i32 0, !dbg !17
|
||||
}
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
!llvm.module.flags = !{!7, !8, !9, !10}
|
||||
!llvm.ident = !{!11}
|
||||
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, debugInfoForProfiling: true, nameTableKind: None)
|
||||
!1 = !DIFile(filename: "main.cpp", directory: "F:\")
|
||||
!2 = !{}
|
||||
!3 = !{!4}
|
||||
!4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression())
|
||||
!5 = distinct !DIGlobalVariable(name: "start", scope: !0, file: !1, line: 4, type: !6, isLocal: false, isDefinition: true)
|
||||
!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||
!7 = !{i32 2, !"Dwarf Version", i32 4}
|
||||
!8 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
!9 = !{i32 1, !"wchar_size", i32 2}
|
||||
!10 = !{i32 7, !"PIC Level", i32 2}
|
||||
!11 = !{!"clang version 10.0.0"}
|
||||
!12 = distinct !DISubprogram(name: "bb_to_bb", linkageName: "bb_to_bb", scope: !1, file: !1, line: 6, type: !13, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15)
|
||||
!13 = !DISubroutineType(types: !14)
|
||||
!14 = !{!6, !6}
|
||||
!15 = !{!16}
|
||||
!16 = !DILocalVariable(name: "myVar", scope: !12, file: !1, line: 7, type: !6)
|
||||
!17 = !DILocation(line: 10, scope: !12)
|
||||
|
||||
...
|
||||
---
|
||||
name: _Z8bb_to_bb
|
||||
debugValueSubstitutions:
|
||||
- { srcinst: 4, srcop: 0, dstinst: 3, dstop: 0 }
|
||||
body: |
|
||||
bb.0.entry:
|
||||
$rax = MOV64ri 1, debug-instr-number 1, debug-location !17
|
||||
; This debug instruction should identify the value as being in $rax.
|
||||
DBG_INSTR_REF 1, 0, !16, !DIExpression(), debug-location !17
|
||||
; CHECK: DBG_VALUE $rax, $noreg
|
||||
|
||||
$rbx = COPY killed $rax, debug-location !17
|
||||
$rax = MOV64ri 1, debug-location !17
|
||||
; Presently, this COPY isn't followed. Dealing with that is future work.
|
||||
|
||||
DBG_INSTR_REF 2, 0, !16, !DIExpression(), debug-location !17
|
||||
; No instruction is labelled with the number "2". This should produce an
|
||||
; empty variable location.
|
||||
; CHECK: DBG_VALUE $noreg, $noreg
|
||||
|
||||
$rbx = MOV64ri 1, debug-instr-number 3, debug-location !17
|
||||
JMP_1 %bb.1
|
||||
|
||||
|
||||
; CHECK-LABEL: bb.1:
|
||||
bb.1:
|
||||
|
||||
DBG_INSTR_REF 3, 0, !16, !DIExpression(), debug-location !17
|
||||
; This refers to a value def'd in a parent block -- but it should be
|
||||
; tracked into this block.
|
||||
; CHECK: DBG_VALUE $rbx, $noreg
|
||||
JMP_1 %bb.2
|
||||
|
||||
; CHECK-LABEL: bb.2:
|
||||
bb.2:
|
||||
; Just like any other variable location, live-ins should be created for
|
||||
; any successor blocks.
|
||||
; CHECK: DBG_VALUE $rbx, $noreg
|
||||
|
||||
RETQ $eax, debug-location !17
|
||||
...
|
Loading…
Reference in New Issue
Block a user