Skip to content

Commit 92e26f0

Browse files
author
Vasileios Porpodas
committed
[Spill2Reg] Code generation part 2
Spill2Reg can now emit spill and reload instructions. This will not generate correct code, as it does not keep track of live regs. Original review: https://reviews.llvm.org/D118302
1 parent 0f88d14 commit 92e26f0

File tree

9 files changed

+511
-3
lines changed

9 files changed

+511
-3
lines changed

llvm/lib/CodeGen/Spill2Reg.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "AllocationOrder.h"
19+
#include "llvm/ADT/Statistic.h"
1920
#include "llvm/CodeGen/LiveRegUnits.h"
2021
#include "llvm/CodeGen/MachineFrameInfo.h"
2122
#include "llvm/CodeGen/MachineFunctionPass.h"
@@ -29,6 +30,9 @@
2930

3031
using namespace llvm;
3132

33+
#define DEBUG_TYPE "Spill2Reg"
34+
STATISTIC(NumSpill2RegInstrs, "Number of spills/reloads replaced by spill2reg");
35+
3236
namespace {
3337

3438
class Spill2Reg : public MachineFunctionPass {
@@ -203,8 +207,7 @@ void Spill2Reg::collectSpillsAndReloads() {
203207
}
204208

205209
bool Spill2Reg::isProfitable(const MachineInstr *MI) const {
206-
// TODO: Unimplemented.
207-
return true;
210+
return TII->isSpill2RegProfitable(MI, TRI, MRI);
208211
}
209212

210213
bool Spill2Reg::allAccessesProfitable(const StackSlotDataEntry &Entry) const {
@@ -228,7 +231,33 @@ Spill2Reg::tryGetFreePhysicalReg(const TargetRegisterClass *RegClass,
228231
// Replace stack-based spills/reloads with register-based ones.
229232
void Spill2Reg::replaceStackWithReg(StackSlotDataEntry &Entry,
230233
Register VectorReg) {
231-
// TODO: Unimplemented
234+
for (StackSlotDataEntry::MIData &SpillData : Entry.Spills) {
235+
MachineInstr *StackSpill = SpillData.MI;
236+
assert(SpillData.MO->isReg() && "Expected register MO");
237+
Register OldReg = SpillData.MO->getReg();
238+
239+
MachineInstr *SpillToVector = TII->spill2RegInsertToVectorReg(
240+
VectorReg, OldReg, SpillData.SpillBits, StackSpill->getParent(),
241+
/*InsertBeforeIt=*/StackSpill->getIterator(), TRI);
242+
243+
// Spill to stack is no longer needed.
244+
StackSpill->eraseFromParent();
245+
assert(OldReg.isPhysical() && "Otherwise we need to removeInterval()");
246+
}
247+
248+
for (StackSlotDataEntry::MIData &ReloadData : Entry.Reloads) {
249+
MachineInstr *StackReload = ReloadData.MI;
250+
assert(ReloadData.MO->isReg() && "Expected Reg MO");
251+
Register OldReg = ReloadData.MO->getReg();
252+
253+
MachineInstr *ReloadFromReg = TII->spill2RegExtractFromVectorReg(
254+
OldReg, VectorReg, ReloadData.SpillBits, StackReload->getParent(),
255+
/*InsertBeforeIt=*/StackReload->getIterator(), TRI);
256+
257+
// Reload from stack is no longer needed.
258+
StackReload->eraseFromParent();
259+
assert(OldReg.isPhysical() && "Otherwise we need to removeInterval()");
260+
}
232261
}
233262

234263
void Spill2Reg::calculateLiveRegs(StackSlotDataEntry &Entry,
@@ -259,6 +288,8 @@ void Spill2Reg::generateCode() {
259288

260289
// Replace stack accesses with register accesses.
261290
replaceStackWithReg(Entry, *PhysVectorRegOpt);
291+
292+
NumSpill2RegInstrs += Entry.Spills.size() + Entry.Reloads.size();
262293
}
263294
}
264295

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 | FileCheck %s
3+
# 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 --check-prefix=FORCED
4+
5+
# Simple test to confirm that spill2reg won't apply if there is a vector
6+
# instruction nearby.
7+
8+
--- |
9+
@D0 = dso_local local_unnamed_addr global i64 0, align 4
10+
@U0 = dso_local local_unnamed_addr global i64 0, align 4
11+
define void @func() { ret void }
12+
...
13+
---
14+
name: func
15+
alignment: 16
16+
tracksRegLiveness: true
17+
tracksDebugUserValues: true
18+
frameInfo:
19+
maxAlignment: 4
20+
stack:
21+
- { id: 0, type: spill-slot, size: 8, alignment: 4 }
22+
machineFunctionInfo: {}
23+
body: |
24+
25+
26+
bb.0:
27+
; CHECK-LABEL: name: func
28+
; CHECK: $rax = MOV64rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s64) from @D0)
29+
; CHECK-NEXT: $xmm15 = MOV64toPQIrr $rax
30+
; CHECK-NEXT: MOV64mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $rax :: (store (s64) into %stack.0)
31+
; CHECK-NEXT: $rax = MOV64rm %stack.0, 1, $noreg, 0, $noreg :: (load (s64) from %stack.0)
32+
; CHECK-NEXT: MOV64mr $rip, 1, $noreg, @U0, $noreg, killed renamable $rax :: (store (s64) into @U0)
33+
; CHECK-NEXT: RET 0
34+
; FORCED-LABEL: name: func
35+
; FORCED: $rax = MOV64rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s64) from @D0)
36+
; FORCED-NEXT: $xmm15 = MOV64toPQIrr $rax
37+
; FORCED-NEXT: $xmm0 = MOV64toPQIrr $rax
38+
; FORCED-NEXT: $rax = MOVPQIto64rr $xmm0
39+
; FORCED-NEXT: MOV64mr $rip, 1, $noreg, @U0, $noreg, killed renamable $rax :: (store (s64) into @U0)
40+
; FORCED-NEXT: RET 0
41+
$rax = MOV64rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s64) from @D0)
42+
$xmm15 = MOV64toPQIrr $rax
43+
MOV64mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $rax :: (store (s64) into %stack.0)
44+
; reload
45+
$rax = MOV64rm %stack.0, 1, $noreg, 0, $noreg :: (load (s64) from %stack.0)
46+
MOV64mr $rip, 1, $noreg, @U0, $noreg, killed renamable $rax :: (store (s64) into @U0)
47+
RET 0
48+
...
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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 Spill2reg is disabled if the NoImplicitFloat attribute is set.
5+
6+
--- |
7+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
8+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
9+
define void @func() #0 { ret void }
10+
11+
attributes #0 = { noimplicitfloat }
12+
...
13+
---
14+
name: func
15+
alignment: 16
16+
tracksRegLiveness: true
17+
tracksDebugUserValues: true
18+
frameInfo:
19+
maxAlignment: 4
20+
stack:
21+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
22+
machineFunctionInfo: {}
23+
body: |
24+
bb.0:
25+
; CHECK-LABEL: name: func
26+
; CHECK: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
27+
; CHECK-NEXT: MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
28+
; CHECK-NEXT: $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
29+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
30+
; CHECK-NEXT: RET 0
31+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
32+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
33+
34+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
35+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
36+
RET 0
37+
...
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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=+avx512f --run-pass=spill2reg -simplify-mir -spill2reg-mem-instrs=0 -spill2reg-vec-instrs=99999 | FileCheck %s
3+
4+
# Checks that spills reading from $k mask registers are skipped by Spill2Reg.
5+
6+
--- |
7+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
8+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
9+
define void @func() { ret void }
10+
...
11+
---
12+
name: func
13+
alignment: 16
14+
tracksRegLiveness: true
15+
tracksDebugUserValues: true
16+
frameInfo:
17+
maxAlignment: 4
18+
stack:
19+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
20+
machineFunctionInfo: {}
21+
body: |
22+
bb.0:
23+
liveins: $k1
24+
; CHECK-LABEL: name: func
25+
; CHECK: liveins: $k1
26+
; CHECK-NEXT: {{ $}}
27+
; CHECK-NEXT: KMOVWmk %stack.0, 1, $noreg, 0, $noreg, killed renamable $k1 :: (store (s16) into %stack.0)
28+
; CHECK-NEXT: renamable $k1 = KMOVWkm %stack.0, 1, $noreg, 0, $noreg :: (load (s16) from %stack.0)
29+
; CHECK-NEXT: RET 0
30+
KMOVWmk %stack.0, 1, $noreg, 0, $noreg, killed renamable $k1 :: (store (s16) into %stack.0)
31+
renamable $k1 = KMOVWkm %stack.0, 1, $noreg, 0, $noreg :: (load (s16) from %stack.0)
32+
RET 0
33+
...
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
# 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 --check-prefix=NOSSE %s
4+
5+
# Simple test with a single spill-reload pair (32-bit version):
6+
# spill stack.0
7+
# reload stack.0
8+
9+
--- |
10+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
11+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
12+
define void @func() { ret void }
13+
...
14+
---
15+
name: func
16+
alignment: 16
17+
tracksRegLiveness: true
18+
tracksDebugUserValues: true
19+
frameInfo:
20+
maxAlignment: 4
21+
stack:
22+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
23+
machineFunctionInfo: {}
24+
body: |
25+
26+
27+
bb.0:
28+
; spill
29+
; CHECK-LABEL: name: func
30+
; CHECK: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
31+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
32+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
33+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
34+
; CHECK-NEXT: RET 0
35+
; NOSSE-LABEL: name: func
36+
; NOSSE: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
37+
; NOSSE-NEXT: MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
38+
; NOSSE-NEXT: $eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
39+
; NOSSE-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
40+
; NOSSE-NEXT: RET 0
41+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
42+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
43+
; reload
44+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
45+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
46+
RET 0
47+
...
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
# 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 --check-prefix=NOSSE %s
4+
5+
# Simple test with a single spill-reload pair (64-bit version):
6+
# spill stack.0
7+
# reload stack.0
8+
9+
--- |
10+
@D0 = dso_local local_unnamed_addr global i64 0, align 4
11+
@U0 = dso_local local_unnamed_addr global i64 0, align 4
12+
define void @func() { ret void }
13+
...
14+
---
15+
name: func
16+
alignment: 16
17+
tracksRegLiveness: true
18+
tracksDebugUserValues: true
19+
frameInfo:
20+
maxAlignment: 4
21+
stack:
22+
- { id: 0, type: spill-slot, size: 8, alignment: 4 }
23+
machineFunctionInfo: {}
24+
body: |
25+
26+
27+
bb.0:
28+
; spill
29+
; CHECK-LABEL: name: func
30+
; CHECK: $rax = MOV64rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s64) from @D0)
31+
; CHECK-NEXT: $xmm0 = MOV64toPQIrr $rax
32+
; CHECK-NEXT: $rax = MOVPQIto64rr $xmm0
33+
; CHECK-NEXT: MOV64mr $rip, 1, $noreg, @U0, $noreg, killed renamable $rax :: (store (s64) into @U0)
34+
; CHECK-NEXT: RET 0
35+
; NOSSE-LABEL: name: func
36+
; NOSSE: $rax = MOV64rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s64) from @D0)
37+
; NOSSE-NEXT: MOV64mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $rax :: (store (s64) into %stack.0)
38+
; NOSSE-NEXT: $rax = MOV64rm %stack.0, 1, $noreg, 0, $noreg :: (load (s64) from %stack.0)
39+
; NOSSE-NEXT: MOV64mr $rip, 1, $noreg, @U0, $noreg, killed renamable $rax :: (store (s64) into @U0)
40+
; NOSSE-NEXT: RET 0
41+
$rax = MOV64rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s64) from @D0)
42+
MOV64mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $rax :: (store (s64) into %stack.0)
43+
; reload
44+
$rax = MOV64rm %stack.0, 1, $noreg, 0, $noreg :: (load (s64) from %stack.0)
45+
MOV64mr $rip, 1, $noreg, @U0, $noreg, killed renamable $rax :: (store (s64) into @U0)
46+
RET 0
47+
...
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
# Simple test with two overlapping spill-reload pairs.
5+
# spill stack.0
6+
# spill stack.1
7+
# reload stack.0
8+
# reload stack.1
9+
10+
--- |
11+
@D0 = dso_local local_unnamed_addr global i32 0, align 4
12+
@D1 = dso_local local_unnamed_addr global i32 0, align 4
13+
@U0 = dso_local local_unnamed_addr global i32 0, align 4
14+
@U1 = dso_local local_unnamed_addr global i32 0, align 4
15+
define void @func() { ret void }
16+
...
17+
---
18+
name: func
19+
alignment: 16
20+
tracksRegLiveness: true
21+
tracksDebugUserValues: true
22+
frameInfo:
23+
maxAlignment: 4
24+
stack:
25+
- { id: 0, type: spill-slot, size: 4, alignment: 4 }
26+
- { id: 1, type: spill-slot, size: 4, alignment: 4 }
27+
machineFunctionInfo: {}
28+
body: |
29+
30+
bb.0:
31+
; CHECK-LABEL: name: func
32+
; CHECK: $eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
33+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
34+
; CHECK-NEXT: $eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1)
35+
; CHECK-NEXT: $xmm0 = MOVDI2PDIrr $eax
36+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
37+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
38+
; CHECK-NEXT: $eax = MOVPDI2DIrr $xmm0
39+
; CHECK-NEXT: MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1)
40+
; CHECK-NEXT: RET 0
41+
$eax = MOV32rm $rip, 1, $noreg, @D0, $noreg :: (dereferenceable load (s32) from @D0)
42+
MOV32mr %stack.0, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.0)
43+
$eax = MOV32rm $rip, 1, $noreg, @D1, $noreg :: (dereferenceable load (s32) from @D1)
44+
MOV32mr %stack.1, 1, $noreg, 0, $noreg, killed renamable $eax :: (store (s32) into %stack.1)
45+
46+
$eax = MOV32rm %stack.0, 1, $noreg, 0, $noreg :: (load (s32) from %stack.0)
47+
MOV32mr $rip, 1, $noreg, @U0, $noreg, killed renamable $eax :: (store (s32) into @U0)
48+
$eax = MOV32rm %stack.1, 1, $noreg, 0, $noreg :: (load (s32) from %stack.1)
49+
MOV32mr $rip, 1, $noreg, @U1, $noreg, killed renamable $eax :: (store (s32) into @U1)
50+
RET 0
51+
52+
...

0 commit comments

Comments
 (0)