Skip to content

Commit 3d51cf4

Browse files
committed
[EarlyCSE] Add tests for writeonly
1 parent ec150a9 commit 3d51cf4

File tree

1 file changed

+152
-1
lines changed

1 file changed

+152
-1
lines changed

llvm/test/Transforms/EarlyCSE/writeonly.ll

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
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
56
declare 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

Comments
 (0)