Skip to content

Commit 0b37831

Browse files
authored
Handle atomics in GTO (WebAssembly#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.
1 parent 0090789 commit 0b37831

File tree

2 files changed

+80
-7
lines changed

2 files changed

+80
-7
lines changed

src/passes/GlobalTypeOptimization.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -514,13 +514,23 @@ struct GlobalTypeOptimization : public Pass {
514514
// operations here: the trap on a null ref happens after the value,
515515
// which might have side effects.
516516
Builder builder(*getModule());
517-
auto flipped = getResultOfFirst(curr->ref,
518-
builder.makeDrop(curr->value),
519-
getFunction(),
520-
getModule(),
521-
getPassOptions());
522-
replaceCurrent(
523-
builder.makeDrop(builder.makeRefAs(RefAsNonNull, flipped)));
517+
auto* flipped = getResultOfFirst(curr->ref,
518+
builder.makeDrop(curr->value),
519+
getFunction(),
520+
getModule(),
521+
getPassOptions());
522+
Expression* replacement =
523+
builder.makeDrop(builder.makeRefAs(RefAsNonNull, flipped));
524+
if (curr->order == MemoryOrder::SeqCst) {
525+
// If the removed set is sequentially consistent, we must insert a
526+
// seqcst fence to preserve the effect on the global order of seqcst
527+
// operations. No fence is necessary for release sets because there
528+
// are no reads for them to synchronize with given that we are
529+
// removing the field.
530+
replacement =
531+
builder.makeSequence(replacement, builder.makeAtomicFence());
532+
}
533+
replaceCurrent(replacement);
524534
}
525535
}
526536

test/lit/passes/gto-removals.wast

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,3 +1564,66 @@
15641564
(export "globalB" (global $globalB))
15651565
)
15661566

1567+
;; Removed atomic sets needs special handling.
1568+
(module
1569+
;; CHECK: (rec
1570+
;; CHECK-NEXT: (type $A (shared (struct)))
1571+
(type $A (shared (struct (mut i32))))
1572+
1573+
;; CHECK: (type $1 (func (param (ref $A))))
1574+
1575+
;; CHECK: (func $sets (type $1) (param $0 (ref $A))
1576+
;; CHECK-NEXT: (drop
1577+
;; CHECK-NEXT: (ref.as_non_null
1578+
;; CHECK-NEXT: (block (result (ref $A))
1579+
;; CHECK-NEXT: (drop
1580+
;; CHECK-NEXT: (i32.const 1)
1581+
;; CHECK-NEXT: )
1582+
;; CHECK-NEXT: (local.get $0)
1583+
;; CHECK-NEXT: )
1584+
;; CHECK-NEXT: )
1585+
;; CHECK-NEXT: )
1586+
;; CHECK-NEXT: (drop
1587+
;; CHECK-NEXT: (ref.as_non_null
1588+
;; CHECK-NEXT: (block (result (ref $A))
1589+
;; CHECK-NEXT: (drop
1590+
;; CHECK-NEXT: (i32.const 1)
1591+
;; CHECK-NEXT: )
1592+
;; CHECK-NEXT: (local.get $0)
1593+
;; CHECK-NEXT: )
1594+
;; CHECK-NEXT: )
1595+
;; CHECK-NEXT: )
1596+
;; CHECK-NEXT: (block
1597+
;; CHECK-NEXT: (drop
1598+
;; CHECK-NEXT: (ref.as_non_null
1599+
;; CHECK-NEXT: (block (result (ref $A))
1600+
;; CHECK-NEXT: (drop
1601+
;; CHECK-NEXT: (i32.const 1)
1602+
;; CHECK-NEXT: )
1603+
;; CHECK-NEXT: (local.get $0)
1604+
;; CHECK-NEXT: )
1605+
;; CHECK-NEXT: )
1606+
;; CHECK-NEXT: )
1607+
;; CHECK-NEXT: (atomic.fence)
1608+
;; CHECK-NEXT: )
1609+
;; CHECK-NEXT: )
1610+
(func $sets (param (ref $A))
1611+
;; Normal set is optimizable.
1612+
(struct.set $A 0
1613+
(local.get 0)
1614+
(i32.const 1)
1615+
)
1616+
;; Release set is optimizable without a fence because there is no get to
1617+
;; synchronize with.
1618+
(struct.atomic.set acqrel $A 0
1619+
(local.get 0)
1620+
(i32.const 1)
1621+
)
1622+
;; This requires a fence to keep the effect on the global order of seqcst
1623+
;; operations.
1624+
(struct.atomic.set $A 0
1625+
(local.get 0)
1626+
(i32.const 1)
1627+
)
1628+
)
1629+
)

0 commit comments

Comments
 (0)