diff options
author | Thomas Lively <tlively@google.com> | 2024-12-18 15:09:24 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-18 15:09:24 -0800 |
commit | 0b378312d74f93f65a9f54efd8b5baeab33c7074 (patch) | |
tree | 42493add8fe6a094750352097e86e83a1f961dcb | |
parent | 009078979a26b7f4ec99fdfe1929b26176575805 (diff) | |
download | binaryen-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.cpp | 24 | ||||
-rw-r--r-- | test/lit/passes/gto-removals.wast | 63 |
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) + ) + ) +) |