summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2024-12-17 20:01:23 -0800
committerGitHub <noreply@github.com>2024-12-18 04:01:23 +0000
commit7c8cd2f4a51213964f6f1ec11e54d2b6e721cdaf (patch)
tree6440ab4fb4a1c572a5a82f3fcb9766fc8d505c5e /test
parent9f5f8dd2ffe0b89ea071aea3d2b3efad42dada4f (diff)
downloadbinaryen-7c8cd2f4a51213964f6f1ec11e54d2b6e721cdaf.tar.gz
binaryen-7c8cd2f4a51213964f6f1ec11e54d2b6e721cdaf.tar.bz2
binaryen-7c8cd2f4a51213964f6f1ec11e54d2b6e721cdaf.zip
Support atomic struct accessors (#7155)
Implement support for both sequentially consistent and acquire-release variants of `struct.atomic.get` and `struct.atomic.set`, as proposed by shared-everything-threads. Introduce a new `MemoryOrdering` enum for describing different levels of atomicity (or the lack thereof). This new enum should eventually be adopted by linear memory atomic accessors as well to support acquire-release semantics, but for now just use it in `StructGet` and `StructSet`. In addition to implementing parsing and emitting for the instructions, validate that shared-everything is enabled to use them, mark them as having synchronization side effects, and lightly optimize them by relaxing acquire-release accesses to non-shared structs to normal, unordered accesses. This is valid because such accesses cannot possibly synchronize with other threads. Also update Precompute to avoid optimizing out synchronization points. There are probably other passes that need to be updated to avoid incorrectly optimizing synchronizing accesses, but identifying and fixing them is left as future work.
Diffstat (limited to 'test')
-rw-r--r--test/lit/basic/gc-atomics.wast149
-rw-r--r--test/lit/passes/optimize-instructions-gc-atomics.wast157
-rw-r--r--test/lit/passes/precompute-gc-atomics.wast72
-rw-r--r--test/lit/passes/vacuum-gc-atomics.wast91
-rw-r--r--test/lit/validation/gc-atomics.wast38
5 files changed, 507 insertions, 0 deletions
diff --git a/test/lit/basic/gc-atomics.wast b/test/lit/basic/gc-atomics.wast
new file mode 100644
index 000000000..c454b4c99
--- /dev/null
+++ b/test/lit/basic/gc-atomics.wast
@@ -0,0 +1,149 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; RUN: wasm-opt -all %s -S -o - | filecheck %s
+;; RUN: wasm-opt -all %s --roundtrip -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $struct (struct (field (mut i32))))
+ (type $struct (struct (field (mut i32))))
+ ;; CHECK: (type $packed (struct (field (mut i8))))
+ (type $packed (struct (field (mut i8))))
+
+ ;; CHECK: (func $get (type $3) (param $0 (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get $struct 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param (ref null $struct)) (result i32)
+ (struct.atomic.get $struct 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst (type $3) (param $0 (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get $struct 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst (param (ref null $struct)) (result i32)
+ (struct.atomic.get seqcst $struct 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel (type $3) (param $0 (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get acqrel $struct 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-acqrel (param (ref null $struct)) (result i32)
+ (struct.atomic.get acqrel $struct 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-s (type $2) (param $0 (ref null $packed)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get_s $packed 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-s (param (ref null $packed)) (result i32)
+ (struct.atomic.get_s $packed 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-s-seqcst (type $2) (param $0 (ref null $packed)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get_s $packed 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-s-seqcst (param (ref null $packed)) (result i32)
+ (struct.atomic.get_s seqcst $packed 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-s-acqrel (type $2) (param $0 (ref null $packed)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get_s acqrel $packed 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-s-acqrel (param (ref null $packed)) (result i32)
+ (struct.atomic.get_s acqrel $packed 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-u (type $2) (param $0 (ref null $packed)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get_u $packed 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-u (param (ref null $packed)) (result i32)
+ (struct.atomic.get_u $packed 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-u-seqcst (type $2) (param $0 (ref null $packed)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get_u $packed 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-u-seqcst (param (ref null $packed)) (result i32)
+ (struct.atomic.get_u seqcst $packed 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $get-u-acqrel (type $2) (param $0 (ref null $packed)) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get_u acqrel $packed 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-u-acqrel (param (ref null $packed)) (result i32)
+ (struct.atomic.get_u acqrel $packed 0
+ (local.get 0)
+ )
+ )
+
+ ;; CHECK: (func $set (type $4) (param $0 (ref null $struct))
+ ;; CHECK-NEXT: (struct.atomic.set $struct 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set (param (ref null $struct))
+ (struct.atomic.set $struct 0
+ (local.get 0)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-seqcst (type $4) (param $0 (ref null $struct))
+ ;; CHECK-NEXT: (struct.atomic.set $struct 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-seqcst (param (ref null $struct))
+ (struct.atomic.set seqcst $struct 0
+ (local.get 0)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-acqrel (type $4) (param $0 (ref null $struct))
+ ;; CHECK-NEXT: (struct.atomic.set acqrel $struct 0
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-acqrel (param (ref null $struct))
+ (struct.atomic.set acqrel $struct 0
+ (local.get 0)
+ (i32.const 0)
+ )
+ )
+)
diff --git a/test/lit/passes/optimize-instructions-gc-atomics.wast b/test/lit/passes/optimize-instructions-gc-atomics.wast
new file mode 100644
index 000000000..a0283390c
--- /dev/null
+++ b/test/lit/passes/optimize-instructions-gc-atomics.wast
@@ -0,0 +1,157 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; RUN: wasm-opt %s -all --optimize-instructions -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $unshared (struct (field (mut i32))))
+
+ ;; CHECK: (type $shared (shared (struct (field (mut i32)))))
+ (type $shared (shared (struct (field (mut i32)))))
+ (type $unshared (struct (field (mut i32))))
+
+ ;; CHECK: (func $get-unordered-unshared (type $2) (result i32)
+ ;; CHECK-NEXT: (struct.get $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-unordered-unshared (result i32)
+ (struct.get $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+
+ ;; CHECK: (func $get-unordered-shared (type $2) (result i32)
+ ;; CHECK-NEXT: (struct.get $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-unordered-shared (result i32)
+ (struct.get $shared 0
+ (struct.new_default $shared)
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst-unshared (type $2) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst-unshared (result i32)
+ (struct.atomic.get seqcst $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst-shared (type $2) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst-shared (result i32)
+ (struct.atomic.get seqcst $shared 0
+ (struct.new_default $shared)
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel-unshared (type $2) (result i32)
+ ;; CHECK-NEXT: (struct.get $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-acqrel-unshared (result i32)
+ ;; This can be relaxed to unordered
+ (struct.atomic.get acqrel $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel-shared (type $2) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get acqrel $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-acqrel-shared (result i32)
+ (struct.atomic.get acqrel $shared 0
+ (struct.new_default $shared)
+ )
+ )
+
+ ;; CHECK: (func $set-unordered-unshared (type $3)
+ ;; CHECK-NEXT: (struct.set $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-unordered-unshared
+ (struct.set $unshared 0
+ (struct.new_default $unshared)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-unordered-shared (type $3)
+ ;; CHECK-NEXT: (struct.set $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-unordered-shared
+ (struct.set $shared 0
+ (struct.new_default $shared)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-seqcst-unshared (type $3)
+ ;; CHECK-NEXT: (struct.atomic.set $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-seqcst-unshared
+ (struct.atomic.set seqcst $unshared 0
+ (struct.new_default $unshared)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-seqcst-shared (type $3)
+ ;; CHECK-NEXT: (struct.atomic.set $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-seqcst-shared
+ (struct.atomic.set seqcst $shared 0
+ (struct.new_default $shared)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-acqrel-unshared (type $3)
+ ;; CHECK-NEXT: (struct.set $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-acqrel-unshared
+ ;; This can be relaxed to unordered.
+ (struct.atomic.set acqrel $unshared 0
+ (struct.new_default $unshared)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: (func $set-acqrel-shared (type $3)
+ ;; CHECK-NEXT: (struct.atomic.set acqrel $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $set-acqrel-shared
+ (struct.atomic.set acqrel $shared 0
+ (struct.new_default $shared)
+ (i32.const 0)
+ )
+ )
+)
diff --git a/test/lit/passes/precompute-gc-atomics.wast b/test/lit/passes/precompute-gc-atomics.wast
new file mode 100644
index 000000000..1f2d07753
--- /dev/null
+++ b/test/lit/passes/precompute-gc-atomics.wast
@@ -0,0 +1,72 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; RUN: wasm-opt %s -all --precompute-propagate -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $shared (shared (struct (field i32))))
+ (type $shared (shared (struct (field i32))))
+ ;; CHECK: (type $unshared (struct (field i32)))
+ (type $unshared (struct (field i32)))
+
+ ;; CHECK: (func $get-unordered-unshared (type $0) (result i32)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $get-unordered-unshared (result i32)
+ (struct.get $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+
+ ;; CHECK: (func $get-unordered-shared (type $0) (result i32)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $get-unordered-shared (result i32)
+ (struct.get $shared 0
+ (struct.new_default $shared)
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst-unshared (type $0) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst-unshared (result i32)
+ (struct.atomic.get seqcst $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst-shared (type $0) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst-shared (result i32)
+ (struct.atomic.get seqcst $shared 0
+ (struct.new_default $shared)
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel-unshared (type $0) (result i32)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $get-acqrel-unshared (result i32)
+ ;; We can optimize this because acquire-release on unshared data does not
+ ;; synchronize with anything.
+ (struct.atomic.get acqrel $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel-shared (type $0) (result i32)
+ ;; CHECK-NEXT: (struct.atomic.get acqrel $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-acqrel-shared (result i32)
+ (struct.atomic.get acqrel $shared 0
+ (struct.new_default $shared)
+ )
+ )
+)
diff --git a/test/lit/passes/vacuum-gc-atomics.wast b/test/lit/passes/vacuum-gc-atomics.wast
new file mode 100644
index 000000000..49a8a8a6f
--- /dev/null
+++ b/test/lit/passes/vacuum-gc-atomics.wast
@@ -0,0 +1,91 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; Check that synchronizing operations are considered to have side effects that
+;; prevent them from being dropped.
+
+;; RUN: wasm-opt %s -all --vacuum -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $shared (shared (struct (field i32))))
+ (type $shared (shared (struct (field i32))))
+ ;; CHECK: (type $unshared (struct (field i32)))
+ (type $unshared (struct (field i32)))
+
+ ;; CHECK: (func $get-unordered-unshared (type $0)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $get-unordered-unshared
+ (drop
+ (struct.get $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+ )
+
+ ;; CHECK: (func $get-unordered-shared (type $0)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $get-unordered-shared
+ (drop
+ (struct.get $shared 0
+ (struct.new_default $shared)
+ )
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst-unshared (type $0)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.atomic.get $unshared 0
+ ;; CHECK-NEXT: (struct.new_default $unshared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst-unshared
+ (drop
+ (struct.atomic.get seqcst $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+ )
+
+ ;; CHECK: (func $get-seqcst-shared (type $0)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.atomic.get $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-seqcst-shared
+ (drop
+ (struct.atomic.get seqcst $shared 0
+ (struct.new_default $shared)
+ )
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel-unshared (type $0)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $get-acqrel-unshared
+ (drop
+ (struct.atomic.get acqrel $unshared 0
+ (struct.new_default $unshared)
+ )
+ )
+ )
+
+ ;; CHECK: (func $get-acqrel-shared (type $0)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.atomic.get acqrel $shared 0
+ ;; CHECK-NEXT: (struct.new_default $shared)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-acqrel-shared
+ (drop
+ (struct.atomic.get acqrel $shared 0
+ (struct.new_default $shared)
+ )
+ )
+ )
+)
diff --git a/test/lit/validation/gc-atomics.wast b/test/lit/validation/gc-atomics.wast
new file mode 100644
index 000000000..28e98b9fe
--- /dev/null
+++ b/test/lit/validation/gc-atomics.wast
@@ -0,0 +1,38 @@
+;; Test that shared-everything GC instructions require the shared-everything
+;; feature.
+
+;; RUN: not wasm-opt -all --disable-shared-everything %s 2>&1 | filecheck %s
+
+(module
+ (type $struct (struct (field (mut i32))))
+
+ ;; CHECK: struct.atomic.get requires shared-everything [--enable-shared-everything]
+ (func $get-seqcst (result i32)
+ (struct.atomic.get seqcst $struct 0
+ (struct.new_default $struct)
+ )
+ )
+
+ ;; CHECK: struct.atomic.get requires shared-everything [--enable-shared-everything]
+ (func $get-acqrel (result i32)
+ (struct.atomic.get acqrel $struct 0
+ (struct.new_default $struct)
+ )
+ )
+
+ ;; CHECK: struct.atomic.set requires shared-everything [--enable-shared-everything]
+ (func $set-seqcst
+ (struct.atomic.set seqcst $struct 0
+ (struct.new_default $struct)
+ (i32.const 0)
+ )
+ )
+
+ ;; CHECK: struct.atomic.set requires shared-everything [--enable-shared-everything]
+ (func $set-acqrel
+ (struct.atomic.set acqrel $struct 0
+ (struct.new_default $struct)
+ (i32.const 0)
+ )
+ )
+) \ No newline at end of file