1; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt
2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+atomics,+sign-ext | FileCheck %s
3
4; Currently all wasm atomic memory access instructions are sequentially
5; consistent, so even if LLVM IR specifies weaker orderings than that, we
6; should upgrade them to sequential ordering and treat them in the same way.
7
8target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
9target triple = "wasm32-unknown-unknown"
10
11;===----------------------------------------------------------------------------
12; Atomic loads
13;===----------------------------------------------------------------------------
14
15; The 'release' and 'acq_rel' orderings are not valid on load instructions.
16
17; CHECK-LABEL: load_i32_unordered:
18; CHECK: i32.atomic.load $push0=, 0($0){{$}}
19; CHECK-NEXT: return $pop0{{$}}
20define i32 @load_i32_unordered(i32 *%p) {
21  %v = load atomic i32, i32* %p unordered, align 4
22  ret i32 %v
23}
24
25; CHECK-LABEL: load_i32_monotonic:
26; CHECK: i32.atomic.load $push0=, 0($0){{$}}
27; CHECK-NEXT: return $pop0{{$}}
28define i32 @load_i32_monotonic(i32 *%p) {
29  %v = load atomic i32, i32* %p monotonic, align 4
30  ret i32 %v
31}
32
33; CHECK-LABEL: load_i32_acquire:
34; CHECK: i32.atomic.load $push0=, 0($0){{$}}
35; CHECK-NEXT: return $pop0{{$}}
36define i32 @load_i32_acquire(i32 *%p) {
37  %v = load atomic i32, i32* %p acquire, align 4
38  ret i32 %v
39}
40
41; CHECK-LABEL: load_i32_seq_cst:
42; CHECK: i32.atomic.load $push0=, 0($0){{$}}
43; CHECK-NEXT: return $pop0{{$}}
44define i32 @load_i32_seq_cst(i32 *%p) {
45  %v = load atomic i32, i32* %p seq_cst, align 4
46  ret i32 %v
47}
48
49;===----------------------------------------------------------------------------
50; Atomic stores
51;===----------------------------------------------------------------------------
52
53; The 'acquire' and 'acq_rel' orderings aren’t valid on store instructions.
54
55; CHECK-LABEL: store_i32_unordered:
56; CHECK-NEXT: .functype store_i32_unordered (i32, i32) -> (){{$}}
57; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}}
58; CHECK-NEXT: return{{$}}
59define void @store_i32_unordered(i32 *%p, i32 %v) {
60  store atomic i32 %v, i32* %p unordered, align 4
61  ret void
62}
63
64; CHECK-LABEL: store_i32_monotonic:
65; CHECK-NEXT: .functype store_i32_monotonic (i32, i32) -> (){{$}}
66; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}}
67; CHECK-NEXT: return{{$}}
68define void @store_i32_monotonic(i32 *%p, i32 %v) {
69  store atomic i32 %v, i32* %p monotonic, align 4
70  ret void
71}
72
73; CHECK-LABEL: store_i32_release:
74; CHECK-NEXT: .functype store_i32_release (i32, i32) -> (){{$}}
75; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}}
76; CHECK-NEXT: return{{$}}
77define void @store_i32_release(i32 *%p, i32 %v) {
78  store atomic i32 %v, i32* %p release, align 4
79  ret void
80}
81
82; CHECK-LABEL: store_i32_seq_cst:
83; CHECK-NEXT: .functype store_i32_seq_cst (i32, i32) -> (){{$}}
84; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}}
85; CHECK-NEXT: return{{$}}
86define void @store_i32_seq_cst(i32 *%p, i32 %v) {
87  store atomic i32 %v, i32* %p seq_cst, align 4
88  ret void
89}
90
91;===----------------------------------------------------------------------------
92; Atomic read-modify-writes
93;===----------------------------------------------------------------------------
94
95; Out of several binary RMW instructions, here we test 'add' as an example.
96; The 'unordered' ordering is not valid on atomicrmw instructions.
97
98; CHECK-LABEL: add_i32_monotonic:
99; CHECK-NEXT: .functype add_i32_monotonic (i32, i32) -> (i32){{$}}
100; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
101; CHECK-NEXT: return $pop0{{$}}
102define i32 @add_i32_monotonic(i32* %p, i32 %v) {
103  %old = atomicrmw add i32* %p, i32 %v monotonic
104  ret i32 %old
105}
106
107; CHECK-LABEL: add_i32_acquire:
108; CHECK-NEXT: .functype add_i32_acquire (i32, i32) -> (i32){{$}}
109; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
110; CHECK-NEXT: return $pop0{{$}}
111define i32 @add_i32_acquire(i32* %p, i32 %v) {
112  %old = atomicrmw add i32* %p, i32 %v acquire
113  ret i32 %old
114}
115
116; CHECK-LABEL: add_i32_release:
117; CHECK-NEXT: .functype add_i32_release (i32, i32) -> (i32){{$}}
118; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
119; CHECK-NEXT: return $pop0{{$}}
120define i32 @add_i32_release(i32* %p, i32 %v) {
121  %old = atomicrmw add i32* %p, i32 %v release
122  ret i32 %old
123}
124
125; CHECK-LABEL: add_i32_acq_rel:
126; CHECK-NEXT: .functype add_i32_acq_rel (i32, i32) -> (i32){{$}}
127; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
128; CHECK-NEXT: return $pop0{{$}}
129define i32 @add_i32_acq_rel(i32* %p, i32 %v) {
130  %old = atomicrmw add i32* %p, i32 %v acq_rel
131  ret i32 %old
132}
133
134; CHECK-LABEL: add_i32_seq_cst:
135; CHECK-NEXT: .functype add_i32_seq_cst (i32, i32) -> (i32){{$}}
136; CHECK: i32.atomic.rmw.add $push0=, 0($0), $1{{$}}
137; CHECK-NEXT: return $pop0{{$}}
138define i32 @add_i32_seq_cst(i32* %p, i32 %v) {
139  %old = atomicrmw add i32* %p, i32 %v seq_cst
140  ret i32 %old
141}
142
143; Ternary RMW instruction: cmpxchg
144; The success and failure ordering arguments specify how this cmpxchg
145; synchronizes with other atomic operations. Both ordering parameters must be at
146; least monotonic, the ordering constraint on failure must be no stronger than
147; that on success, and the failure ordering cannot be either release or acq_rel.
148
149; CHECK-LABEL: cmpxchg_i32_monotonic_monotonic:
150; CHECK-NEXT: .functype cmpxchg_i32_monotonic_monotonic (i32, i32, i32) -> (i32){{$}}
151; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
152; CHECK-NEXT: return $pop0{{$}}
153define i32 @cmpxchg_i32_monotonic_monotonic(i32* %p, i32 %exp, i32 %new) {
154  %pair = cmpxchg i32* %p, i32 %exp, i32 %new monotonic monotonic
155  %old = extractvalue { i32, i1 } %pair, 0
156  ret i32 %old
157}
158
159; CHECK-LABEL: cmpxchg_i32_acquire_monotonic:
160; CHECK-NEXT: .functype cmpxchg_i32_acquire_monotonic (i32, i32, i32) -> (i32){{$}}
161; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
162; CHECK-NEXT: return $pop0{{$}}
163define i32 @cmpxchg_i32_acquire_monotonic(i32* %p, i32 %exp, i32 %new) {
164  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acquire monotonic
165  %old = extractvalue { i32, i1 } %pair, 0
166  ret i32 %old
167}
168
169; CHECK-LABEL: cmpxchg_i32_release_monotonic:
170; CHECK-NEXT: .functype cmpxchg_i32_release_monotonic (i32, i32, i32) -> (i32){{$}}
171; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
172; CHECK-NEXT: return $pop0{{$}}
173define i32 @cmpxchg_i32_release_monotonic(i32* %p, i32 %exp, i32 %new) {
174  %pair = cmpxchg i32* %p, i32 %exp, i32 %new release monotonic
175  %old = extractvalue { i32, i1 } %pair, 0
176  ret i32 %old
177}
178
179; CHECK-LABEL: cmpxchg_i32_acq_rel_monotonic:
180; CHECK-NEXT: .functype cmpxchg_i32_acq_rel_monotonic (i32, i32, i32) -> (i32){{$}}
181; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
182; CHECK-NEXT: return $pop0{{$}}
183define i32 @cmpxchg_i32_acq_rel_monotonic(i32* %p, i32 %exp, i32 %new) {
184  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acq_rel monotonic
185  %old = extractvalue { i32, i1 } %pair, 0
186  ret i32 %old
187}
188
189; CHECK-LABEL: cmpxchg_i32_seq_cst_monotonic:
190; CHECK-NEXT: .functype cmpxchg_i32_seq_cst_monotonic (i32, i32, i32) -> (i32){{$}}
191; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
192; CHECK-NEXT: return $pop0{{$}}
193define i32 @cmpxchg_i32_seq_cst_monotonic(i32* %p, i32 %exp, i32 %new) {
194  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst monotonic
195  %old = extractvalue { i32, i1 } %pair, 0
196  ret i32 %old
197}
198
199; CHECK-LABEL: cmpxchg_i32_acquire_acquire:
200; CHECK-NEXT: .functype cmpxchg_i32_acquire_acquire (i32, i32, i32) -> (i32){{$}}
201; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
202; CHECK-NEXT: return $pop0{{$}}
203define i32 @cmpxchg_i32_acquire_acquire(i32* %p, i32 %exp, i32 %new) {
204  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acquire acquire
205  %old = extractvalue { i32, i1 } %pair, 0
206  ret i32 %old
207}
208
209; CHECK-LABEL: cmpxchg_i32_release_acquire:
210; CHECK-NEXT: .functype cmpxchg_i32_release_acquire (i32, i32, i32) -> (i32){{$}}
211; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
212; CHECK-NEXT: return $pop0{{$}}
213define i32 @cmpxchg_i32_release_acquire(i32* %p, i32 %exp, i32 %new) {
214  %pair = cmpxchg i32* %p, i32 %exp, i32 %new release acquire
215  %old = extractvalue { i32, i1 } %pair, 0
216  ret i32 %old
217}
218
219; CHECK-LABEL: cmpxchg_i32_acq_rel_acquire:
220; CHECK-NEXT: .functype cmpxchg_i32_acq_rel_acquire (i32, i32, i32) -> (i32){{$}}
221; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
222; CHECK-NEXT: return $pop0{{$}}
223define i32 @cmpxchg_i32_acq_rel_acquire(i32* %p, i32 %exp, i32 %new) {
224  %pair = cmpxchg i32* %p, i32 %exp, i32 %new acq_rel acquire
225  %old = extractvalue { i32, i1 } %pair, 0
226  ret i32 %old
227}
228
229; CHECK-LABEL: cmpxchg_i32_seq_cst_acquire:
230; CHECK-NEXT: .functype cmpxchg_i32_seq_cst_acquire (i32, i32, i32) -> (i32){{$}}
231; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
232; CHECK-NEXT: return $pop0{{$}}
233define i32 @cmpxchg_i32_seq_cst_acquire(i32* %p, i32 %exp, i32 %new) {
234  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst acquire
235  %old = extractvalue { i32, i1 } %pair, 0
236  ret i32 %old
237}
238
239; CHECK-LABEL: cmpxchg_i32_seq_cst_seq_cst:
240; CHECK-NEXT: .functype cmpxchg_i32_seq_cst_seq_cst (i32, i32, i32) -> (i32){{$}}
241; CHECK: i32.atomic.rmw.cmpxchg $push0=, 0($0), $1, $2{{$}}
242; CHECK-NEXT: return $pop0{{$}}
243define i32 @cmpxchg_i32_seq_cst_seq_cst(i32* %p, i32 %exp, i32 %new) {
244  %pair = cmpxchg i32* %p, i32 %exp, i32 %new seq_cst seq_cst
245  %old = extractvalue { i32, i1 } %pair, 0
246  ret i32 %old
247}
248