diff --git a/src/passes/LocalCSE.cpp b/src/passes/LocalCSE.cpp index 0233d17061d..2ad15b731fe 100644 --- a/src/passes/LocalCSE.cpp +++ b/src/passes/LocalCSE.cpp @@ -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)) { invalidated.push_back(original); } } diff --git a/test/lit/passes/local-cse-atomics.wast b/test/lit/passes/local-cse-atomics.wast new file mode 100644 index 00000000000..15553ec9bd4 --- /dev/null +++ b/test/lit/passes/local-cse-atomics.wast @@ -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)) + ) +)