summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2024-12-18 15:09:24 -0800
committerGitHub <noreply@github.com>2024-12-18 15:09:24 -0800
commit0b378312d74f93f65a9f54efd8b5baeab33c7074 (patch)
tree42493add8fe6a094750352097e86e83a1f961dcb
parent009078979a26b7f4ec99fdfe1929b26176575805 (diff)
downloadbinaryen-0b378312d74f93f65a9f54efd8b5baeab33c7074.tar.gz
binaryen-0b378312d74f93f65a9f54efd8b5baeab33c7074.tar.bz2
binaryen-0b378312d74f93f65a9f54efd8b5baeab33c7074.zip
Handle atomics in GTO (#7160)
GTO removes fields that are never read and also removes sets to those fields. Update the pass to add a seqcst fence when removing a seqcst set to preserve its effect on the global order of seqcst operations.
-rw-r--r--src/passes/GlobalTypeOptimization.cpp24
-rw-r--r--test/lit/passes/gto-removals.wast63
2 files changed, 80 insertions, 7 deletions
diff --git a/src/passes/GlobalTypeOptimization.cpp b/src/passes/GlobalTypeOptimization.cpp
index 0baecdf43..b2ba23b0f 100644
--- a/src/passes/GlobalTypeOptimization.cpp
+++ b/src/passes/GlobalTypeOptimization.cpp
@@ -514,13 +514,23 @@ struct GlobalTypeOptimization : public Pass {
// operations here: the trap on a null ref happens after the value,
// which might have side effects.
Builder builder(*getModule());
- auto flipped = getResultOfFirst(curr->ref,
- builder.makeDrop(curr->value),
- getFunction(),
- getModule(),
- getPassOptions());
- replaceCurrent(
- builder.makeDrop(builder.makeRefAs(RefAsNonNull, flipped)));
+ auto* flipped = getResultOfFirst(curr->ref,
+ builder.makeDrop(curr->value),
+ getFunction(),
+ getModule(),
+ getPassOptions());
+ Expression* replacement =
+ builder.makeDrop(builder.makeRefAs(RefAsNonNull, flipped));
+ if (curr->order == MemoryOrder::SeqCst) {
+ // If the removed set is sequentially consistent, we must insert a
+ // seqcst fence to preserve the effect on the global order of seqcst
+ // operations. No fence is necessary for release sets because there
+ // are no reads for them to synchronize with given that we are
+ // removing the field.
+ replacement =
+ builder.makeSequence(replacement, builder.makeAtomicFence());
+ }
+ replaceCurrent(replacement);
}
}
diff --git a/test/lit/passes/gto-removals.wast b/test/lit/passes/gto-removals.wast
index a6fd9c228..6ab126611 100644
--- a/test/lit/passes/gto-removals.wast
+++ b/test/lit/passes/gto-removals.wast
@@ -1564,3 +1564,66 @@
(export "globalB" (global $globalB))
)
+;; Removed atomic sets needs special handling.
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (shared (struct)))
+ (type $A (shared (struct (mut i32))))
+
+ ;; CHECK: (type $1 (func (param (ref $A))))
+
+ ;; CHECK: (func $sets (type $1) (param $0 (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (block (result (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (block (result (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (block (result (ref $A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.get $0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (atomic.fence)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $sets (param (ref $A))
+ ;; Normal set is optimizable.
+ (struct.set $A 0
+ (local.get 0)
+ (i32.const 1)
+ )
+ ;; Release set is optimizable without a fence because there is no get to
+ ;; synchronize with.
+ (struct.atomic.set acqrel $A 0
+ (local.get 0)
+ (i32.const 1)
+ )
+ ;; This requires a fence to keep the effect on the global order of seqcst
+ ;; operations.
+ (struct.atomic.set $A 0
+ (local.get 0)
+ (i32.const 1)
+ )
+ )
+)