Skip to content

[CodeGen][RAGreedy] Inform LiveDebugVariables about snippets spilled by InlineSpiller. #109962

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions llvm/include/llvm/CodeGen/Spiller.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ class Spiller {
/// spill - Spill the LRE.getParent() live interval.
virtual void spill(LiveRangeEdit &LRE) = 0;

/// Return the registers that were spilled.
virtual ArrayRef<Register> getSpilledRegs() = 0;

/// Return registers that were not spilled, but otherwise replaced
/// (e.g. rematerialized).
virtual ArrayRef<Register> getReplacedRegs() = 0;

virtual void postOptimization() {}
};

Expand Down
15 changes: 14 additions & 1 deletion llvm/lib/CodeGen/InlineSpiller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ class InlineSpiller : public Spiller {
// All registers to spill to StackSlot, including the main register.
SmallVector<Register, 8> RegsToSpill;

// All registers that were replaced by the spiller through some other method,
// e.g. rematerialization.
SmallVector<Register, 8> RegsReplaced;

// All COPY instructions to/from snippets.
// They are ignored since both operands refer to the same stack slot.
// For bundled copies, this will only include the first header copy.
Expand Down Expand Up @@ -199,6 +203,8 @@ class InlineSpiller : public Spiller {
HSpiller(Pass, MF, VRM), VRAI(VRAI) {}

void spill(LiveRangeEdit &) override;
ArrayRef<Register> getSpilledRegs() override { return RegsToSpill; }
ArrayRef<Register> getReplacedRegs() override { return RegsReplaced; }
void postOptimization() override;

private:
Expand Down Expand Up @@ -385,6 +391,7 @@ void InlineSpiller::collectRegsToSpill() {
// Main register always spills.
RegsToSpill.assign(1, Reg);
SnippetCopies.clear();
RegsReplaced.clear();

// Snippets all have the same original, so there can't be any for an original
// register.
Expand Down Expand Up @@ -796,6 +803,7 @@ void InlineSpiller::reMaterializeAll() {
for (Register Reg : RegsToSpill) {
if (MRI.reg_nodbg_empty(Reg)) {
Edit->eraseVirtReg(Reg);
RegsReplaced.push_back(Reg);
continue;
}

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

// Spill around uses of all RegsToSpill.
for (Register Reg : RegsToSpill)
for (Register Reg : RegsToSpill) {
spillAroundUses(Reg);
// Assign all of the spilled registers to the slot so that
// LiveDebugVariables knows about these locations later on.
if (VRM.getStackSlot(Reg) == VirtRegMap::NO_STACK_SLOT)
VRM.assignVirt2StackSlot(Reg, StackSlot);
}

// Hoisted spills may cause dead code.
if (!DeadDefs.empty()) {
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/RegAllocGreedy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2504,7 +2504,10 @@ MCRegister RAGreedy::selectOrSplitImpl(const LiveInterval &VirtReg,
// Tell LiveDebugVariables about the new ranges. Ranges not being covered by
// the new regs are kept in LDV (still mapping to the old register), until
// we rewrite spilled locations in LDV at a later stage.
DebugVars->splitRegister(VirtReg.reg(), LRE.regs(), *LIS);
for (Register r : spiller().getSpilledRegs())
DebugVars->splitRegister(r, LRE.regs(), *LIS);
for (Register r : spiller().getReplacedRegs())
DebugVars->splitRegister(r, LRE.regs(), *LIS);

if (VerifyEnabled)
MF->verify(this, "After spilling", &errs());
Expand Down
55 changes: 55 additions & 0 deletions llvm/test/CodeGen/X86/debug-spilled-snippet.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
; RUN: llc -mtriple i386 %s -stop-after=livedebugvalues -o - | FileCheck %s

; There should be multiple debug values for this variable after regalloc. The
; value has been spilled, but we shouldn't lose track of the location because
; of this.

; CHECK-COUNT-4: DBG_VALUE $ebp, 0, !6, !DIExpression(DW_OP_constu, 16, DW_OP_minus), debug-location !10

define void @main(i32 %call, i32 %xor.i, i1 %tobool4.not, i32 %.pre) #0 !dbg !4 {
entry:
%tobool1.not = icmp ne i32 %call, 0
%spec.select = zext i1 %tobool1.not to i32
br label %for.body5

for.cond.loopexit.loopexit: ; preds = %for.body5
#dbg_value(i32 %spec.select, !6, !DIExpression(), !10)
%tobool.not.i53 = icmp eq i32 %spec.select, 0
br i1 %tobool.not.i53, label %transparent_crc.exit57, label %if.then.i54

for.body5: ; preds = %for.body5, %entry
%0 = phi i32 [ 0, %entry ], [ %xor1.i40.i, %for.body5 ]
%xor6.i = xor i32 %.pre, %0
%shr7.i = ashr i32 %xor6.i, 1
%xor17.i = xor i32 %shr7.i, %call
%shr18.i = ashr i32 %xor17.i, 1
%xor.i.i = xor i32 %shr18.i, %xor.i
%arrayidx.i.i = getelementptr [0 x i32], ptr null, i32 0, i32 %xor.i.i
%xor1.i40.i = xor i32 %xor.i.i, %call
br i1 %tobool4.not, label %for.cond.loopexit.loopexit, label %for.body5

if.then.i54: ; preds = %for.cond.loopexit.loopexit
store i64 0, ptr null, align 4
br label %transparent_crc.exit57

transparent_crc.exit57: ; preds = %if.then.i54, %for.cond.loopexit.loopexit
ret void
}

attributes #0 = { "frame-pointer"="all" }

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

!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)
!1 = !DIFile(filename: "xx.c", directory: "/path", checksumkind: CSK_MD5, checksum: "c4b2fc62bca9171ad484c91fb78b8842")
!2 = !{}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 20, type: !5, scopeLine: 20, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
!5 = !DISubroutineType(types: !2)
!6 = !DILocalVariable(name: "flag", arg: 2, scope: !7, file: !1, line: 8, type: !9)
!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)
!8 = distinct !DISubroutineType(types: !2)
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = !DILocation(line: 0, scope: !7, inlinedAt: !11)
!11 = distinct !DILocation(line: 28, column: 3, scope: !4)
100 changes: 100 additions & 0 deletions llvm/test/CodeGen/X86/debug-spilled-snippet.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# RUN: llc -mtriple i386 -start-before=greedy -stop-after=livedebugvars %s -o - | FileCheck %s

# There should be multiple debug values for this variable after regalloc. The
# value has been spilled, but we shouldn't lose track of the location because
# of this.

# CHECK-COUNT-4: DBG_VALUE $ebp, 0, !6, !DIExpression(DW_OP_constu, 16, DW_OP_minus), debug-location !10

--- |

define void @main() #0 !dbg !4 {
entry:
#dbg_value(i32 undef, !6, !DIExpression(), !10)
ret void
}

attributes #0 = { "frame-pointer"="all" }

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

!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)
!1 = !DIFile(filename: "xx.c", directory: "/path", checksumkind: CSK_MD5, checksum: "c4b2fc62bca9171ad484c91fb78b8842")
!2 = !{}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 20, type: !5, scopeLine: 20, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
!5 = !DISubroutineType(types: !2)
!6 = !DILocalVariable(name: "flag", arg: 2, scope: !7, file: !1, line: 8, type: !9)
!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)
!8 = distinct !DISubroutineType(types: !2)
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = !DILocation(line: 0, scope: !7, inlinedAt: !11)
!11 = distinct !DILocation(line: 28, column: 3, scope: !4)

...
---
name: main
alignment: 16
tracksRegLiveness: true
hasWinCFI: false
noPhis: true
fixedStack:
- { id: 0, type: default, offset: 0, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, type: default, offset: 4, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 2, type: default, offset: 8, size: 1, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 3, type: default, offset: 12, size: 4, alignment: 4, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
body: |
bb.0:
successors: %bb.2(0x80000000)

%0:gr32 = MOV32rm %fixed-stack.3, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.3)
%1:gr8 = MOV8rm %fixed-stack.2, 1, $noreg, 0, $noreg :: (load (s8) from %fixed-stack.2, align 4)
%2:gr32 = MOV32rm %fixed-stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.0)
%3:gr32_abcd = MOV32r0 implicit-def dead $eflags
TEST32rr %2, %2, implicit-def $eflags
%3.sub_8bit:gr32_abcd = SETCCr 5, implicit $eflags
%4:gr32 = COPY %2
%4:gr32 = SAR32ri %4, 1, implicit-def dead $eflags
%5:gr32 = MOV32rm %fixed-stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %fixed-stack.1)
%6:gr32 = MOV32r0 implicit-def dead $eflags
JMP_1 %bb.2

bb.1:
successors: %bb.4(0x30000000), %bb.3(0x50000000)

DBG_VALUE %3, $noreg, !6, !DIExpression(), debug-location !10
TEST32rr %3, %3, implicit-def $eflags
JCC_1 %bb.4, 4, implicit $eflags
JMP_1 %bb.3

bb.2:
successors: %bb.1(0x04000000), %bb.2(0x7c000000)

%6:gr32 = XOR32rr %6, %0, implicit-def dead $eflags
%6:gr32 = SAR32ri %6, 2, implicit-def dead $eflags
%6:gr32 = XOR32rr %6, %4, implicit-def dead $eflags
%6:gr32 = XOR32rr %6, %5, implicit-def dead $eflags
%6:gr32 = XOR32rr %6, %2, implicit-def dead $eflags
TEST8ri %1, 1, implicit-def $eflags
JCC_1 %bb.1, 5, implicit $eflags
JMP_1 %bb.2

bb.3:
successors: %bb.4(0x80000000)

MOV32mi $noreg, 1, $noreg, 4, $noreg, 0 :: (store (s32) into `ptr null` + 4)
MOV32mi $noreg, 1, $noreg, 0, $noreg, 0 :: (store (s32) into `ptr null`)

bb.4:
RET 0

...
Loading