Skip to content

Commit 402b490

Browse files
committed
[WebAssembly] Support for atomic stores
Summary: Add support for atomic store instructions. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D48839 llvm-svn: 336145
1 parent fd10286 commit 402b490

7 files changed

+561
-2
lines changed

llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h

+7
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
124124
case WebAssembly::STORE8_I32_S:
125125
case WebAssembly::STORE8_I64:
126126
case WebAssembly::STORE8_I64_S:
127+
case WebAssembly::ATOMIC_STORE8_I32:
128+
case WebAssembly::ATOMIC_STORE8_I64:
127129
return 0;
128130
case WebAssembly::LOAD16_S_I32:
129131
case WebAssembly::LOAD16_S_I32_S:
@@ -141,6 +143,8 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
141143
case WebAssembly::STORE16_I32_S:
142144
case WebAssembly::STORE16_I64:
143145
case WebAssembly::STORE16_I64_S:
146+
case WebAssembly::ATOMIC_STORE16_I32:
147+
case WebAssembly::ATOMIC_STORE16_I64:
144148
return 1;
145149
case WebAssembly::LOAD_I32:
146150
case WebAssembly::LOAD_I32_S:
@@ -160,6 +164,8 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
160164
case WebAssembly::ATOMIC_LOAD_I32_S:
161165
case WebAssembly::ATOMIC_LOAD32_U_I64:
162166
case WebAssembly::ATOMIC_LOAD32_U_I64_S:
167+
case WebAssembly::ATOMIC_STORE_I32:
168+
case WebAssembly::ATOMIC_STORE32_I64:
163169
return 2;
164170
case WebAssembly::LOAD_I64:
165171
case WebAssembly::LOAD_I64_S:
@@ -171,6 +177,7 @@ inline unsigned GetDefaultP2Align(unsigned Opcode) {
171177
case WebAssembly::STORE_F64_S:
172178
case WebAssembly::ATOMIC_LOAD_I64:
173179
case WebAssembly::ATOMIC_LOAD_I64_S:
180+
case WebAssembly::ATOMIC_STORE_I64:
174181
return 3;
175182
default:
176183
llvm_unreachable("Only loads and stores have p2align values");

llvm/lib/Target/WebAssembly/WebAssemblyInstrAtomics.td

+139-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,145 @@ def : LoadPatExternSymOffOnly<i64, anyext_aload_16_64, ATOMIC_LOAD16_U_I64>;
196196
// Atomic stores
197197
//===----------------------------------------------------------------------===//
198198

199-
// TODO: add atomic stores here...
199+
let Defs = [ARGUMENTS] in {
200+
defm ATOMIC_STORE_I32 : WebAssemblyStore<I32, "i32.atomic.store", 0xfe17>;
201+
defm ATOMIC_STORE_I64 : WebAssemblyStore<I64, "i64.atomic.store", 0xfe18>;
202+
} // Defs = [ARGUMENTS]
203+
204+
// We need an 'atomic' version of store patterns because store and atomic_store
205+
// nodes have different operand orders:
206+
// store: (store $val, $ptr)
207+
// atomic_store: (store $ptr, $val)
208+
209+
let Predicates = [HasAtomics] in {
210+
211+
// Select stores with no constant offset.
212+
class AStorePatNoOffset<ValueType ty, PatFrag node, NI inst> :
213+
Pat<(node I32:$addr, ty:$val), (inst 0, 0, $addr, $val)>;
214+
def : AStorePatNoOffset<i32, atomic_store_32, ATOMIC_STORE_I32>;
215+
def : AStorePatNoOffset<i64, atomic_store_64, ATOMIC_STORE_I64>;
216+
217+
// Select stores with a constant offset.
218+
219+
// Pattern with address + immediate offset
220+
class AStorePatImmOff<ValueType ty, PatFrag storekind, PatFrag operand,
221+
NI inst> :
222+
Pat<(storekind (operand I32:$addr, imm:$off), ty:$val),
223+
(inst 0, imm:$off, $addr, ty:$val)>;
224+
def : AStorePatImmOff<i32, atomic_store_32, regPlusImm, ATOMIC_STORE_I32>;
225+
def : AStorePatImmOff<i64, atomic_store_64, regPlusImm, ATOMIC_STORE_I64>;
226+
def : AStorePatImmOff<i32, atomic_store_32, or_is_add, ATOMIC_STORE_I32>;
227+
def : AStorePatImmOff<i64, atomic_store_64, or_is_add, ATOMIC_STORE_I64>;
228+
229+
class AStorePatGlobalAddr<ValueType ty, PatFrag storekind, NI inst> :
230+
Pat<(storekind (regPlusGA I32:$addr, (WebAssemblywrapper tglobaladdr:$off)),
231+
ty:$val),
232+
(inst 0, tglobaladdr:$off, I32:$addr, ty:$val)>;
233+
def : AStorePatGlobalAddr<i32, atomic_store_32, ATOMIC_STORE_I32>;
234+
def : AStorePatGlobalAddr<i64, atomic_store_64, ATOMIC_STORE_I64>;
235+
236+
class AStorePatExternalSym<ValueType ty, PatFrag storekind, NI inst> :
237+
Pat<(storekind (add I32:$addr, (WebAssemblywrapper texternalsym:$off)),
238+
ty:$val),
239+
(inst 0, texternalsym:$off, I32:$addr, ty:$val)>;
240+
def : AStorePatExternalSym<i32, atomic_store_32, ATOMIC_STORE_I32>;
241+
def : AStorePatExternalSym<i64, atomic_store_64, ATOMIC_STORE_I64>;
242+
243+
// Select stores with just a constant offset.
244+
class AStorePatOffsetOnly<ValueType ty, PatFrag storekind, NI inst> :
245+
Pat<(storekind imm:$off, ty:$val),
246+
(inst 0, imm:$off, (CONST_I32 0), ty:$val)>;
247+
def : AStorePatOffsetOnly<i32, atomic_store_32, ATOMIC_STORE_I32>;
248+
def : AStorePatOffsetOnly<i64, atomic_store_64, ATOMIC_STORE_I64>;
249+
250+
class AStorePatGlobalAddrOffOnly<ValueType ty, PatFrag storekind, NI inst> :
251+
Pat<(storekind (WebAssemblywrapper tglobaladdr:$off), ty:$val),
252+
(inst 0, tglobaladdr:$off, (CONST_I32 0), ty:$val)>;
253+
def : AStorePatGlobalAddrOffOnly<i32, atomic_store_32, ATOMIC_STORE_I32>;
254+
def : AStorePatGlobalAddrOffOnly<i64, atomic_store_64, ATOMIC_STORE_I64>;
255+
256+
class AStorePatExternSymOffOnly<ValueType ty, PatFrag storekind, NI inst> :
257+
Pat<(storekind (WebAssemblywrapper texternalsym:$off), ty:$val),
258+
(inst 0, texternalsym:$off, (CONST_I32 0), ty:$val)>;
259+
def : AStorePatExternSymOffOnly<i32, atomic_store_32, ATOMIC_STORE_I32>;
260+
def : AStorePatExternSymOffOnly<i64, atomic_store_64, ATOMIC_STORE_I64>;
261+
262+
} // Predicates = [HasAtomics]
263+
264+
// Truncating stores.
265+
let Defs = [ARGUMENTS] in {
266+
defm ATOMIC_STORE8_I32 : WebAssemblyStore<I32, "i32.atomic.store8", 0xfe19>;
267+
defm ATOMIC_STORE16_I32 : WebAssemblyStore<I32, "i32.atomic.store16", 0xfe1a>;
268+
defm ATOMIC_STORE8_I64 : WebAssemblyStore<I64, "i64.atomic.store8", 0xfe1b>;
269+
defm ATOMIC_STORE16_I64 : WebAssemblyStore<I64, "i64.atomic.store16", 0xfe1c>;
270+
defm ATOMIC_STORE32_I64 : WebAssemblyStore<I64, "i64.atomic.store32", 0xfe1d>;
271+
} // Defs = [ARGUMENTS]
272+
273+
// Fragments for truncating stores.
274+
275+
// We don't have single truncating atomic store instructions. For 32-bit
276+
// instructions, we just need to match bare atomic stores. On the other hand,
277+
// truncating stores from i64 values are once truncated to i32 first.
278+
class trunc_astore_64<PatFrag storekind> :
279+
PatFrag<(ops node:$addr, node:$val),
280+
(storekind node:$addr, (i32 (trunc (i64 node:$val))))>;
281+
def trunc_astore_8_64 : trunc_astore_64<atomic_store_8>;
282+
def trunc_astore_16_64 : trunc_astore_64<atomic_store_16>;
283+
def trunc_astore_32_64 : trunc_astore_64<atomic_store_32>;
284+
285+
let Predicates = [HasAtomics] in {
286+
287+
// Truncating stores with no constant offset
288+
def : AStorePatNoOffset<i32, atomic_store_8, ATOMIC_STORE8_I32>;
289+
def : AStorePatNoOffset<i32, atomic_store_16, ATOMIC_STORE16_I32>;
290+
def : AStorePatNoOffset<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>;
291+
def : AStorePatNoOffset<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>;
292+
def : AStorePatNoOffset<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
293+
294+
// Truncating stores with a constant offset
295+
def : AStorePatImmOff<i32, atomic_store_8, regPlusImm, ATOMIC_STORE8_I32>;
296+
def : AStorePatImmOff<i32, atomic_store_16, regPlusImm, ATOMIC_STORE16_I32>;
297+
def : AStorePatImmOff<i64, trunc_astore_8_64, regPlusImm, ATOMIC_STORE8_I64>;
298+
def : AStorePatImmOff<i64, trunc_astore_16_64, regPlusImm, ATOMIC_STORE16_I64>;
299+
def : AStorePatImmOff<i64, trunc_astore_32_64, regPlusImm, ATOMIC_STORE32_I64>;
300+
def : AStorePatImmOff<i32, atomic_store_8, or_is_add, ATOMIC_STORE8_I32>;
301+
def : AStorePatImmOff<i32, atomic_store_16, or_is_add, ATOMIC_STORE16_I32>;
302+
def : AStorePatImmOff<i64, trunc_astore_8_64, or_is_add, ATOMIC_STORE8_I64>;
303+
def : AStorePatImmOff<i64, trunc_astore_16_64, or_is_add, ATOMIC_STORE16_I64>;
304+
def : AStorePatImmOff<i64, trunc_astore_32_64, or_is_add, ATOMIC_STORE32_I64>;
305+
306+
def : AStorePatGlobalAddr<i32, atomic_store_8, ATOMIC_STORE8_I32>;
307+
def : AStorePatGlobalAddr<i32, atomic_store_16, ATOMIC_STORE16_I32>;
308+
def : AStorePatGlobalAddr<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>;
309+
def : AStorePatGlobalAddr<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>;
310+
def : AStorePatGlobalAddr<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
311+
312+
def : AStorePatExternalSym<i32, atomic_store_8, ATOMIC_STORE8_I32>;
313+
def : AStorePatExternalSym<i32, atomic_store_16, ATOMIC_STORE16_I32>;
314+
def : AStorePatExternalSym<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>;
315+
def : AStorePatExternalSym<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>;
316+
def : AStorePatExternalSym<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
317+
318+
// Truncating stores with just a constant offset
319+
def : AStorePatOffsetOnly<i32, atomic_store_8, ATOMIC_STORE8_I32>;
320+
def : AStorePatOffsetOnly<i32, atomic_store_16, ATOMIC_STORE16_I32>;
321+
def : AStorePatOffsetOnly<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>;
322+
def : AStorePatOffsetOnly<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>;
323+
def : AStorePatOffsetOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
324+
325+
def : AStorePatGlobalAddrOffOnly<i32, atomic_store_8, ATOMIC_STORE8_I32>;
326+
def : AStorePatGlobalAddrOffOnly<i32, atomic_store_16, ATOMIC_STORE16_I32>;
327+
def : AStorePatGlobalAddrOffOnly<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>;
328+
def : AStorePatGlobalAddrOffOnly<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>;
329+
def : AStorePatGlobalAddrOffOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
330+
331+
def : AStorePatExternSymOffOnly<i32, atomic_store_8, ATOMIC_STORE8_I32>;
332+
def : AStorePatExternSymOffOnly<i32, atomic_store_16, ATOMIC_STORE16_I32>;
333+
def : AStorePatExternSymOffOnly<i64, trunc_astore_8_64, ATOMIC_STORE8_I64>;
334+
def : AStorePatExternSymOffOnly<i64, trunc_astore_16_64, ATOMIC_STORE16_I64>;
335+
def : AStorePatExternSymOffOnly<i64, trunc_astore_32_64, ATOMIC_STORE32_I64>;
336+
337+
} // Predicates = [HasAtomics]
200338

201339
//===----------------------------------------------------------------------===//
202340
// Low-level exclusive operations

llvm/lib/Target/WebAssembly/WebAssemblySetP2AlignOperands.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ bool WebAssemblySetP2AlignOperands::runOnMachineFunction(MachineFunction &MF) {
118118
case WebAssembly::STORE8_I64:
119119
case WebAssembly::STORE16_I64:
120120
case WebAssembly::STORE32_I64:
121+
case WebAssembly::ATOMIC_STORE_I32:
122+
case WebAssembly::ATOMIC_STORE8_I32:
123+
case WebAssembly::ATOMIC_STORE16_I32:
124+
case WebAssembly::ATOMIC_STORE_I64:
125+
case WebAssembly::ATOMIC_STORE8_I64:
126+
case WebAssembly::ATOMIC_STORE16_I64:
127+
case WebAssembly::ATOMIC_STORE32_I64:
121128
RewriteP2Align(MI, WebAssembly::StoreP2AlignOperandNo);
122129
break;
123130
default:

llvm/test/CodeGen/WebAssembly/i32-load-store-alignment.ll

+20
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
66
target triple = "wasm32-unknown-unknown"
77

8+
; Loads.
9+
810
; CHECK-LABEL: ldi32_a1:
911
; CHECK-NEXT: .param i32{{$}}
1012
; CHECK-NEXT: .result i32{{$}}
@@ -236,3 +238,21 @@ define i32 @ldi32_atomic_a8(i32 *%p) {
236238
%v = load atomic i32, i32* %p seq_cst, align 8
237239
ret i32 %v
238240
}
241+
242+
; CHECK-LABEL: sti32_atomic_a4:
243+
; CHECK-NEXT: .param i32, i32{{$}}
244+
; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}}
245+
; CHECK-NEXT: return{{$}}
246+
define void @sti32_atomic_a4(i32 *%p, i32 %v) {
247+
store atomic i32 %v, i32* %p seq_cst, align 4
248+
ret void
249+
}
250+
251+
; CHECK-LABEL: sti32_atomic_a8:
252+
; CHECK-NEXT: .param i32, i32{{$}}
253+
; CHECK-NEXT: i32.atomic.store 0($0), $1{{$}}
254+
; CHECK-NEXT: return{{$}}
255+
define void @sti32_atomic_a8(i32 *%p, i32 %v) {
256+
store atomic i32 %v, i32* %p seq_cst, align 8
257+
ret void
258+
}

llvm/test/CodeGen/WebAssembly/i64-load-store-alignment.ll

+24-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
66
target triple = "wasm32-unknown-unknown"
77

8+
; Loads.
9+
810
; CHECK-LABEL: ldi64_a1:
911
; CHECK-NEXT: .param i32{{$}}
1012
; CHECK-NEXT: .result i64{{$}}
@@ -325,6 +327,9 @@ define void @sti32_a8(i32 *%p, i64 %w) {
325327
}
326328

327329
; Atomics.
330+
; Wasm atomics have the alignment field, but it must always have the type's
331+
; natural alignment.
332+
328333
; CHECK-LABEL: ldi64_atomic_a8:
329334
; CHECK-NEXT: .param i32{{$}}
330335
; CHECK-NEXT: .result i64{{$}}
@@ -336,7 +341,6 @@ define i64 @ldi64_atomic_a8(i64 *%p) {
336341
}
337342

338343
; 16 is greater than the default alignment so it is ignored.
339-
340344
; CHECK-LABEL: ldi64_atomic_a16:
341345
; CHECK-NEXT: .param i32{{$}}
342346
; CHECK-NEXT: .result i64{{$}}
@@ -346,3 +350,22 @@ define i64 @ldi64_atomic_a16(i64 *%p) {
346350
%v = load atomic i64, i64* %p seq_cst, align 16
347351
ret i64 %v
348352
}
353+
354+
; CHECK-LABEL: sti64_atomic_a4:
355+
; CHECK-NEXT: .param i32, i64{{$}}
356+
; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}}
357+
; CHECK-NEXT: return{{$}}
358+
define void @sti64_atomic_a4(i64 *%p, i64 %v) {
359+
store atomic i64 %v, i64* %p seq_cst, align 8
360+
ret void
361+
}
362+
363+
; 16 is greater than the default alignment so it is ignored.
364+
; CHECK-LABEL: sti64_atomic_a8:
365+
; CHECK-NEXT: .param i32, i64{{$}}
366+
; CHECK-NEXT: i64.atomic.store 0($0), $1{{$}}
367+
; CHECK-NEXT: return{{$}}
368+
define void @sti64_atomic_a8(i64 *%p, i64 %v) {
369+
store atomic i64 %v, i64* %p seq_cst, align 16
370+
ret void
371+
}

0 commit comments

Comments
 (0)