Skip to content

Commit 1a65d95

Browse files
[CodeGen][RAGreedy] Inform LiveDebugVariables about snippets spilled by InlineSpiller. (#109962)
RAGreedy invokes InlineSpiller to spill a particular virtreg inline. When the spiller does this, it also identifies small, adjacent liveranges called snippets. These are also spilled or rematerialized in the process. However, the spiller does not inform RA that it has spilled these regs. This means that debug variable locations referencing these regs/ranges are lost. Mark any spilled regs which do not have a stack slot assigned to them as allocated to the slot being spilled to to tell LDV that those regs are located in that slot, even though the regs might no longer exist in the program after regalloc is finished. Also, inform RA about all of the regs which were replaced (spilled or rematted), not just the one that was requested so that it can properly manage the ranges of the debug vars.
1 parent 7537142 commit 1a65d95

File tree

5 files changed

+180
-2
lines changed

5 files changed

+180
-2
lines changed

llvm/include/llvm/CodeGen/Spiller.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ class Spiller {
3030
/// spill - Spill the LRE.getParent() live interval.
3131
virtual void spill(LiveRangeEdit &LRE) = 0;
3232

33+
/// Return the registers that were spilled.
34+
virtual ArrayRef<Register> getSpilledRegs() = 0;
35+
36+
/// Return registers that were not spilled, but otherwise replaced
37+
/// (e.g. rematerialized).
38+
virtual ArrayRef<Register> getReplacedRegs() = 0;
39+
3340
virtual void postOptimization() {}
3441
};
3542

llvm/lib/CodeGen/InlineSpiller.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ class InlineSpiller : public Spiller {
167167
// All registers to spill to StackSlot, including the main register.
168168
SmallVector<Register, 8> RegsToSpill;
169169

170+
// All registers that were replaced by the spiller through some other method,
171+
// e.g. rematerialization.
172+
SmallVector<Register, 8> RegsReplaced;
173+
170174
// All COPY instructions to/from snippets.
171175
// They are ignored since both operands refer to the same stack slot.
172176
// For bundled copies, this will only include the first header copy.
@@ -199,6 +203,8 @@ class InlineSpiller : public Spiller {
199203
HSpiller(Pass, MF, VRM), VRAI(VRAI) {}
200204

201205
void spill(LiveRangeEdit &) override;
206+
ArrayRef<Register> getSpilledRegs() override { return RegsToSpill; }
207+
ArrayRef<Register> getReplacedRegs() override { return RegsReplaced; }
202208
void postOptimization() override;
203209

204210
private:
@@ -385,6 +391,7 @@ void InlineSpiller::collectRegsToSpill() {
385391
// Main register always spills.
386392
RegsToSpill.assign(1, Reg);
387393
SnippetCopies.clear();
394+
RegsReplaced.clear();
388395

389396
// Snippets all have the same original, so there can't be any for an original
390397
// register.
@@ -796,6 +803,7 @@ void InlineSpiller::reMaterializeAll() {
796803
for (Register Reg : RegsToSpill) {
797804
if (MRI.reg_nodbg_empty(Reg)) {
798805
Edit->eraseVirtReg(Reg);
806+
RegsReplaced.push_back(Reg);
799807
continue;
800808
}
801809

@@ -1255,8 +1263,13 @@ void InlineSpiller::spillAll() {
12551263
LLVM_DEBUG(dbgs() << "Merged spilled regs: " << *StackInt << '\n');
12561264

12571265
// Spill around uses of all RegsToSpill.
1258-
for (Register Reg : RegsToSpill)
1266+
for (Register Reg : RegsToSpill) {
12591267
spillAroundUses(Reg);
1268+
// Assign all of the spilled registers to the slot so that
1269+
// LiveDebugVariables knows about these locations later on.
1270+
if (VRM.getStackSlot(Reg) == VirtRegMap::NO_STACK_SLOT)
1271+
VRM.assignVirt2StackSlot(Reg, StackSlot);
1272+
}
12601273

12611274
// Hoisted spills may cause dead code.
12621275
if (!DeadDefs.empty()) {

llvm/lib/CodeGen/RegAllocGreedy.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2504,7 +2504,10 @@ MCRegister RAGreedy::selectOrSplitImpl(const LiveInterval &VirtReg,
25042504
// Tell LiveDebugVariables about the new ranges. Ranges not being covered by
25052505
// the new regs are kept in LDV (still mapping to the old register), until
25062506
// we rewrite spilled locations in LDV at a later stage.
2507-
DebugVars->splitRegister(VirtReg.reg(), LRE.regs(), *LIS);
2507+
for (Register r : spiller().getSpilledRegs())
2508+
DebugVars->splitRegister(r, LRE.regs(), *LIS);
2509+
for (Register r : spiller().getReplacedRegs())
2510+
DebugVars->splitRegister(r, LRE.regs(), *LIS);
25082511

25092512
if (VerifyEnabled)
25102513
MF->verify(this, "After spilling", &errs());
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
; RUN: llc -mtriple i386 %s -stop-after=livedebugvalues -o - | FileCheck %s
2+
3+
; There should be multiple debug values for this variable after regalloc. The
4+
; value has been spilled, but we shouldn't lose track of the location because
5+
; of this.
6+
7+
; CHECK-COUNT-4: DBG_VALUE $ebp, 0, !6, !DIExpression(DW_OP_constu, 16, DW_OP_minus), debug-location !10
8+
9+
define void @main(i32 %call, i32 %xor.i, i1 %tobool4.not, i32 %.pre) #0 !dbg !4 {
10+
entry:
11+
%tobool1.not = icmp ne i32 %call, 0
12+
%spec.select = zext i1 %tobool1.not to i32
13+
br label %for.body5
14+
15+
for.cond.loopexit.loopexit: ; preds = %for.body5
16+
#dbg_value(i32 %spec.select, !6, !DIExpression(), !10)
17+
%tobool.not.i53 = icmp eq i32 %spec.select, 0
18+
br i1 %tobool.not.i53, label %transparent_crc.exit57, label %if.then.i54
19+
20+
for.body5: ; preds = %for.body5, %entry
21+
%0 = phi i32 [ 0, %entry ], [ %xor1.i40.i, %for.body5 ]
22+
%xor6.i = xor i32 %.pre, %0
23+
%shr7.i = ashr i32 %xor6.i, 1
24+
%xor17.i = xor i32 %shr7.i, %call
25+
%shr18.i = ashr i32 %xor17.i, 1
26+
%xor.i.i = xor i32 %shr18.i, %xor.i
27+
%arrayidx.i.i = getelementptr [0 x i32], ptr null, i32 0, i32 %xor.i.i
28+
%xor1.i40.i = xor i32 %xor.i.i, %call
29+
br i1 %tobool4.not, label %for.cond.loopexit.loopexit, label %for.body5
30+
31+
if.then.i54: ; preds = %for.cond.loopexit.loopexit
32+
store i64 0, ptr null, align 4
33+
br label %transparent_crc.exit57
34+
35+
transparent_crc.exit57: ; preds = %if.then.i54, %for.cond.loopexit.loopexit
36+
ret void
37+
}
38+
39+
attributes #0 = { "frame-pointer"="all" }
40+
41+
!llvm.dbg.cu = !{!0}
42+
!llvm.module.flags = !{!3}
43+
44+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 20.0.0git.prerel", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None)
45+
!1 = !DIFile(filename: "xx.c", directory: "/path", checksumkind: CSK_MD5, checksum: "c4b2fc62bca9171ad484c91fb78b8842")
46+
!2 = !{}
47+
!3 = !{i32 2, !"Debug Info Version", i32 3}
48+
!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 20, type: !5, scopeLine: 20, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
49+
!5 = !DISubroutineType(types: !2)
50+
!6 = !DILocalVariable(name: "flag", arg: 2, scope: !7, file: !1, line: 8, type: !9)
51+
!7 = distinct !DISubprogram(name: "transparent_crc", scope: !1, file: !1, line: 8, type: !8, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
52+
!8 = distinct !DISubroutineType(types: !2)
53+
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
54+
!10 = !DILocation(line: 0, scope: !7, inlinedAt: !11)
55+
!11 = distinct !DILocation(line: 28, column: 3, scope: !4)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# RUN: llc -mtriple i386 -start-before=greedy -stop-after=livedebugvars %s -o - | FileCheck %s
2+
3+
# There should be multiple debug values for this variable after regalloc. The
4+
# value has been spilled, but we shouldn't lose track of the location because
5+
# of this.
6+
7+
# CHECK-COUNT-4: DBG_VALUE $ebp, 0, !6, !DIExpression(DW_OP_constu, 16, DW_OP_minus), debug-location !10
8+
9+
--- |
10+
11+
define void @main() #0 !dbg !4 {
12+
entry:
13+
#dbg_value(i32 undef, !6, !DIExpression(), !10)
14+
ret void
15+
}
16+
17+
attributes #0 = { "frame-pointer"="all" }
18+
19+
!llvm.dbg.cu = !{!0}
20+
!llvm.module.flags = !{!3}
21+
22+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 20.0.0git.prerel", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None)
23+
!1 = !DIFile(filename: "xx.c", directory: "/path", checksumkind: CSK_MD5, checksum: "c4b2fc62bca9171ad484c91fb78b8842")
24+
!2 = !{}
25+
!3 = !{i32 2, !"Debug Info Version", i32 3}
26+
!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 20, type: !5, scopeLine: 20, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
27+
!5 = !DISubroutineType(types: !2)
28+
!6 = !DILocalVariable(name: "flag", arg: 2, scope: !7, file: !1, line: 8, type: !9)
29+
!7 = distinct !DISubprogram(name: "transparent_crc", scope: !1, file: !1, line: 8, type: !8, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
30+
!8 = distinct !DISubroutineType(types: !2)
31+
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
32+
!10 = !DILocation(line: 0, scope: !7, inlinedAt: !11)
33+
!11 = distinct !DILocation(line: 28, column: 3, scope: !4)
34+
35+
...
36+
---
37+
name: main
38+
alignment: 16
39+
tracksRegLiveness: true
40+
hasWinCFI: false
41+
noPhis: true
42+
fixedStack:
43+
- { id: 0, type: default, offset: 0, size: 4, alignment: 4, stack-id: default,
44+
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
45+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
46+
- { id: 1, type: default, offset: 4, size: 4, alignment: 4, stack-id: default,
47+
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
48+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
49+
- { id: 2, type: default, offset: 8, size: 1, alignment: 4, stack-id: default,
50+
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
51+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
52+
- { id: 3, type: default, offset: 12, size: 4, alignment: 4, stack-id: default,
53+
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
54+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
55+
body: |
56+
bb.0:
57+
successors: %bb.2(0x80000000)
58+
59+
%0:gr32 = MOV32rm %fixed-stack.3, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.3)
60+
%1:gr8 = MOV8rm %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s8) from %fixed-stack.2, align 4)
61+
%2:gr32 = MOV32rm %fixed-stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.0)
62+
%3:gr32_abcd = MOV32r0 implicit-def dead $eflags
63+
TEST32rr %2, %2, implicit-def $eflags
64+
%3.sub_8bit:gr32_abcd = SETCCr 5, implicit $eflags
65+
%4:gr32 = COPY %2
66+
%4:gr32 = SAR32ri %4, 1, implicit-def dead $eflags
67+
%5:gr32 = MOV32rm %fixed-stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.1)
68+
%6:gr32 = MOV32r0 implicit-def dead $eflags
69+
JMP_1 %bb.2
70+
71+
bb.1:
72+
successors: %bb.4(0x30000000), %bb.3(0x50000000)
73+
74+
DBG_VALUE %3, $noreg, !6, !DIExpression(), debug-location !10
75+
TEST32rr %3, %3, implicit-def $eflags
76+
JCC_1 %bb.4, 4, implicit $eflags
77+
JMP_1 %bb.3
78+
79+
bb.2:
80+
successors: %bb.1(0x04000000), %bb.2(0x7c000000)
81+
82+
%6:gr32 = XOR32rr %6, %0, implicit-def dead $eflags
83+
%6:gr32 = SAR32ri %6, 2, implicit-def dead $eflags
84+
%6:gr32 = XOR32rr %6, %4, implicit-def dead $eflags
85+
%6:gr32 = XOR32rr %6, %5, implicit-def dead $eflags
86+
%6:gr32 = XOR32rr %6, %2, implicit-def dead $eflags
87+
TEST8ri %1, 1, implicit-def $eflags
88+
JCC_1 %bb.1, 5, implicit $eflags
89+
JMP_1 %bb.2
90+
91+
bb.3:
92+
successors: %bb.4(0x80000000)
93+
94+
MOV32mi $noreg, 1, $noreg, 4, $noreg, 0 :: (store (s32) into `ptr null` + 4)
95+
MOV32mi $noreg, 1, $noreg, 0, $noreg, 0 :: (store (s32) into `ptr null`)
96+
97+
bb.4:
98+
RET 0
99+
100+
...

0 commit comments

Comments
 (0)