xref: /llvm-project/llvm/test/Transforms/LICM/promote-single-thread.ll (revision b58a697f3e19437f8525ec31a384c8901b0b736c)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -S -passes=licm < %s | FileCheck %s --check-prefixes=CHECK,MT
3; RUN: opt -S -passes=licm -licm-force-thread-model-single < %s | FileCheck %s --check-prefixes=CHECK,ST
4
5@g = external global i32
6@c = external constant i32
7
8declare void @capture(ptr)
9
10; Even in single-thread mode, can only perform load-only promotion for globals,
11; because we might not have provenance to write to the global. See
12; promote_global_noalias for an example of the issue.
13define void @promote_global(i1 %c, i1 %c2) {
14; CHECK-LABEL: @promote_global(
15; CHECK-NEXT:  entry:
16; CHECK-NEXT:    [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
17; CHECK-NEXT:    br label [[LOOP:%.*]]
18; CHECK:       loop:
19; CHECK-NEXT:    [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
20; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
21; CHECK:       if:
22; CHECK-NEXT:    [[V_INC:%.*]] = add i32 [[V_INC2]], 1
23; CHECK-NEXT:    store i32 [[V_INC]], ptr @g, align 4
24; CHECK-NEXT:    br label [[LATCH]]
25; CHECK:       latch:
26; CHECK-NEXT:    [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
27; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
28; CHECK:       exit:
29; CHECK-NEXT:    ret void
30;
31entry:
32  br label %loop
33
34loop:
35  br i1 %c, label %if, label %latch
36
37if:
38  %v = load i32, ptr @g
39  %v.inc = add i32 %v, 1
40  store i32 %v.inc, ptr @g
41  br label %latch
42
43latch:
44  br i1 %c2, label %exit, label %loop
45
46exit:
47  ret void
48}
49
50; The store can never be promoted here, because the global is constant, and
51; the store could trap.
52define void @promote_constant_global(i1 %c, i1 %c2) {
53; CHECK-LABEL: @promote_constant_global(
54; CHECK-NEXT:  entry:
55; CHECK-NEXT:    [[V:%.*]] = load i32, ptr @c, align 4
56; CHECK-NEXT:    [[V_INC:%.*]] = add i32 [[V]], 1
57; CHECK-NEXT:    br label [[LOOP:%.*]]
58; CHECK:       loop:
59; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH:%.*]]
60; CHECK:       if:
61; CHECK-NEXT:    store i32 [[V_INC]], ptr @c, align 4
62; CHECK-NEXT:    br label [[LATCH]]
63; CHECK:       latch:
64; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
65; CHECK:       exit:
66; CHECK-NEXT:    ret void
67;
68entry:
69  br label %loop
70
71loop:
72  br i1 %c, label %if, label %latch
73
74if:
75  %v = load i32, ptr @c
76  %v.inc = add i32 %v, 1
77  store i32 %v.inc, ptr @c
78  br label %latch
79
80latch:
81  br i1 %c2, label %exit, label %loop
82
83exit:
84  ret void
85}
86
87; if %c is false and %ptr == @g, then this should store 42 to the pointer.
88; However, if we perform load+store promotion, then we would instead store the
89; original value of the global.
90define void @promote_global_noalias(i1 %c, i1 %c2, ptr noalias %ptr) {
91; CHECK-LABEL: @promote_global_noalias(
92; CHECK-NEXT:  entry:
93; CHECK-NEXT:    [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
94; CHECK-NEXT:    br label [[LOOP:%.*]]
95; CHECK:       loop:
96; CHECK-NEXT:    [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
97; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
98; CHECK:       if:
99; CHECK-NEXT:    [[V_INC:%.*]] = add i32 [[V_INC2]], 1
100; CHECK-NEXT:    store i32 [[V_INC]], ptr @g, align 4
101; CHECK-NEXT:    br label [[LATCH]]
102; CHECK:       else:
103; CHECK-NEXT:    store i32 42, ptr [[PTR:%.*]], align 4
104; CHECK-NEXT:    br label [[LATCH]]
105; CHECK:       latch:
106; CHECK-NEXT:    [[V_INC1]] = phi i32 [ [[V_INC2]], [[ELSE]] ], [ [[V_INC]], [[IF]] ]
107; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
108; CHECK:       exit:
109; CHECK-NEXT:    ret void
110;
111entry:
112  br label %loop
113
114loop:
115  br i1 %c, label %if, label %else
116
117if:
118  %v = load i32, ptr @g
119  %v.inc = add i32 %v, 1
120  store i32 %v.inc, ptr @g
121  br label %latch
122
123else:
124  store i32 42, ptr %ptr
125  br label %latch
126
127latch:
128  br i1 %c2, label %exit, label %loop
129
130exit:
131  ret void
132}
133
134; In single-thread mode both loads and stores can be promoted. In multi-thread
135; mode only loads can be promoted, as a different thread might write to the
136; captured alloca.
137define void @promote_captured_alloca(i1 %c, i1 %c2) {
138; MT-LABEL: @promote_captured_alloca(
139; MT-NEXT:  entry:
140; MT-NEXT:    [[A:%.*]] = alloca i32, align 4
141; MT-NEXT:    call void @capture(ptr [[A]])
142; MT-NEXT:    [[A_PROMOTED:%.*]] = load i32, ptr [[A]], align 4
143; MT-NEXT:    br label [[LOOP:%.*]]
144; MT:       loop:
145; MT-NEXT:    [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[A_PROMOTED]], [[ENTRY:%.*]] ]
146; MT-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
147; MT:       if:
148; MT-NEXT:    [[V_INC:%.*]] = add i32 [[V_INC2]], 1
149; MT-NEXT:    store i32 [[V_INC]], ptr [[A]], align 4
150; MT-NEXT:    br label [[LATCH]]
151; MT:       latch:
152; MT-NEXT:    [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
153; MT-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
154; MT:       exit:
155; MT-NEXT:    ret void
156;
157; ST-LABEL: @promote_captured_alloca(
158; ST-NEXT:  entry:
159; ST-NEXT:    [[A:%.*]] = alloca i32, align 4
160; ST-NEXT:    call void @capture(ptr [[A]])
161; ST-NEXT:    [[A_PROMOTED:%.*]] = load i32, ptr [[A]], align 4
162; ST-NEXT:    br label [[LOOP:%.*]]
163; ST:       loop:
164; ST-NEXT:    [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[A_PROMOTED]], [[ENTRY:%.*]] ]
165; ST-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
166; ST:       if:
167; ST-NEXT:    [[V_INC:%.*]] = add i32 [[V_INC2]], 1
168; ST-NEXT:    br label [[LATCH]]
169; ST:       latch:
170; ST-NEXT:    [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
171; ST-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
172; ST:       exit:
173; ST-NEXT:    [[V_INC1_LCSSA:%.*]] = phi i32 [ [[V_INC1]], [[LATCH]] ]
174; ST-NEXT:    store i32 [[V_INC1_LCSSA]], ptr [[A]], align 4
175; ST-NEXT:    ret void
176;
177entry:
178  %a = alloca i32
179  call void @capture(ptr %a)
180  br label %loop
181
182loop:
183  br i1 %c, label %if, label %latch
184
185if:
186  %v = load i32, ptr %a
187  %v.inc = add i32 %v, 1
188  store i32 %v.inc, ptr %a
189  br label %latch
190
191latch:
192  br i1 %c2, label %exit, label %loop
193
194exit:
195  ret void
196}
197
198; The store cannot be promoted here, because we do not know whether the
199; argument memory is writable.
200define void @promote_arg(ptr noalias dereferenceable(4) align 4 %arg, i1 %c, i1 %c2) {
201; CHECK-LABEL: @promote_arg(
202; CHECK-NEXT:  entry:
203; CHECK-NEXT:    [[ARG_PROMOTED:%.*]] = load i32, ptr [[ARG:%.*]], align 4
204; CHECK-NEXT:    br label [[LOOP:%.*]]
205; CHECK:       loop:
206; CHECK-NEXT:    [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[ARG_PROMOTED]], [[ENTRY:%.*]] ]
207; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
208; CHECK:       if:
209; CHECK-NEXT:    [[V_INC:%.*]] = add i32 [[V_INC2]], 1
210; CHECK-NEXT:    store i32 [[V_INC]], ptr [[ARG]], align 4
211; CHECK-NEXT:    br label [[LATCH]]
212; CHECK:       latch:
213; CHECK-NEXT:    [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
214; CHECK-NEXT:    br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
215; CHECK:       exit:
216; CHECK-NEXT:    ret void
217;
218entry:
219  br label %loop
220
221loop:
222  br i1 %c, label %if, label %latch
223
224if:
225  %v = load i32, ptr %arg
226  %v.inc = add i32 %v, 1
227  store i32 %v.inc, ptr %arg
228  br label %latch
229
230latch:
231  br i1 %c2, label %exit, label %loop
232
233exit:
234  ret void
235}
236