1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt < %s -passes='rewrite-statepoints-for-gc,verify<safepoint-ir>' -S | FileCheck %s
3
4declare void @use_obj(ptr addrspace(1)) "gc-leaf-function"
5declare void @do_safepoint()
6
7
8; Profitable test
9define i32 @test_remat(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
10; CHECK-LABEL: @test_remat(
11; CHECK-NEXT:  entry:
12; CHECK-NEXT:    [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
13; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
14; CHECK-NEXT:    [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
15; CHECK-NEXT:    br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
16; CHECK:       here:
17; CHECK-NEXT:    [[STATEPOINT_TOKEN1:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
18; CHECK-NEXT:    [[BASE_RELOCATED2:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN1]], i32 0, i32 0)
19; CHECK-NEXT:    br label [[MERGE:%.*]]
20; CHECK:       there:
21; CHECK-NEXT:    [[STATEPOINT_TOKEN3:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
22; CHECK-NEXT:    [[BASE_RELOCATED4:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN3]], i32 0, i32 0)
23; CHECK-NEXT:    br label [[MERGE]]
24; CHECK:       merge:
25; CHECK-NEXT:    [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE_RELOCATED2]], [[HERE]] ], [ [[BASE_RELOCATED4]], [[THERE]] ]
26; CHECK-NEXT:    [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 16
27; CHECK-NEXT:    [[RET:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT]], align 4
28; CHECK-NEXT:    ret i32 [[RET]]
29;
30entry:
31  %derived = getelementptr i32, ptr addrspace(1) %base, i32 16
32  call void @do_safepoint() [ "deopt"() ]
33  br i1 %cond, label %here, label %there
34
35here:
36  call void @do_safepoint() [ "deopt"() ]
37  br label %merge
38
39there:
40  call void @do_safepoint() [ "deopt"() ]
41  br label %merge
42
43merge:
44  %ret = load i32, ptr addrspace(1) %derived
45  ret i32 %ret
46}
47
48; Unprofitable test
49define i32 @test_many_uses(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
50; CHECK-LABEL: @test_many_uses(
51; CHECK-NEXT:  entry:
52; CHECK-NEXT:    [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
53; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
54; CHECK-NEXT:    [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
55; CHECK-NEXT:    [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 16
56; CHECK-NEXT:    br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
57; CHECK:       here:
58; CHECK-NEXT:    [[STATEPOINT_TOKEN3:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
59; CHECK-NEXT:    [[BASE_RELOCATED4:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN3]], i32 0, i32 0)
60; CHECK-NEXT:    [[DERIVED_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED4]], i32 16
61; CHECK-NEXT:    call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT1]])
62; CHECK-NEXT:    br label [[MERGE:%.*]]
63; CHECK:       there:
64; CHECK-NEXT:    call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT]])
65; CHECK-NEXT:    [[STATEPOINT_TOKEN5:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
66; CHECK-NEXT:    [[BASE_RELOCATED6:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN5]], i32 0, i32 0)
67; CHECK-NEXT:    [[DERIVED_REMAT2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED6]], i32 16
68; CHECK-NEXT:    br label [[MERGE]]
69; CHECK:       merge:
70; CHECK-NEXT:    [[DOT0:%.*]] = phi ptr addrspace(1) [ [[DERIVED_REMAT1]], [[HERE]] ], [ [[DERIVED_REMAT2]], [[THERE]] ]
71; CHECK-NEXT:    call void @use_obj(ptr addrspace(1) [[DOT0]])
72; CHECK-NEXT:    [[RET:%.*]] = load i32, ptr addrspace(1) [[DOT0]], align 4
73; CHECK-NEXT:    ret i32 [[RET]]
74;
75entry:
76  %derived = getelementptr i32, ptr addrspace(1) %base, i32 16
77  call void @do_safepoint() [ "deopt" () ]
78  br i1 %cond, label %here, label %there
79
80here:
81  call void @do_safepoint() [ "deopt"() ]
82  call void @use_obj(ptr addrspace(1) %derived)
83  br label %merge
84
85there:
86  call void @use_obj(ptr addrspace(1) %derived)
87  call void @do_safepoint() [ "deopt"() ]
88  br label %merge
89
90merge:
91  call void @use_obj(ptr addrspace(1) %derived)
92  %ret = load i32, ptr addrspace(1) %derived
93  ret i32 %ret
94}
95
96; Remat before phi - not implemented
97define i32 @test_phi(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
98; CHECK-LABEL: @test_phi(
99; CHECK-NEXT:  entry:
100; CHECK-NEXT:    [[DERIVED1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
101; CHECK-NEXT:    [[DERIVED2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE]], i32 32
102; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
103; CHECK-NEXT:    [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
104; CHECK-NEXT:    [[DERIVED2_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 32
105; CHECK-NEXT:    [[DERIVED1_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 16
106; CHECK-NEXT:    br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
107; CHECK:       here:
108; CHECK-NEXT:    br label [[MERGE:%.*]]
109; CHECK:       there:
110; CHECK-NEXT:    br label [[MERGE]]
111; CHECK:       merge:
112; CHECK-NEXT:    [[PHI1:%.*]] = phi ptr addrspace(1) [ [[DERIVED1_REMAT]], [[HERE]] ], [ [[DERIVED2_REMAT]], [[THERE]] ]
113; CHECK-NEXT:    [[RET:%.*]] = load i32, ptr addrspace(1) [[PHI1]], align 4
114; CHECK-NEXT:    ret i32 [[RET]]
115;
116entry:
117  %derived1 = getelementptr i32, ptr addrspace(1) %base, i32 16
118  %derived2 = getelementptr i32, ptr addrspace(1) %base, i32 32
119  call void @do_safepoint() [ "deopt"() ]
120  br i1 %cond, label %here, label %there
121
122here:
123  br label %merge
124
125there:
126  br label %merge
127
128merge:
129  %phi1 = phi ptr addrspace(1) [ %derived1, %here ], [ %derived2, %there ]
130  %ret = load i32, ptr addrspace(1) %phi1
131  ret i32 %ret
132}
133
134; Several uses per block
135; TODO: We could rematerialize once per block for several uses if there is not statepoint between
136define i32 @test_same_block_with_statepoint(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
137; CHECK-LABEL: @test_same_block_with_statepoint(
138; CHECK-NEXT:  entry:
139; CHECK-NEXT:    [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
140; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
141; CHECK-NEXT:    [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
142; CHECK-NEXT:    br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
143; CHECK:       here:
144; CHECK-NEXT:    [[DERIVED_REMAT3:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 16
145; CHECK-NEXT:    call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT3]])
146; CHECK-NEXT:    [[STATEPOINT_TOKEN4:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
147; CHECK-NEXT:    [[BASE_RELOCATED5:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN4]], i32 0, i32 0)
148; CHECK-NEXT:    [[DERIVED_REMAT2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED5]], i32 16
149; CHECK-NEXT:    call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT2]])
150; CHECK-NEXT:    [[DERIVED_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED5]], i32 16
151; CHECK-NEXT:    [[DUMMY:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT1]], align 4
152; CHECK-NEXT:    [[STATEPOINT_TOKEN6:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED5]]) ]
153; CHECK-NEXT:    [[BASE_RELOCATED7:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN6]], i32 0, i32 0)
154; CHECK-NEXT:    br label [[MERGE:%.*]]
155; CHECK:       there:
156; CHECK-NEXT:    [[STATEPOINT_TOKEN8:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
157; CHECK-NEXT:    [[BASE_RELOCATED9:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN8]], i32 0, i32 0)
158; CHECK-NEXT:    br label [[MERGE]]
159; CHECK:       merge:
160; CHECK-NEXT:    [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE_RELOCATED7]], [[HERE]] ], [ [[BASE_RELOCATED9]], [[THERE]] ]
161; CHECK-NEXT:    [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 16
162; CHECK-NEXT:    [[RET:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT]], align 4
163; CHECK-NEXT:    ret i32 [[RET]]
164;
165entry:
166  %derived = getelementptr i32, ptr addrspace(1) %base, i32 16
167  call void @do_safepoint() [ "deopt" () ]
168  br i1 %cond, label %here, label %there
169
170here:
171  call void @use_obj(ptr addrspace(1) %derived)
172  call void @do_safepoint() [ "deopt"() ]
173  call void @use_obj(ptr addrspace(1) %derived)
174  %dummy = load i32, ptr addrspace(1) %derived
175  call void @do_safepoint() [ "deopt" () ]
176  br label %merge
177
178there:
179  call void @do_safepoint() [ "deopt"() ]
180  br label %merge
181
182merge:
183  %ret = load i32, ptr addrspace(1) %derived
184  ret i32 %ret
185}
186
187; Test long chain with sub-chain rematerialized
188; TODO: If we rematerialized longer chain first (%v4), then shorter on (%v0) would become dead
189define void @test_chain(ptr addrspace(1) %base, i1 %cond1) gc "statepoint-example" {
190; CHECK-LABEL: @test_chain(
191; CHECK-NEXT:  entry:
192; CHECK-NEXT:    [[V0:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE:%.*]], i64 16
193; CHECK-NEXT:    [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(ptr addrspace(1) [[BASE]]), "gc-live"(ptr addrspace(1) [[BASE]]) ]
194; CHECK-NEXT:    [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
195; CHECK-NEXT:    br i1 [[COND1:%.*]], label [[BLOCK3:%.*]], label [[COMMON_RET:%.*]]
196; CHECK:       block3:
197; CHECK-NEXT:    [[V0_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE_RELOCATED]], i64 16
198; CHECK-NEXT:    [[V4:%.*]] = getelementptr i8, ptr addrspace(1) [[V0_REMAT]], i64 70
199; CHECK-NEXT:    [[STATEPOINT_TOKEN1:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(ptr addrspace(1) [[BASE_RELOCATED]]), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
200; CHECK-NEXT:    [[BASE_RELOCATED2:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN1]], i32 0, i32 0)
201; CHECK-NEXT:    [[V0_REMAT_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE_RELOCATED2]], i64 16
202; CHECK-NEXT:    [[V4_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[V0_REMAT_REMAT]], i64 70
203; CHECK-NEXT:    [[V5:%.*]] = load atomic i8, ptr addrspace(1) [[V4_REMAT]] unordered, align 2
204; CHECK-NEXT:    br label [[COMMON_RET]]
205; CHECK:       common.ret:
206; CHECK-NEXT:    ret void
207;
208entry:
209  %v0 = getelementptr i8, ptr addrspace(1) %base, i64 16
210  call void @do_safepoint() [ "deopt"(ptr addrspace(1) %base) ]
211  br i1 %cond1, label %block3, label %common.ret
212
213block3:
214  %v4 = getelementptr i8, ptr addrspace(1) %v0, i64 70
215  call void @do_safepoint() [ "deopt"(ptr addrspace(1) %base) ]
216  %v5 = load atomic i8, ptr addrspace(1) %v4 unordered, align 2
217  br label %common.ret
218
219common.ret:
220  ret void
221}
222