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,148 @@ 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: ret void
27+ ;
28+ call void @writeonly_void ()
29+ call void @writeonly_void ()
30+ ret void
31+ }
32+
33+ ; Can CSE, loads do not matter.
34+ define i32 @writeonly_cse_intervening_load (ptr %p ) {
35+ ; CHECK-LABEL: @writeonly_cse_intervening_load(
36+ ; CHECK-NEXT: call void @writeonly_void()
37+ ; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
38+ ; CHECK-NEXT: ret i32 [[V]]
39+ ;
40+ call void @writeonly_void ()
41+ %v = load i32 , ptr %p
42+ call void @writeonly_void ()
43+ ret i32 %v
44+ }
45+
46+ ; Cannot CSE, the store may be to the same memory.
47+ define void @writeonly_cse_intervening_store (ptr %p ) {
48+ ; CHECK-LABEL: @writeonly_cse_intervening_store(
49+ ; CHECK-NEXT: call void @writeonly_void()
50+ ; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
51+ ; CHECK-NEXT: call void @writeonly_void()
52+ ; CHECK-NEXT: ret void
53+ ;
54+ call void @writeonly_void ()
55+ store i32 0 , ptr %p
56+ call void @writeonly_void ()
57+ ret void
58+ }
59+
60+ ; Can CSE, the store does not alias the writeonly call.
61+ define void @writeonly_cse_intervening_noalias_store (ptr noalias %p ) {
62+ ; NO-MSSA-LABEL: @writeonly_cse_intervening_noalias_store(
63+ ; NO-MSSA-NEXT: call void @writeonly_void()
64+ ; NO-MSSA-NEXT: store i32 0, ptr [[P:%.*]], align 4
65+ ; NO-MSSA-NEXT: call void @writeonly_void()
66+ ; NO-MSSA-NEXT: ret void
67+ ;
68+ ; MSSA-LABEL: @writeonly_cse_intervening_noalias_store(
69+ ; MSSA-NEXT: call void @writeonly_void()
70+ ; MSSA-NEXT: store i32 0, ptr [[P:%.*]], align 4
71+ ; MSSA-NEXT: ret void
72+ ;
73+ call void @writeonly_void ()
74+ store i32 0 , ptr %p
75+ call void @writeonly_void ()
76+ ret void
77+ }
78+
79+ ; Cannot CSE loads across writeonly call.
80+ define i32 @load_cse_across_writeonly (ptr %p ) {
81+ ; CHECK-LABEL: @load_cse_across_writeonly(
82+ ; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4
83+ ; CHECK-NEXT: call void @writeonly_void()
84+ ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4
85+ ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
86+ ; CHECK-NEXT: ret i32 [[RES]]
87+ ;
88+ %v1 = load i32 , ptr %p
89+ call void @writeonly_void ()
90+ %v2 = load i32 , ptr %p
91+ %res = sub i32 %v1 , %v2
92+ ret i32 %res
93+ }
94+
95+ ; Can CSE loads across eliminated writeonly call.
96+ define i32 @load_cse_across_csed_writeonly (ptr %p ) {
97+ ; CHECK-LABEL: @load_cse_across_csed_writeonly(
98+ ; CHECK-NEXT: call void @writeonly_void()
99+ ; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P:%.*]], align 4
100+ ; CHECK-NEXT: ret i32 0
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: [[V2:%.*]] = call i32 @writeonly(ptr [[P:%.*]])
116+ ; CHECK-NEXT: ret i32 0
117+ ;
118+ %v1 = call i32 @writeonly (ptr %p )
119+ %v2 = call i32 @writeonly (ptr %p )
120+ %res = sub i32 %v1 , %v2
121+ ret i32 %res
122+ }
123+
124+ ; Cannot CSE writeonly calls with different arguments.
125+ define i32 @writeonly_different_args (ptr %p1 , ptr %p2 ) {
126+ ; CHECK-LABEL: @writeonly_different_args(
127+ ; CHECK-NEXT: [[V1:%.*]] = call i32 @writeonly(ptr [[P1:%.*]])
128+ ; CHECK-NEXT: [[V2:%.*]] = call i32 @writeonly(ptr [[P2:%.*]])
129+ ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
130+ ; CHECK-NEXT: ret i32 [[RES]]
131+ ;
132+ %v1 = call i32 @writeonly (ptr %p1 )
133+ %v2 = call i32 @writeonly (ptr %p2 )
134+ %res = sub i32 %v1 , %v2
135+ ret i32 %res
136+ }
137+
138+ declare void @callee ()
139+
140+ ; These are weird cases where the same call is both readonly and writeonly
141+ ; based on call-site attributes. I believe this implies that both calls are
142+ ; actually readnone and safe to CSE, but leave them alone to be conservative.
143+ define void @readonly_and_writeonly () {
144+ ; CHECK-LABEL: @readonly_and_writeonly(
145+ ; CHECK-NEXT: call void @callee() #[[ATTR2:[0-9]+]]
146+ ; CHECK-NEXT: call void @callee() #[[ATTR1]]
147+ ; CHECK-NEXT: ret void
148+ ;
149+ call void @callee () memory(read)
150+ call void @callee () memory(write)
151+ ret void
152+ }
153+
154+ define void @writeonly_and_readonly () {
155+ ; CHECK-LABEL: @writeonly_and_readonly(
156+ ; CHECK-NEXT: call void @callee() #[[ATTR1]]
157+ ; CHECK-NEXT: call void @callee() #[[ATTR2]]
158+ ; CHECK-NEXT: ret void
159+ ;
160+ call void @callee () memory(write)
161+ call void @callee () memory(read)
162+ ret void
163+ }
0 commit comments