aboutsummaryrefslogtreecommitdiff
path: root/llvm/test/Transforms/RewriteStatepointsForGC/liveness-basics.ll
blob: e827ff6e6e6a641c7baf6f5e4503fd664b3089d9 (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
; A collection of liveness test cases to ensure we're reporting the
; correct live values at statepoints
; RUN: opt -passes=rewrite-statepoints-for-gc -spp-rematerialization-threshold=0 -S < %s | FileCheck %s

; Tests to make sure we consider %obj live in both the taken and untaken
; predeccessor of merge.

define ptr addrspace(1) @test1(i1 %cmp, ptr addrspace(1) %obj) gc "statepoint-example" {
; CHECK-LABEL: @test1
entry:
  br i1 %cmp, label %taken, label %untaken

taken:                                            ; preds = %entry
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated = call coldcc ptr addrspace(1)
; CHECK-NEXT: br label %merge
  call void @foo() [ "deopt"() ]
  br label %merge

untaken:                                          ; preds = %entry
; CHECK-LABEL: untaken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated2 = call coldcc ptr addrspace(1)
; CHECK-NEXT: br label %merge
  call void @foo() [ "deopt"() ]
  br label %merge

merge:                                            ; preds = %untaken, %taken
; CHECK-LABEL: merge:
; CHECK-NEXT: %.0 = phi ptr addrspace(1) [ %obj.relocated, %taken ], [ %obj.relocated2, %untaken ]
; CHECK-NEXT: ret ptr addrspace(1) %.0
; A local kill should not effect liveness in predecessor block
  ret ptr addrspace(1) %obj
}

define ptr addrspace(1) @test2(i1 %cmp, ptr %loc) gc "statepoint-example" {
; CHECK-LABEL: @test2
entry:
; CHECK-LABEL: entry:
; CHECK-NEXT:  gc.statepoint
; CHECK-NEXT:  br
  call void @foo() [ "deopt"() ]
  br i1 %cmp, label %taken, label %untaken

taken:                                            ; preds = %entry
; CHECK-LABEL: taken:
; CHECK-NEXT:  %obj = load
; CHECK-NEXT:  gc.statepoint
; CHECK-NEXT:  gc.relocate
; CHECK-NEXT:  ret ptr addrspace(1) %obj.relocated
; A local kill should effect values live from a successor phi.  Also, we
; should only propagate liveness from a phi to the appropriate predecessors.
  %obj = load ptr addrspace(1), ptr %loc
  call void @foo() [ "deopt"() ]
  ret ptr addrspace(1) %obj

untaken:                                          ; preds = %entry
  ret ptr addrspace(1) null
}

define ptr addrspace(1) @test3(i1 %cmp, ptr %loc) gc "statepoint-example" {
; CHECK-LABEL: @test3
entry:
  br i1 %cmp, label %taken, label %untaken

taken:                                            ; preds = %entry
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj = load
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated = call coldcc ptr addrspace(1)
; CHECK-NEXT: br label %merge
  call void @foo() [ "deopt"() ]
  %obj = load ptr addrspace(1), ptr %loc
  call void @foo() [ "deopt"() ]
  br label %merge

untaken:                                          ; preds = %entry
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: br label %merge
; A base pointer must be live if it is needed at a later statepoint,
; even if the base pointer is otherwise unused.
  call void @foo() [ "deopt"() ]
  br label %merge

merge:                                            ; preds = %untaken, %taken
  %phi = phi ptr addrspace(1) [ %obj, %taken ], [ null, %untaken ]
  ret ptr addrspace(1) %phi
}

define ptr addrspace(1) @test4(i1 %cmp, ptr 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.relocated2 =

; Note: It's legal to relocate obj again, but not strictly needed
; CHECK-NEXT:  %obj.relocated3 =
; CHECK-NEXT:  ret ptr addrspace(1) %derived.relocated2
;
; 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.
  %derived = getelementptr i64, ptr addrspace(1) %obj, i64 8
  call void @foo() [ "deopt"() ]
  call void @foo() [ "deopt"() ]
  ret ptr addrspace(1) %derived
}

declare void @consume(...) readonly "gc-leaf-function"

define ptr addrspace(1) @test5(i1 %cmp, ptr addrspace(1) %obj) gc "statepoint-example" {
; CHECK-LABEL: @test5
entry:
  br i1 %cmp, label %taken, label %untaken

taken:                                            ; preds = %entry
; CHECK-LABEL: taken:
; CHECK-NEXT: gc.statepoint
; CHECK-NEXT: %obj.relocated = call coldcc ptr addrspace(1)
; CHECK-NEXT: br label %merge
  call void @foo() [ "deopt"() ]
  br label %merge

untaken:                                          ; preds = %entry
; CHECK-LABEL: untaken:
; CHECK-NEXT: br label %merge
  br label %merge

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

final:                                            ; preds = %merge
; CHECK-LABEL: final:
; CHECK-NEXT: @consume
; CHECK-NEXT: ret ptr addrspace(1) %.0
  call void (...) @consume(ptr addrspace(1) %obj2a)
  ret ptr addrspace(1) %obj
}

declare void @foo()