Skip to content

Commit b675b0d

Browse files
author
Vasileios Porpodas
committed
[Spill2Reg] MBB live-ins are now being updated
This patch implements updates for the MBB live-ins due to the newly introduced instructions emitted by spill2reg. This is required for correct tracking of live register usage. Original review: https://reviews.llvm.org/D118304
1 parent 7a4e635 commit b675b0d

File tree

6 files changed

+543
-2
lines changed

6 files changed

+543
-2
lines changed

llvm/lib/CodeGen/Spill2Reg.cpp

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,35 @@ class Spill2Reg : public MachineFunctionPass {
6565
/// differs across accesses to the same stack slot.
6666
unsigned SpillBits = 0;
6767
#ifndef NDEBUG
68-
LLVM_DUMP_METHOD void dump() const;
68+
LLVM_DUMP_METHOD virtual void dump() const;
69+
virtual ~MIData() {}
70+
#endif
71+
};
72+
73+
struct MIDataWithLiveIn : public MIData {
74+
MIDataWithLiveIn(MachineInstr *MI, const MachineOperand *MO,
75+
unsigned SpillBits)
76+
: MIData(MI, MO, SpillBits) {}
77+
/// We set this to false to mark the vector register associated to this
78+
/// reload as definitely not live-in. This is useful in blocks with both
79+
/// spill and reload of the same stack slot, like in the example:
80+
/// \verbatim
81+
/// bb:
82+
/// spill %stack.0
83+
/// reload %stack.0
84+
/// \endverbatim
85+
/// This information is used during `updateLiveIns()`. We are collecting
86+
/// this information during `collectSpillsAndReloads()` because we are
87+
/// already walking through the code there. Otherwise we would need to
88+
/// walk throught the code again in `updateLiveIns()` just to check for
89+
/// other spills in the block, which would waste compilation time.
90+
bool IsLiveIn = true;
91+
#ifndef NDEBUG
92+
LLVM_DUMP_METHOD virtual void dump() const override;
6993
#endif
7094
};
7195
SmallVector<MIData, 1> Spills;
72-
SmallVector<MIData, 1> Reloads;
96+
SmallVector<MIDataWithLiveIn, 1> Reloads;
7397

7498
/// \Returns the register class of the register being spilled.
7599
const TargetRegisterClass *
@@ -78,6 +102,7 @@ class Spill2Reg : public MachineFunctionPass {
78102
auto Reg0 = Spills.front().MO->getReg();
79103
return TRI->getCandidateRegisterClassForSpill2Reg(TRI, Reg0);
80104
}
105+
81106
#ifndef NDEBUG
82107
LLVM_DUMP_METHOD void dump() const;
83108
#endif
@@ -194,6 +219,15 @@ void Spill2Reg::collectSpillsAndReloads() {
194219
}
195220
unsigned SpillBits = TRI->getRegSizeInBits(MO->getReg(), *MRI);
196221
Entry.Spills.emplace_back(Spill, MO, SpillBits);
222+
223+
// If any of the reloads collected so far is in the same MBB then mark
224+
// it as non live-in. This is used in `updateLiveIns()` where we update
225+
// the liveins of MBBs to include the new vector register. Doing this
226+
// now avoids an MBB walk in `updateLiveIns()` which should save
227+
// compilation time.
228+
for (auto &MID : Entry.Reloads)
229+
if (MID.MI->getParent() == &MBB)
230+
MID.IsLiveIn = false;
197231
} else if (const MachineOperand *MO =
198232
TII->isLoadFromStackSlotMO(MI, StackSlot)) {
199233
MachineInstr *Reload = &MI;
@@ -264,6 +298,49 @@ static void DFS(MachineBasicBlock *MBB, DenseSet<MachineBasicBlock *> &Visited,
264298
for (MachineBasicBlock *PredMBB : MBB->predecessors())
265299
DFS(PredMBB, Visited, Fn);
266300
}
301+
302+
void Spill2Reg::updateLiveIns(StackSlotDataEntry &Entry, MCRegister VectorReg) {
303+
// Collect the parent MBBs of Spills for fast lookup.
304+
DenseSet<MachineBasicBlock *> SpillMBBs(Entry.Spills.size());
305+
DenseSet<MachineInstr *> Spills(Entry.Spills.size());
306+
for (const auto &Data : Entry.Spills) {
307+
SpillMBBs.insert(Data.MI->getParent());
308+
Spills.insert(Data.MI);
309+
}
310+
311+
auto AddLiveInIfRequired = [VectorReg, &SpillMBBs](MachineBasicBlock *MBB) {
312+
// If there is a spill in this MBB then we don't need to add a live-in.
313+
// This works even if there is a reload above the spill, like this:
314+
// reload stack.0
315+
// spill stack.0
316+
// because the live-in due to the reload is handled at a separate walk.
317+
if (SpillMBBs.count(MBB))
318+
// Return true to stop the recursion.
319+
return true;
320+
// If there are no spills in this block then the register is live-in.
321+
if (!MBB->isLiveIn(VectorReg))
322+
MBB->addLiveIn(VectorReg);
323+
// Return false to continue the recursion.
324+
return false;
325+
};
326+
327+
// Update the MBB live-ins. These are used for the live regs calculation.
328+
DenseSet<MachineBasicBlock *> Visited;
329+
for (const auto &ReloadData : Entry.Reloads) {
330+
MachineInstr *Reload = ReloadData.MI;
331+
MachineBasicBlock *MBB = Reload->getParent();
332+
// From a previous walk in MBB we know whether the reload is live-in, or
333+
// whether the value comes from an earlier spill in the same MBB.
334+
if (!ReloadData.IsLiveIn)
335+
continue;
336+
if (!MBB->isLiveIn(VectorReg))
337+
MBB->addLiveIn(VectorReg);
338+
339+
for (MachineBasicBlock *PredMBB : Reload->getParent()->predecessors())
340+
DFS(PredMBB, Visited, AddLiveInIfRequired);
341+
}
342+
}
343+
267344
// Replace stack-based spills/reloads with register-based ones.
268345
void Spill2Reg::replaceStackWithReg(StackSlotDataEntry &Entry,
269346
Register VectorReg) {
@@ -407,6 +484,12 @@ void Spill2Reg::generateCode() {
407484
if (!PhysVectorRegOpt)
408485
continue;
409486

487+
// Update the MBB live-ins. These are used for the live regs calculation.
488+
// Collect the parent MBBs of Spills for fast lookup.
489+
// NOTE: We do that before calling replaceStackWithReg() because it will
490+
// remove the spill/reload instructions from Entry.
491+
updateLiveIns(Entry, *PhysVectorRegOpt);
492+
410493
// Replace stack accesses with register accesses.
411494
replaceStackWithReg(Entry, *PhysVectorRegOpt);
412495

@@ -436,6 +519,10 @@ void Spill2Reg::StackSlotDataEntry::MIData::dump() const {
436519
dbgs() << " (" << *MO << ") " << *MI;
437520
}
438521

522+
void Spill2Reg::StackSlotDataEntry::MIDataWithLiveIn::dump() const {
523+
dbgs() << " (" << *MO << ") " << *MI << " IsLiveIn: " << IsLiveIn;
524+
}
525+
439526
void Spill2Reg::StackSlotDataEntry::dump() const {
440527
dbgs().indent(DumpInd) << "Disable: " << Disable << "\n";
441528
dbgs().indent(DumpInd) << "Spills:\n";
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s
3+
4+
# Check that the liveins of a MBB get updated correctly if it contains a reload
5+
# before a spill.
6+
7+
# bb.0:
8+
# spill stack.0
9+
# JMP bb.1
10+
#
11+
# bb.1:
12+
# reload stack.0
13+
# spill stack.0
14+
# JMP bb.2
15+
#
16+
# bb.2:
17+
# reload stack.0
18+
19+
20+
--- |
21+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
22+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
23+
define void @func() { ret void }
24+
...
25+
---
26+
name: func
27+
alignment: 16
28+
tracksRegLiveness: true
29+
tracksDebugUserValues: true
30+
frameInfo:
31+
maxAlignment: 4
32+
stack:
33+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
34+
machineFunctionInfo: {}
35+
body: |
36+
; CHECK-LABEL: name: func
37+
; CHECK: bb.0:
38+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
39+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
40+
; CHECK-NEXT: JMP_1 %bb.1
41+
; CHECK-NEXT: {{ $}}
42+
; CHECK-NEXT: bb.1:
43+
; CHECK-NEXT: liveins: $xmm0
44+
; CHECK-NEXT: {{ $}}
45+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
46+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
47+
; CHECK-NEXT: JMP_1 %bb.2
48+
; CHECK-NEXT: {{ $}}
49+
; CHECK-NEXT: bb.2:
50+
; CHECK-NEXT: liveins: $xmm0
51+
; CHECK-NEXT: {{ $}}
52+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
53+
; CHECK-NEXT: RET 0
54+
55+
56+
57+
bb.0:
58+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
59+
; Spill stack.0
60+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
61+
JMP_1 %bb.1
62+
63+
bb.1:
64+
; Reload stack.0
65+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
66+
; Spill stack.0
67+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
68+
JMP_1 %bb.2
69+
70+
bb.2:
71+
; Reload
72+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
73+
RET 0
74+
...
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s
3+
4+
5+
# Checks that spills that are overriden by other spills (like spill0 by spill1)
6+
# are not marked as live-in.
7+
# Also checks live reg tracking with spills-reload pairs separated by an instr
8+
# that clobbers the xmm registers.
9+
10+
# BB0:
11+
# [stack.0] = ... ; spill0
12+
# BB1:
13+
# [stack.0] = ... ; spill1
14+
# ... = [stack.0] ; reload1
15+
# call ; clobbers xmm regs
16+
# [stack.0] = ... ; spill2
17+
# ... = [stack.0] ; reload2
18+
# BB2:
19+
# ... = [stack.0] ; reload3
20+
21+
--- |
22+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
23+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
24+
declare void @foo()
25+
define void @func() { ret void }
26+
...
27+
---
28+
name: func
29+
alignment: 16
30+
tracksRegLiveness: true
31+
tracksDebugUserValues: true
32+
frameInfo:
33+
maxAlignment: 4
34+
stack:
35+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
36+
machineFunctionInfo: {}
37+
body: |
38+
; CHECK-LABEL: name: func
39+
; CHECK: bb.0:
40+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
41+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
42+
; CHECK-NEXT: {{ $}}
43+
; CHECK-NEXT: bb.1:
44+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
45+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
46+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
47+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
48+
; CHECK-NEXT: CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit-def $rsp
49+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
50+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
51+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
52+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
53+
; CHECK-NEXT: JMP_1 %bb.2
54+
; CHECK-NEXT: {{ $}}
55+
; CHECK-NEXT: bb.2:
56+
; CHECK-NEXT: liveins: $xmm0
57+
; CHECK-NEXT: {{ $}}
58+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
59+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
60+
; CHECK-NEXT: RET 0
61+
62+
63+
64+
bb.0:
65+
successors: %bb.1
66+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
67+
; spill0
68+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
69+
70+
bb.1:
71+
successors: %bb.2
72+
; spill1
73+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
74+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
75+
76+
; reload1
77+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
78+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
79+
80+
; The call clobbers all xmm regs
81+
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit-def $rsp
82+
83+
; spill2
84+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
85+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
86+
87+
; reload2
88+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
89+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
90+
JMP_1 %bb.2
91+
92+
bb.2:
93+
; reload3
94+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
95+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
96+
RET 0
97+
...
98+
99+
100+
101+
102+
103+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc %s -o - -mtriple=x86_64-unknown-linux -enable-spill2reg -mattr=+sse4.1 --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s
3+
4+
# Check that live reg tracking works correctly when one live range spans
5+
# multiple blocks and the other spans a single block.
6+
# BB0:
7+
# [stack.0] = ...
8+
# BB1:
9+
# [stack.1] = ...
10+
# ... = [stack.1]
11+
# BB2:
12+
# ... = [stack.0]
13+
14+
--- |
15+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
16+
@D1 = dso_local local_unnamed_addr global i32 0, align 4
17+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
18+
@U1 = dso_local local_unnamed_addr global i32 0, align 4
19+
define void @func() { ret void }
20+
...
21+
---
22+
name: func
23+
alignment: 16
24+
tracksRegLiveness: true
25+
tracksDebugUserValues: true
26+
frameInfo:
27+
maxAlignment: 4
28+
stack:
29+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
30+
- { id: 1, type: spill-slot, size: 4, alignment: 4 }
31+
machineFunctionInfo: {}
32+
body: |
33+
; CHECK-LABEL: name: func
34+
; CHECK: bb.0:
35+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
36+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
37+
; CHECK-NEXT: {{ $}}
38+
; CHECK-NEXT: bb.1:
39+
; CHECK-NEXT: liveins: $xmm0
40+
; CHECK-NEXT: {{ $}}
41+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1)
42+
; CHECK-NEXT: $xmm1 = MOVDI2PDIrr $eax
43+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm1
44+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1)
45+
; CHECK-NEXT: JMP_1 %bb.2
46+
; CHECK-NEXT: {{ $}}
47+
; CHECK-NEXT: bb.2:
48+
; CHECK-NEXT: liveins: $xmm0
49+
; CHECK-NEXT: {{ $}}
50+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
51+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
52+
; CHECK-NEXT: RET 0
53+
54+
55+
56+
bb.0:
57+
successors: %bb.1
58+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
59+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
60+
61+
bb.1:
62+
successors: %bb.2
63+
$eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1)
64+
MOV32mr %stack.1, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.1)
65+
$eax = MOV32rm %stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %stack.1)
66+
MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1)
67+
JMP_1 %bb.2
68+
69+
bb.2:
70+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
71+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
72+
RET 0
73+
...

0 commit comments

Comments
 (0)