11; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2- ; RUN: opt -S -passes=early-cse -earlycse-debug-hash < %s | FileCheck %s
2+ ; RUN: opt -S -passes=early-cse -earlycse-debug-hash < %s | FileCheck %s --check-prefixes=CHECK,NO-MSSA
3+ ; RUN: opt -S -passes='early-cse<memssa>' < %s | FileCheck %s --check-prefixes=CHECK,MSSA
34
45@var = global i32 undef
56declare void @foo () nounwind
@@ -15,3 +16,153 @@ define void @test() {
1516 store i32 2 , ptr @var
1617 ret void
1718}
19+
20+ declare void @writeonly_void () memory(write)
21+
22+ ; Can CSE writeonly calls, including non-nounwind/willreturn.
23+ define void @writeonly_cse () {
24+ ; CHECK-LABEL: @writeonly_cse(
25+ ; CHECK-NEXT: call void @writeonly_void()
26+ ; CHECK-NEXT: call void @writeonly_void()
27+ ; CHECK-NEXT: ret void
28+ ;
29+ call void @writeonly_void ()
30+ call void @writeonly_void ()
31+ ret void
32+ }
33+
34+ ; Can CSE, loads do not matter.
35+ define i32 @writeonly_cse_intervening_load (ptr %p ) {
36+ ; CHECK-LABEL: @writeonly_cse_intervening_load(
37+ ; CHECK-NEXT: call void @writeonly_void()
38+ ; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
39+ ; CHECK-NEXT: call void @writeonly_void()
40+ ; CHECK-NEXT: ret i32 [[V]]
41+ ;
42+ call void @writeonly_void ()
43+ %v = load i32 , ptr %p
44+ call void @writeonly_void ()
45+ ret i32 %v
46+ }
47+
48+ ; Cannot CSE, the store may be to the same memory.
49+ define void @writeonly_cse_intervening_store (ptr %p ) {
50+ ; CHECK-LABEL: @writeonly_cse_intervening_store(
51+ ; CHECK-NEXT: call void @writeonly_void()
52+ ; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
53+ ; CHECK-NEXT: call void @writeonly_void()
54+ ; CHECK-NEXT: ret void
55+ ;
56+ call void @writeonly_void ()
57+ store i32 0 , ptr %p
58+ call void @writeonly_void ()
59+ ret void
60+ }
61+
62+ ; Can CSE, the store does not alias the writeonly call.
63+ define void @writeonly_cse_intervening_noalias_store (ptr noalias %p ) {
64+ ; CHECK-LABEL: @writeonly_cse_intervening_noalias_store(
65+ ; CHECK-NEXT: call void @writeonly_void()
66+ ; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
67+ ; CHECK-NEXT: call void @writeonly_void()
68+ ; CHECK-NEXT: ret void
69+ ;
70+ call void @writeonly_void ()
71+ store i32 0 , ptr %p
72+ call void @writeonly_void ()
73+ ret void
74+ }
75+
76+ ; Cannot CSE loads across writeonly call.
77+ define i32 @load_cse_across_writeonly (ptr %p ) {
78+ ; CHECK-LABEL: @load_cse_across_writeonly(
79+ ; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4
80+ ; CHECK-NEXT: call void @writeonly_void()
81+ ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4
82+ ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
83+ ; CHECK-NEXT: ret i32 [[RES]]
84+ ;
85+ %v1 = load i32 , ptr %p
86+ call void @writeonly_void ()
87+ %v2 = load i32 , ptr %p
88+ %res = sub i32 %v1 , %v2
89+ ret i32 %res
90+ }
91+
92+ ; Can CSE loads across eliminated writeonly call.
93+ define i32 @load_cse_across_csed_writeonly (ptr %p ) {
94+ ; CHECK-LABEL: @load_cse_across_csed_writeonly(
95+ ; CHECK-NEXT: call void @writeonly_void()
96+ ; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4
97+ ; CHECK-NEXT: call void @writeonly_void()
98+ ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4
99+ ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
100+ ; CHECK-NEXT: ret i32 [[RES]]
101+ ;
102+ call void @writeonly_void ()
103+ %v1 = load i32 , ptr %p
104+ call void @writeonly_void ()
105+ %v2 = load i32 , ptr %p
106+ %res = sub i32 %v1 , %v2
107+ ret i32 %res
108+ }
109+
110+ declare i32 @writeonly (ptr %p ) memory(write)
111+
112+ ; Can CSE writeonly calls with arg and return.
113+ define i32 @writeonly_ret_cse (ptr %p ) {
114+ ; CHECK-LABEL: @writeonly_ret_cse(
115+ ; CHECK-NEXT: [[V1:%.*]] = call i32 @writeonly(ptr [[P:%.*]])
116+ ; CHECK-NEXT: [[V2:%.*]] = call i32 @writeonly(ptr [[P]])
117+ ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
118+ ; CHECK-NEXT: ret i32 [[RES]]
119+ ;
120+ %v1 = call i32 @writeonly (ptr %p )
121+ %v2 = call i32 @writeonly (ptr %p )
122+ %res = sub i32 %v1 , %v2
123+ ret i32 %res
124+ }
125+
126+ ; Cannot CSE writeonly calls with different arguments.
127+ define i32 @writeonly_different_args (ptr %p1 , ptr %p2 ) {
128+ ; CHECK-LABEL: @writeonly_different_args(
129+ ; CHECK-NEXT: [[V1:%.*]] = call i32 @writeonly(ptr [[P1:%.*]])
130+ ; CHECK-NEXT: [[V2:%.*]] = call i32 @writeonly(ptr [[P2:%.*]])
131+ ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
132+ ; CHECK-NEXT: ret i32 [[RES]]
133+ ;
134+ %v1 = call i32 @writeonly (ptr %p1 )
135+ %v2 = call i32 @writeonly (ptr %p2 )
136+ %res = sub i32 %v1 , %v2
137+ ret i32 %res
138+ }
139+
140+ declare void @callee ()
141+
142+ ; These are weird cases where the same call is both readonly and writeonly
143+ ; based on call-site attributes. I believe this implies that both calls are
144+ ; actually readnone and safe to CSE, but leave them alone to be conservative.
145+ define void @readonly_and_writeonly () {
146+ ; CHECK-LABEL: @readonly_and_writeonly(
147+ ; CHECK-NEXT: call void @callee() #[[ATTR2:[0-9]+]]
148+ ; CHECK-NEXT: call void @callee() #[[ATTR1]]
149+ ; CHECK-NEXT: ret void
150+ ;
151+ call void @callee () memory(read)
152+ call void @callee () memory(write)
153+ ret void
154+ }
155+
156+ define void @writeonly_and_readonly () {
157+ ; CHECK-LABEL: @writeonly_and_readonly(
158+ ; CHECK-NEXT: call void @callee() #[[ATTR1]]
159+ ; CHECK-NEXT: call void @callee() #[[ATTR2]]
160+ ; CHECK-NEXT: ret void
161+ ;
162+ call void @callee () memory(write)
163+ call void @callee () memory(read)
164+ ret void
165+ }
166+ ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
167+ ; MSSA: {{.*}}
168+ ; NO-MSSA: {{.*}}
0 commit comments