Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 3 additions & 21 deletions src/passes/MergeBlocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,29 +498,11 @@ struct MergeBlocks
// )
// at which point the block is on the outside and potentially mergeable with
// an outer block
Block* optimize(Expression* curr,
Expression*& child,
Block* outer = nullptr,
Expression** dependency1 = nullptr,
Expression** dependency2 = nullptr) {
Block*
optimize(Expression* curr, Expression*& child, Block* outer = nullptr) {
if (!child) {
return outer;
}
if ((dependency1 && *dependency1) || (dependency2 && *dependency2)) {
// there are dependencies, things we must be reordered through. make sure
// no problems there
EffectAnalyzer childEffects(getPassOptions(), *getModule(), child);
if (dependency1 && *dependency1 &&
EffectAnalyzer(getPassOptions(), *getModule(), *dependency1)
.invalidates(childEffects)) {
return outer;
}
if (dependency2 && *dependency2 &&
EffectAnalyzer(getPassOptions(), *getModule(), *dependency2)
.invalidates(childEffects)) {
return outer;
}
}
if (auto* block = child->dynCast<Block>()) {
if (!block->name.is() && block->list.size() >= 2) {
auto* back = block->list.back();
Expand Down Expand Up @@ -665,7 +647,7 @@ struct MergeBlocks
EffectAnalyzer blockChildEffects(
getPassOptions(), *getModule(), blockChild);
for (auto& effects : childEffects) {
if (blockChildEffects.invalidates(effects)) {
if (effects.orderedBefore(blockChildEffects)) {
fail = true;
break;
}
Expand Down
91 changes: 91 additions & 0 deletions test/lit/passes/merge-blocks-atomics.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --merge-blocks -all -S -o - | filecheck %s

(module
;; CHECK: (type $struct (shared (struct (field (mut i32)))))
(type $struct (shared (struct (field (mut i32)))))

;; CHECK: (memory $mem 1 1 shared)
(memory $mem 1 1 shared)

;; CHECK: (func $foo (type $2) (param $0 i32) (param $1 i32)
;; CHECK-NEXT: )
(func $foo (param i32 i32))

;; Test 1: Disallowed reordering (GC read NOT moved before Wasm acquire load)
;; Child 0 is named, so it is NOT optimized and its effects (acquire load) are left behind.
;; Child 1 tries to move the GC read out, which should be blocked because it cannot move before the acquire load.
;; CHECK: (func $disallowed (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (call $foo
;; CHECK-NEXT: (block $label1 (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.atomic.load acqrel
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $disallowed (param $x (ref $struct))
(call $foo
;; Child 0: Left behind (named block)
(block $label1 (result i32)
(drop (i32.atomic.load acqrel (i32.const 0)))
(i32.const 0)
)
;; Child 1: Tries to move out
(block (result i32)
;; A: GC read
(drop (struct.get $struct 0 (local.get $x)))
;; B
(i32.const 0)
)
)
)

;; Test 2: Allowed reordering (GC read moved before Wasm release store)
;; Child 0 is named, so it is NOT optimized and its effects (release store) are left behind.
;; Child 1 tries to move the GC read out, which should be ALLOWED because it can move before the release store.
;; CHECK: (func $allowed (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $foo
;; CHECK-NEXT: (block $label2 (result i32)
;; CHECK-NEXT: (i32.atomic.store acqrel
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $allowed (param $x (ref $struct))
(call $foo
;; Child 0: Left behind (named block)
(block $label2 (result i32)
(i32.atomic.store acqrel (i32.const 0) (i32.const 42))
(i32.const 0)
)
;; Child 1: Tries to move out
(block (result i32)
;; A: GC read
(drop (struct.get $struct 0 (local.get $x)))
;; B
(i32.const 0)
)
)
)
)
Loading