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
4 changes: 3 additions & 1 deletion src/passes/LocalCSE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,9 @@ struct Checker
continue;
}
auto& originalInfo = kv.second;
if (effects.invalidates(originalInfo.effects)) {
// Check whether curr must remain before COPY. We use ORIGINAL's effects
// in the check because we know they are the same as COPY's effects.
if (effects.orderedBefore(originalInfo.effects)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have [original, curr] as the order of expressions. Shouldn't we invalidate like this?

Suggested change
if (effects.orderedBefore(originalInfo.effects)) {
if (!effects.orderedAfter(originalInfo.effects)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. effects/curr begin after. Should we not ask if they must remain after, as before/after is not symmetric?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is correct-as is. Say we have A; B; A', where A and A' is the same, so CSE is trying to optimize out A' and use the result of A instead. This is semantically the same as reordering A' to be before B (or alternatively B after A'): A; A'; B, so we want to invalidate the optimization if B ordered-before A', preventing the move. You're right that originalInfo.effects is calculated from A, but A' is the same expression, so here we're using it as A''s effects. So checking B ordered-before A' is the same as effects.orderedBefore(originalInfo.effects).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. Maybe add a comment? Specifically the bit about using the effects of original to model those of the thing being optimized away, which is later, seems non-obvious.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment clarifying that originalInfo.effects is standing in for COPY's effects.

invalidated.push_back(original);
}
}
Expand Down
58 changes: 58 additions & 0 deletions test/lit/passes/local-cse-atomics.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt -all --local-cse -S -o - %s | 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)

;; Test 1: Allowed reordering (GC read reused across Wasm release store)
;; CHECK: (func $allowed (type $1) (param $x (ref $struct)) (result i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (local $2 i32)
;; CHECK-NEXT: (local.set $y
;; CHECK-NEXT: (local.tee $2
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.atomic.store acqrel
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $2)
;; CHECK-NEXT: )
(func $allowed (param $x (ref $struct)) (result i32)
(local $y i32)
(local.set $y (struct.get $struct 0 (local.get $x)))
(i32.atomic.store acqrel (i32.const 0) (i32.const 42))
(struct.get $struct 0 (local.get $x))
)

;; Test 2: Disallowed reordering (GC read NOT reused across Wasm acquire load)
;; CHECK: (func $disallowed (type $1) (param $x (ref $struct)) (result i32)
;; CHECK-NEXT: (local $y i32)
;; CHECK-NEXT: (local.set $y
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.atomic.load acqrel
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $disallowed (param $x (ref $struct)) (result i32)
(local $y i32)
(local.set $y (struct.get $struct 0 (local.get $x)))
(drop (i32.atomic.load acqrel (i32.const 0)))
(struct.get $struct 0 (local.get $x))
)
)
Loading