1; RUN: opt -S -early-cse < %s | FileCheck %s
2
3declare void @llvm.experimental.guard(i1,...)
4
5define i32 @test0(i32* %ptr, i1 %cond) {
6; We can do store to load forwarding over a guard, since it does not
7; clobber memory
8
9; CHECK-LABEL: @test0(
10; CHECK-NEXT:  store i32 40, i32* %ptr
11; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
12; CHECK-NEXT:  ret i32 40
13
14  store i32 40, i32* %ptr
15  call void(i1,...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
16  %rval = load i32, i32* %ptr
17  ret i32 %rval
18}
19
20define i32 @test1(i32* %val, i1 %cond) {
21; We can CSE loads over a guard, since it does not clobber memory
22
23; CHECK-LABEL: @test1(
24; CHECK-NEXT:  %val0 = load i32, i32* %val
25; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
26; CHECK-NEXT:  ret i32 0
27
28  %val0 = load i32, i32* %val
29  call void(i1,...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
30  %val1 = load i32, i32* %val
31  %rval = sub i32 %val0, %val1
32  ret i32 %rval
33}
34
35define i32 @test2() {
36; Guards on "true" get removed
37
38; CHECK-LABEL: @test2(
39; CHECK-NEXT: ret i32 0
40  call void(i1, ...) @llvm.experimental.guard(i1 true) [ "deopt"() ]
41  ret i32 0
42}
43
44define i32 @test3(i32 %val) {
45; After a guard has executed the condition it was guarding is known to
46; be true.
47
48; CHECK-LABEL: @test3(
49; CHECK-NEXT:  %cond0 = icmp slt i32 %val, 40
50; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ]
51; CHECK-NEXT:  ret i32 -1
52
53  %cond0 = icmp slt i32 %val, 40
54  call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ]
55  %cond1 = icmp slt i32 %val, 40
56  call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
57
58  %cond2 = icmp slt i32 %val, 40
59  %rval = sext i1 %cond2 to i32
60  ret i32 %rval
61}
62
63define i32 @test3.unhandled(i32 %val) {
64; After a guard has executed the condition it was guarding is known to
65; be true.
66
67; CHECK-LABEL: @test3.unhandled(
68; CHECK-NEXT:  %cond0 = icmp slt i32 %val, 40
69; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ]
70; CHECK-NEXT:  %cond1 = icmp sge i32 %val, 40
71; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
72; CHECK-NEXT:  ret i32 0
73
74; Demonstrates a case we do not yet handle (it is legal to fold %cond2
75; to false)
76  %cond0 = icmp slt i32 %val, 40
77  call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ]
78  %cond1 = icmp sge i32 %val, 40
79  call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
80  ret i32 0
81}
82
83define i32 @test4(i32 %val, i1 %c) {
84; Same as test3, but with some control flow involved.
85
86; CHECK-LABEL: @test4(
87; CHECK: entry:
88; CHECK-NEXT:  %cond0 = icmp slt i32 %val, 40
89; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond0
90; CHECK-NEXT:  br label %bb0
91
92; CHECK:     bb0:
93; CHECK-NEXT:  %cond2 = icmp ult i32 %val, 200
94; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond2
95; CHECK-NEXT:  br i1 %c, label %left, label %right
96
97; CHECK:     left:
98; CHECK-NEXT:  ret i32 0
99
100; CHECK:     right:
101; CHECK-NEXT:  ret i32 20
102
103entry:
104  %cond0 = icmp slt i32 %val, 40
105  call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ]
106  %cond1 = icmp slt i32 %val, 40
107  call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
108  br label %bb0
109
110bb0:
111  %cond2 = icmp ult i32 %val, 200
112  call void(i1,...) @llvm.experimental.guard(i1 %cond2) [ "deopt"() ]
113  br i1 %c, label %left, label %right
114
115left:
116  %cond3 = icmp ult i32 %val, 200
117  call void(i1,...) @llvm.experimental.guard(i1 %cond3) [ "deopt"() ]
118  ret i32 0
119
120right:
121 ret i32 20
122}
123
124define i32 @test5(i32 %val, i1 %c) {
125; Same as test4, but the %left block has mutliple predecessors.
126
127; CHECK-LABEL: @test5(
128
129; CHECK: entry:
130; CHECK-NEXT:  %cond0 = icmp slt i32 %val, 40
131; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond0
132; CHECK-NEXT:  br label %bb0
133
134; CHECK: bb0:
135; CHECK-NEXT:  %cond2 = icmp ult i32 %val, 200
136; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %cond2
137; CHECK-NEXT:  br i1 %c, label %left, label %right
138
139; CHECK: left:
140; CHECK-NEXT:  br label %right
141
142; CHECK: right:
143; CHECK-NEXT:  br label %left
144
145entry:
146  %cond0 = icmp slt i32 %val, 40
147  call void(i1,...) @llvm.experimental.guard(i1 %cond0) [ "deopt"() ]
148  %cond1 = icmp slt i32 %val, 40
149  call void(i1,...) @llvm.experimental.guard(i1 %cond1) [ "deopt"() ]
150  br label %bb0
151
152bb0:
153  %cond2 = icmp ult i32 %val, 200
154  call void(i1,...) @llvm.experimental.guard(i1 %cond2) [ "deopt"() ]
155  br i1 %c, label %left, label %right
156
157left:
158  %cond3 = icmp ult i32 %val, 200
159  call void(i1,...) @llvm.experimental.guard(i1 %cond3) [ "deopt"() ]
160  br label %right
161
162right:
163  br label %left
164}
165
166define void @test6(i1 %c, i32* %ptr) {
167; Check that we do not DSE over calls to @llvm.experimental.guard.
168; Guard intrinsics do _read_ memory, so th call to guard below needs
169; to see the store of 500 to %ptr
170
171; CHECK-LABEL: @test6(
172; CHECK-NEXT:  store i32 500, i32* %ptr
173; CHECK-NEXT:  call void (i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"() ]
174; CHECK-NEXT:  store i32 600, i32* %ptr
175
176
177  store i32 500, i32* %ptr
178  call void(i1,...) @llvm.experimental.guard(i1 %c) [ "deopt"() ]
179  store i32 600, i32* %ptr
180  ret void
181}
182