summaryrefslogtreecommitdiffstats
path: root/test/Transforms/RewriteStatepointsForGC/liveness-basics.ll
blob: 0990c68daf9afefc45a0dbe35ecfc6c916a36688 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
; A collection of liveness test cases to ensure we're reporting the
; correct live values at statepoints
; RUN: opt -rewrite-statepoints-for-gc -S < %s | FileCheck %s


; Tests to make sure we consider %obj live in both the taken and untaken 
; predeccessor of merge.
define i64 addrspace(1)* @test1(i1 %cmp, i64 addrspace(1)* %obj) gc "statepoint-example" {
; CHECK-LABEL: @test1
entry:
  br i1 %cmp, label %taken, label %untaken

taken:
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated = call coldcc i64 addrspace(1)*
; CHECK-NEXT: br label %merge
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  br label %merge

untaken:
; CHECK-LABEL: untaken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated1 = call coldcc i64 addrspace(1)*
; CHECK-NEXT: br label %merge
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  br label %merge

merge:
; CHECK-LABEL: merge:
; CHECK-NEXT: %.0 = phi i64 addrspace(1)* [ %obj.relocated, %taken ], [ %obj.relocated1, %untaken ]
; CHECK-NEXT: ret i64 addrspace(1)* %.0
  ret i64 addrspace(1)* %obj
}

; A local kill should not effect liveness in predecessor block
define i64 addrspace(1)* @test2(i1 %cmp, i64 addrspace(1)** %loc) gc "statepoint-example" {
; CHECK-LABEL: @test2
entry:
; CHECK-LABEL: entry:
; CHECK-NEXT:  gc.statepoint
; CHECK-NEXT:  br
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  br i1 %cmp, label %taken, label %untaken

taken:
; CHECK-LABEL: taken:
; CHECK-NEXT:  %obj = load
; CHECK-NEXT:  gc.statepoint
; CHECK-NEXT:  gc.relocate
; CHECK-NEXT:  ret i64 addrspace(1)* %obj.relocated

  %obj = load i64 addrspace(1)*, i64 addrspace(1)** %loc
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  ret i64 addrspace(1)* %obj

untaken:
  ret i64 addrspace(1)* null
}

; A local kill should effect values live from a successor phi.  Also, we
; should only propagate liveness from a phi to the appropriate predecessors.
define i64 addrspace(1)* @test3(i1 %cmp, i64 addrspace(1)** %loc) gc "statepoint-example" {
; CHECK-LABEL: @test3
entry:
  br i1 %cmp, label %taken, label %untaken

taken:
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj = load
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated = call coldcc i64 addrspace(1)*
; CHECK-NEXT: br label %merge
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  %obj = load i64 addrspace(1)*, i64 addrspace(1)** %loc
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  br label %merge

untaken:
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: br label %merge
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  br label %merge

merge:
  %phi = phi i64 addrspace(1)* [ %obj, %taken ], [ null, %untaken ]
  ret i64 addrspace(1)* %phi
}

; A base pointer must be live if it is needed at a later statepoint,
; even if the base pointer is otherwise unused.
define i64 addrspace(1)* @test4(i1 %cmp, i64 addrspace(1)* %obj) gc "statepoint-example" {
; CHECK-LABEL: @test4
entry:
; CHECK-LABEL: entry:
; CHECK-NEXT:  %derived = getelementptr
; CHECK-NEXT:  gc.statepoint
; CHECK-NEXT:  %derived.relocated = 
; CHECK-NEXT:  %obj.relocated =
; CHECK-NEXT:  gc.statepoint
; CHECK-NEXT:  %derived.relocated1 = 
; Note: It's legal to relocate obj again, but not strictly needed
; CHECK-NEXT:  %obj.relocated2 =
; CHECK-NEXT:  ret i64 addrspace(1)* %derived.relocated1
; 
  %derived = getelementptr i64, i64 addrspace(1)* %obj, i64 8
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)

  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  ret i64 addrspace(1)* %derived
}

declare void @consume(...) readonly

; Make sure that a phi def visited during iteration is considered a kill.
; Also, liveness after base pointer analysis can change based on new uses,
; not just new defs.
define i64 addrspace(1)* @test5(i1 %cmp, i64 addrspace(1)* %obj) gc "statepoint-example" {
; CHECK-LABEL: @test5
entry:
  br i1 %cmp, label %taken, label %untaken

taken:
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated = call coldcc i64 addrspace(1)*
; CHECK-NEXT: br label %merge
  call i32 (void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()* @foo, i32 0, i32 0, i32 0)
  br label %merge

untaken:
; CHECK-LABEL: untaken:
; CHECK-NEXT: br label %merge
  br label %merge

merge:
; CHECK-LABEL: merge:
; CHECK-NEXT: %.0 = phi i64 addrspace(1)*
; CHECK-NEXT: %obj2a = phi
; CHECK-NEXT: @consume
; CHECK-NEXT: br label %final
  %obj2a = phi i64 addrspace(1)* [ %obj, %taken ], [null, %untaken]
  call void (...) @consume(i64 addrspace(1)* %obj2a)
  br label %final
final:
; CHECK-LABEL: final:
; CHECK-NEXT: @consume
; CHECK-NEXT: ret i64 addrspace(1)* %.0
  call void (...) @consume(i64 addrspace(1)* %obj2a)
  ret i64 addrspace(1)* %obj
}

declare void @foo()

declare i32 @llvm.experimental.gc.statepoint.p0f_isVoidf(void ()*, i32, i32, ...)