Skip to content

Commit 9656be8

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 ee63a7b commit 9656be8

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

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

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

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

523+
void Spill2Reg::StackSlotDataEntry::MIDataWithLiveIn::dump() const {
524+
dbgs() << " (" << *MO << ") " << *MI << " IsLiveIn: " << IsLiveIn;
525+
}
526+
440527
void Spill2Reg::StackSlotDataEntry::dump() const {
441528
dbgs().indent(DumpInd) << "Disable: " << Disable << "\n";
442529
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)