Skip to content

Commit 3fab2d1

Browse files
committed
[DebugInfo][InstrRef] Add a max-stack-slots-to-track cut-out
In certain circumstances with things like autogenerated code and asan, you can end up with thousands of Values live at the same time, causing a large working set and a lot of information spilled to the stack. Unfortunately InstrRefBasedLDV doesn't cope well with this and consumes a lot of memory when there are many many stack slots. See the reproducer in D116821. It seems very unlikely that a developer would be able to reason about hundreds of live named local variables at the same time, so a huge working set and many stack slots is an indicator that we're likely analysing autogenerated or instrumented code. In those cases: gracefully degrade by setting an upper bound on the amount of stack slots to track. This limits peak memory consumption, at the cost of dropping some variable locations, but in a rare scenario where it's unlikely someone is actually going to use them. In terms of the patch, this adds a cl::opt for max number of stack slots to track, and has the stack-slot-numbering code optionally return None. That then filters through a number of code paths, which can then chose to not track a spill / restore if it touches an untracked spill slot. The added test checks that we drop variable locations that are on the stack, if we set the limit to zero. Differential Revision: https://reviews.llvm.org/D118601
1 parent de4e8bc commit 3fab2d1

File tree

4 files changed

+167
-45
lines changed

4 files changed

+167
-45
lines changed

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,20 @@ static cl::opt<bool> EmulateOldLDV("emulate-old-livedebugvalues", cl::Hidden,
148148
cl::desc("Act like old LiveDebugValues did"),
149149
cl::init(false));
150150

151+
// Limit for the maximum number of stack slots we should track, past which we
152+
// will ignore any spills. InstrRefBasedLDV gathers detailed information on all
153+
// stack slots which leads to high memory consumption, and in some scenarios
154+
// (such as asan with very many locals) the working set of the function can be
155+
// very large, causing many spills. In these scenarios, it is very unlikely that
156+
// the developer has hundreds of variables live at the same time that they're
157+
// carefully thinking about -- instead, they probably autogenerated the code.
158+
// When this happens, gracefully stop tracking excess spill slots, rather than
159+
// consuming all the developer's memory.
160+
static cl::opt<unsigned>
161+
StackWorkingSetLimit("livedebugvalues-max-stack-slots", cl::Hidden,
162+
cl::desc("livedebugvalues-stack-ws-limit"),
163+
cl::init(250));
164+
151165
/// Tracker for converting machine value locations and variable values into
152166
/// variable locations (the output of LiveDebugValues), recorded as DBG_VALUEs
153167
/// specifying block live-in locations and transfers within blocks.
@@ -757,9 +771,15 @@ void MLocTracker::writeRegMask(const MachineOperand *MO, unsigned CurBB,
757771
Masks.push_back(std::make_pair(MO, InstID));
758772
}
759773

760-
SpillLocationNo MLocTracker::getOrTrackSpillLoc(SpillLoc L) {
774+
Optional<SpillLocationNo> MLocTracker::getOrTrackSpillLoc(SpillLoc L) {
761775
SpillLocationNo SpillID(SpillLocs.idFor(L));
776+
762777
if (SpillID.id() == 0) {
778+
// If there is no location, and we have reached the limit of how many stack
779+
// slots to track, then don't track this one.
780+
if (SpillLocs.size() >= StackWorkingSetLimit)
781+
return None;
782+
763783
// Spill location is untracked: create record for this one, and all
764784
// subregister slots too.
765785
SpillID = SpillLocationNo(SpillLocs.insert(L));
@@ -898,7 +918,7 @@ bool InstrRefBasedLDV::isCalleeSaved(LocIdx L) const {
898918
// void InstrRefBasedLDV::printVarLocInMBB(..)
899919
#endif
900920

901-
SpillLocationNo
921+
Optional<SpillLocationNo>
902922
InstrRefBasedLDV::extractSpillBaseRegAndOffset(const MachineInstr &MI) {
903923
assert(MI.hasOneMemOperand() &&
904924
"Spill instruction does not have exactly one memory operand?");
@@ -913,8 +933,11 @@ InstrRefBasedLDV::extractSpillBaseRegAndOffset(const MachineInstr &MI) {
913933
return MTracker->getOrTrackSpillLoc({Reg, Offset});
914934
}
915935

916-
Optional<LocIdx> InstrRefBasedLDV::findLocationForMemOperand(const MachineInstr &MI) {
917-
SpillLocationNo SpillLoc = extractSpillBaseRegAndOffset(MI);
936+
Optional<LocIdx>
937+
InstrRefBasedLDV::findLocationForMemOperand(const MachineInstr &MI) {
938+
Optional<SpillLocationNo> SpillLoc = extractSpillBaseRegAndOffset(MI);
939+
if (!SpillLoc)
940+
return None;
918941

919942
// Where in the stack slot is this value defined -- i.e., what size of value
920943
// is this? An important question, because it could be loaded into a register
@@ -930,7 +953,7 @@ Optional<LocIdx> InstrRefBasedLDV::findLocationForMemOperand(const MachineInstr
930953
// occur, but the safe action is to indicate the variable is optimised out.
931954
return None;
932955

933-
unsigned SpillID = MTracker->getSpillIDWithIdx(SpillLoc, IdxIt->second);
956+
unsigned SpillID = MTracker->getSpillIDWithIdx(*SpillLoc, IdxIt->second);
934957
return MTracker->getSpillMLoc(SpillID);
935958
}
936959

@@ -1251,7 +1274,12 @@ bool InstrRefBasedLDV::transferDebugPHI(MachineInstr &MI) {
12511274
Register Base;
12521275
StackOffset Offs = TFI->getFrameIndexReference(*MI.getMF(), FI, Base);
12531276
SpillLoc SL = {Base, Offs};
1254-
SpillLocationNo SpillNo = MTracker->getOrTrackSpillLoc(SL);
1277+
Optional<SpillLocationNo> SpillNo = MTracker->getOrTrackSpillLoc(SL);
1278+
1279+
// We might be able to find a value, but have chosen not to, to avoid
1280+
// tracking too much stack information.
1281+
if (!SpillNo)
1282+
return true;
12551283

12561284
// Problem: what value should we extract from the stack? LLVM does not
12571285
// record what size the last store to the slot was, and it would become
@@ -1263,7 +1291,7 @@ bool InstrRefBasedLDV::transferDebugPHI(MachineInstr &MI) {
12631291
Optional<ValueIDNum> Result = None;
12641292
Optional<LocIdx> SpillLoc = None;
12651293
for (unsigned CS : CandidateSizes) {
1266-
unsigned SpillID = MTracker->getLocID(SpillNo, {CS, 0});
1294+
unsigned SpillID = MTracker->getLocID(*SpillNo, {CS, 0});
12671295
SpillLoc = MTracker->getSpillMLoc(SpillID);
12681296
ValueIDNum Val = MTracker->readMLoc(*SpillLoc);
12691297
// If this value was defined in it's own position, then it was probably
@@ -1280,7 +1308,7 @@ bool InstrRefBasedLDV::transferDebugPHI(MachineInstr &MI) {
12801308
// "supposed" to be is more complex, and benefits a small number of
12811309
// locations.
12821310
if (!Result) {
1283-
unsigned SpillID = MTracker->getLocID(SpillNo, {64, 0});
1311+
unsigned SpillID = MTracker->getLocID(*SpillNo, {64, 0});
12841312
SpillLoc = MTracker->getSpillMLoc(SpillID);
12851313
Result = MTracker->readMLoc(*SpillLoc);
12861314
}
@@ -1357,11 +1385,12 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
13571385

13581386
// If this instruction writes to a spill slot, def that slot.
13591387
if (hasFoldedStackStore(MI)) {
1360-
SpillLocationNo SpillNo = extractSpillBaseRegAndOffset(MI);
1361-
for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) {
1362-
unsigned SpillID = MTracker->getSpillIDWithIdx(SpillNo, I);
1363-
LocIdx L = MTracker->getSpillMLoc(SpillID);
1364-
MTracker->setMLoc(L, ValueIDNum(CurBB, CurInst, L));
1388+
if (Optional<SpillLocationNo> SpillNo = extractSpillBaseRegAndOffset(MI)) {
1389+
for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) {
1390+
unsigned SpillID = MTracker->getSpillIDWithIdx(*SpillNo, I);
1391+
LocIdx L = MTracker->getSpillMLoc(SpillID);
1392+
MTracker->setMLoc(L, ValueIDNum(CurBB, CurInst, L));
1393+
}
13651394
}
13661395
}
13671396

@@ -1396,11 +1425,12 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
13961425

13971426
// Tell TTracker about any folded stack store.
13981427
if (hasFoldedStackStore(MI)) {
1399-
SpillLocationNo SpillNo = extractSpillBaseRegAndOffset(MI);
1400-
for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) {
1401-
unsigned SpillID = MTracker->getSpillIDWithIdx(SpillNo, I);
1402-
LocIdx L = MTracker->getSpillMLoc(SpillID);
1403-
TTracker->clobberMloc(L, MI.getIterator(), true);
1428+
if (Optional<SpillLocationNo> SpillNo = extractSpillBaseRegAndOffset(MI)) {
1429+
for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) {
1430+
unsigned SpillID = MTracker->getSpillIDWithIdx(*SpillNo, I);
1431+
LocIdx L = MTracker->getSpillMLoc(SpillID);
1432+
TTracker->clobberMloc(L, MI.getIterator(), true);
1433+
}
14041434
}
14051435
}
14061436
}
@@ -1436,23 +1466,24 @@ void InstrRefBasedLDV::performCopy(Register SrcRegNum, Register DstRegNum) {
14361466
}
14371467
}
14381468

1439-
bool InstrRefBasedLDV::isSpillInstruction(const MachineInstr &MI,
1440-
MachineFunction *MF) {
1469+
Optional<SpillLocationNo>
1470+
InstrRefBasedLDV::isSpillInstruction(const MachineInstr &MI,
1471+
MachineFunction *MF) {
14411472
// TODO: Handle multiple stores folded into one.
14421473
if (!MI.hasOneMemOperand())
1443-
return false;
1474+
return None;
14441475

14451476
// Reject any memory operand that's aliased -- we can't guarantee its value.
14461477
auto MMOI = MI.memoperands_begin();
14471478
const PseudoSourceValue *PVal = (*MMOI)->getPseudoValue();
14481479
if (PVal->isAliased(MFI))
1449-
return false;
1480+
return None;
14501481

14511482
if (!MI.getSpillSize(TII) && !MI.getFoldedSpillSize(TII))
1452-
return false; // This is not a spill instruction, since no valid size was
1453-
// returned from either function.
1483+
return None; // This is not a spill instruction, since no valid size was
1484+
// returned from either function.
14541485

1455-
return true;
1486+
return extractSpillBaseRegAndOffset(MI);
14561487
}
14571488

14581489
bool InstrRefBasedLDV::isLocationSpill(const MachineInstr &MI,
@@ -1509,13 +1540,11 @@ bool InstrRefBasedLDV::transferSpillOrRestoreInst(MachineInstr &MI) {
15091540
// First, if there are any DBG_VALUEs pointing at a spill slot that is
15101541
// written to, terminate that variable location. The value in memory
15111542
// will have changed. DbgEntityHistoryCalculator doesn't try to detect this.
1512-
if (isSpillInstruction(MI, MF)) {
1513-
SpillLocationNo Loc = extractSpillBaseRegAndOffset(MI);
1514-
1543+
if (Optional<SpillLocationNo> Loc = isSpillInstruction(MI, MF)) {
15151544
// Un-set this location and clobber, so that earlier locations don't
15161545
// continue past this store.
15171546
for (unsigned SlotIdx = 0; SlotIdx < MTracker->NumSlotIdxes; ++SlotIdx) {
1518-
unsigned SpillID = MTracker->getSpillIDWithIdx(Loc, SlotIdx);
1547+
unsigned SpillID = MTracker->getSpillIDWithIdx(*Loc, SlotIdx);
15191548
Optional<LocIdx> MLoc = MTracker->getSpillMLoc(SpillID);
15201549
if (!MLoc)
15211550
continue;
@@ -1533,7 +1562,9 @@ bool InstrRefBasedLDV::transferSpillOrRestoreInst(MachineInstr &MI) {
15331562

15341563
// Try to recognise spill and restore instructions that may transfer a value.
15351564
if (isLocationSpill(MI, MF, Reg)) {
1536-
SpillLocationNo Loc = extractSpillBaseRegAndOffset(MI);
1565+
// isLocationSpill returning true should guarantee we can extract a
1566+
// location.
1567+
SpillLocationNo Loc = *extractSpillBaseRegAndOffset(MI);
15371568

15381569
auto DoTransfer = [&](Register SrcReg, unsigned SpillID) {
15391570
auto ReadValue = MTracker->readReg(SrcReg);
@@ -1560,10 +1591,9 @@ bool InstrRefBasedLDV::transferSpillOrRestoreInst(MachineInstr &MI) {
15601591
unsigned SpillID = MTracker->getLocID(Loc, {Size, 0});
15611592
DoTransfer(Reg, SpillID);
15621593
} else {
1563-
Optional<SpillLocationNo> OptLoc = isRestoreInstruction(MI, MF, Reg);
1564-
if (!OptLoc)
1594+
Optional<SpillLocationNo> Loc = isRestoreInstruction(MI, MF, Reg);
1595+
if (!Loc)
15651596
return false;
1566-
SpillLocationNo Loc = *OptLoc;
15671597

15681598
// Assumption: we're reading from the base of the stack slot, not some
15691599
// offset into it. It seems very unlikely LLVM would ever generate
@@ -1590,13 +1620,13 @@ bool InstrRefBasedLDV::transferSpillOrRestoreInst(MachineInstr &MI) {
15901620

15911621
for (MCSubRegIterator SRI(Reg, TRI, false); SRI.isValid(); ++SRI) {
15921622
unsigned Subreg = TRI->getSubRegIndex(Reg, *SRI);
1593-
unsigned SpillID = MTracker->getLocID(Loc, Subreg);
1623+
unsigned SpillID = MTracker->getLocID(*Loc, Subreg);
15941624
DoTransfer(*SRI, SpillID);
15951625
}
15961626

15971627
// Directly look up this registers slot idx by size, and transfer.
15981628
unsigned Size = TRI->getRegSizeInBits(Reg, *MRI);
1599-
unsigned SpillID = MTracker->getLocID(Loc, {Size, 0});
1629+
unsigned SpillID = MTracker->getLocID(*Loc, {Size, 0});
16001630
DoTransfer(Reg, SpillID);
16011631
}
16021632
return true;

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,9 @@ class MLocTracker {
616616
void writeRegMask(const MachineOperand *MO, unsigned CurBB, unsigned InstID);
617617

618618
/// Find LocIdx for SpillLoc \p L, creating a new one if it's not tracked.
619-
SpillLocationNo getOrTrackSpillLoc(SpillLoc L);
619+
/// Returns None when in scenarios where a spill slot could be tracked, but
620+
/// we would likely run into resource limitations.
621+
Optional<SpillLocationNo> getOrTrackSpillLoc(SpillLoc L);
620622

621623
// Get LocIdx of a spill ID.
622624
LocIdx getSpillMLoc(unsigned SpillID) {
@@ -873,7 +875,8 @@ class InstrRefBasedLDV : public LDVImpl {
873875
StringRef StackProbeSymbolName;
874876

875877
/// Tests whether this instruction is a spill to a stack slot.
876-
bool isSpillInstruction(const MachineInstr &MI, MachineFunction *MF);
878+
Optional<SpillLocationNo> isSpillInstruction(const MachineInstr &MI,
879+
MachineFunction *MF);
877880

878881
/// Decide if @MI is a spill instruction and return true if it is. We use 2
879882
/// criteria to make this decision:
@@ -891,7 +894,8 @@ class InstrRefBasedLDV : public LDVImpl {
891894

892895
/// Given a spill instruction, extract the spill slot information, ensure it's
893896
/// tracked, and return the spill number.
894-
SpillLocationNo extractSpillBaseRegAndOffset(const MachineInstr &MI);
897+
Optional<SpillLocationNo>
898+
extractSpillBaseRegAndOffset(const MachineInstr &MI);
895899

896900
/// Observe a single instruction while stepping through a block.
897901
void process(MachineInstr &MI, ValueIDNum **MLiveOuts = nullptr,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# RUN: llc %s -o - -experimental-debug-variable-locations \
2+
# RUN: -run-pass=livedebugvalues -livedebugvalues-max-stack-slots=0 | \
3+
# RUN: FileCheck %s --implicit-check-not=DBG_VALUE
4+
# RUN: llc %s -o - -experimental-debug-variable-locations \
5+
# RUN: -run-pass=livedebugvalues -livedebugvalues-max-stack-slots=100 | \
6+
# RUN: FileCheck %s --check-prefixes=NOLIMIT --implicit-check-not=DBG_VALUE
7+
#
8+
# Test that spills of live values to the stack are NOT tracked by
9+
# LiveDebugValues if an internal accounting limit is exceeded -- in this test,
10+
# set to zero. This is to avoid scenarios where we track thousands of stack
11+
# slots, which can show up with autogenerated code and/or asan.
12+
#
13+
# This is a copy of livedebugvalues_stackslot_subregs.mir, here the stack slot
14+
# limit is set to zero, meaning the spill shouldn't be tracked.
15+
#
16+
## Capture variable num,
17+
# CHECK: ![[VARNUM:[0-9]+]] = !DILocalVariable
18+
#
19+
## There should be no variable location, just a single DBG_VALUE $noreg.
20+
# CHECK: DBG_VALUE $noreg
21+
#
22+
## And then another.
23+
# CHECK: DBG_VALUE $noreg
24+
#
25+
## Test that if there's no limit, we _do_ get some locations.
26+
# NOLIMIT: DBG_INSTR_REF 1, 0
27+
# NOLIMIT-NEXT: DBG_VALUE $esi
28+
#
29+
# NOLIMIT: DBG_INSTR_REF 5,
30+
# NOLIMIT-NEXT: DBG_VALUE $rsp
31+
--- |
32+
define i8 @test(i32 %bar) local_unnamed_addr !dbg !7 {
33+
entry:
34+
ret i8 0, !dbg !12
35+
}
36+
37+
declare dso_local void @ext(i64)
38+
39+
!llvm.dbg.cu = !{!0}
40+
!llvm.module.flags = !{!3, !4, !5, !6}
41+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
42+
!1 = !DIFile(filename: "foo.cpp", directory: ".")
43+
!2 = !DIBasicType(name: "int", size: 8, encoding: DW_ATE_signed)
44+
!3 = !{i32 2, !"Dwarf Version", i32 4}
45+
!4 = !{i32 2, !"Debug Info Version", i32 3}
46+
!5 = !{i32 1, !"wchar_size", i32 2}
47+
!6 = !{i32 7, !"PIC Level", i32 2}
48+
!7 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
49+
!8 = !DISubroutineType(types: !9)
50+
!9 = !{!2, !2}
51+
!10 = !{!11}
52+
!11 = !DILocalVariable(name: "baz", scope: !7, file: !1, line: 7, type: !2)
53+
!12 = !DILocation(line: 10, scope: !7)
54+
...
55+
---
56+
name: test
57+
tracksRegLiveness: true
58+
liveins:
59+
- { reg: '$rdi', virtual-reg: '' }
60+
stack:
61+
- { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
62+
stack-id: default, callee-saved-register: '', callee-saved-restored: true,
63+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
64+
body: |
65+
bb.0:
66+
liveins: $rdi, $rax, $rbx
67+
$eax = MOV32ri 0, debug-instr-number 1
68+
$edi = COPY $eax
69+
MOV64mr $rsp, 1, $noreg, 16, $noreg, $rdi :: (store 8 into %stack.0)
70+
$rsi = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0)
71+
72+
MOV64mr $rsp, 1, $noreg, 16, $noreg, $rbx :: (store 8 into %stack.0)
73+
$rax = MOV64ri 0
74+
$rdi = MOV64ri 0
75+
76+
DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12
77+
; This shouldn't find anything -- we have disabled tracking of spills.
78+
79+
; In addition to plain spills, spills that are folded into instructions
80+
; shouldn't be tracked either.
81+
INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-instr-number 5, debug-location !12 :: (store (s32) into %stack.0)
82+
83+
84+
DBG_INSTR_REF 5, 1000000, !11, !DIExpression(), debug-location !12
85+
; Shouldn't be able to find the reference to instr 5's memory operand.
86+
87+
RET64 $rsi, debug-location !12
88+
...

0 commit comments

Comments
 (0)