| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| |
| ;; RUN: foreach %s %t wasm-opt --cfp --closed-world -all -S -o - | filecheck %s |
| |
| ;; RMW ops are generally writes that inhibit optimization. |
| (module |
| ;; CHECK: (type $A (shared (struct (field (mut i32))))) |
| (type $A (shared (struct (mut i32)))) |
| |
| ;; CHECK: (type $1 (func (result (ref $A)))) |
| |
| ;; CHECK: (type $2 (func (param (ref $A)) (result i32))) |
| |
| ;; CHECK: (func $init (type $1) (result (ref $A)) |
| ;; CHECK-NEXT: (struct.new_default $A) |
| ;; CHECK-NEXT: ) |
| (func $init (result (ref $A)) |
| (struct.new_default $A) |
| ) |
| |
| ;; CHECK: (func $rmw (type $2) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.atomic.rmw.add $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw (param (ref $A)) (result i32) |
| ;; This RMW mutates the field. |
| (drop |
| (struct.atomic.rmw.add $A 0 |
| (local.get 0) |
| (i32.const 1) |
| ) |
| ) |
| ;; So this get is not optimizable. |
| (struct.get $A 0 |
| (local.get 0) |
| ) |
| ) |
| |
| ) |
| |
| ;; RMW xchg operations also cannot be optimized, even if they don't write new |
| ;; values. They may form cross-thread synchronization edges. |
| (module |
| ;; CHECK: (type $A (shared (struct (field (mut i32))))) |
| (type $A (shared (struct (mut i32)))) |
| |
| ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) |
| |
| ;; CHECK: (type $2 (func (result (ref $A)))) |
| |
| ;; CHECK: (func $init (type $2) (result (ref $A)) |
| ;; CHECK-NEXT: (struct.new_default $A) |
| ;; CHECK-NEXT: ) |
| (func $init (result (ref $A)) |
| (struct.new_default $A) |
| ) |
| |
| ;; CHECK: (func $rmw-xchg (type $1) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.xchg $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-xchg (param (ref $A)) (result i32) |
| ;; This xchg does not change the value, but still cannot be optimized. |
| (struct.atomic.rmw.xchg $A 0 |
| (local.get 0) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $rmw-xchg-acqrel (type $1) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.xchg acqrel acqrel $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-xchg-acqrel (param (ref $A)) (result i32) |
| ;; Making the accesses acqrel instead of seqcst doesn't make a difference. |
| (struct.atomic.rmw.xchg acqrel acqrel $A 0 |
| (local.get 0) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $get (type $1) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $get (param (ref $A)) (result i32) |
| ;; This get is still optimizable. |
| (struct.get $A 0 |
| (local.get 0) |
| ) |
| ) |
| ) |
| |
| ;; A RMW xchg copy is also unoptimizable. |
| (module |
| ;; CHECK: (type $A (shared (struct (field (mut i32))))) |
| (type $A (shared (struct (mut i32)))) |
| |
| ;; CHECK: (type $1 (func (param (ref $A) (ref $A)) (result i32))) |
| |
| ;; CHECK: (type $2 (func (result (ref $A)))) |
| |
| ;; CHECK: (type $3 (func (param (ref $A)) (result i32))) |
| |
| ;; CHECK: (func $init (type $2) (result (ref $A)) |
| ;; CHECK-NEXT: (struct.new_default $A) |
| ;; CHECK-NEXT: ) |
| (func $init (result (ref $A)) |
| (struct.new_default $A) |
| ) |
| |
| ;; CHECK: (func $rmw-xchg-copy (type $1) (param $0 (ref $A)) (param $1 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.xchg $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (struct.atomic.get $A 0 |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-xchg-copy (param (ref $A) (ref $A)) (result i32) |
| ;; This is a copy from one A to another, but still cannot be optimized. |
| (struct.atomic.rmw.xchg $A 0 |
| (local.get 0) |
| (struct.atomic.get $A 0 |
| (local.get 1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $rmw-xchg-copy-acqrel (type $1) (param $0 (ref $A)) (param $1 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.xchg acqrel acqrel $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (struct.atomic.get acqrel $A 0 |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-xchg-copy-acqrel (param (ref $A) (ref $A)) (result i32) |
| ;; Making the accesses acqrel instead of seqcst doesn't make a difference. |
| (struct.atomic.rmw.xchg acqrel acqrel $A 0 |
| (local.get 0) |
| (struct.atomic.get acqrel $A 0 |
| (local.get 1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $get (type $3) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $get (param (ref $A)) (result i32) |
| ;; This get is optimizable. |
| (struct.get $A 0 |
| (local.get 0) |
| ) |
| ) |
| ) |
| |
| ;; Similarly, cmpxchg cannot be optimized. |
| (module |
| ;; CHECK: (type $A (shared (struct (field (mut i32))))) |
| (type $A (shared (struct (mut i32)))) |
| |
| ;; CHECK: (type $1 (func (param (ref $A) i32) (result i32))) |
| |
| ;; CHECK: (type $2 (func (result (ref $A)))) |
| |
| ;; CHECK: (type $3 (func (param (ref $A)) (result i32))) |
| |
| ;; CHECK: (func $init (type $2) (result (ref $A)) |
| ;; CHECK-NEXT: (struct.new_default $A) |
| ;; CHECK-NEXT: ) |
| (func $init (result (ref $A)) |
| (struct.new_default $A) |
| ) |
| |
| ;; CHECK: (func $rmw-cmpxchg (type $1) (param $0 (ref $A)) (param $1 i32) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-cmpxchg (param (ref $A) i32) (result i32) |
| ;; This cmpxchg does not change the value, but cannot be optimized. |
| (struct.atomic.rmw.cmpxchg $A 0 |
| (local.get 0) |
| (local.get 1) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $rmw-cmpxchg-acqrel (type $1) (param $0 (ref $A)) (param $1 i32) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg acqrel acqrel $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-cmpxchg-acqrel (param (ref $A) i32) (result i32) |
| ;; Acqrel accesses do not make a difference. |
| (struct.atomic.rmw.cmpxchg acqrel acqrel $A 0 |
| (local.get 0) |
| (local.get 1) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $get (type $3) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $get (param (ref $A)) (result i32) |
| ;; This get is optimizable. |
| (struct.get $A 0 |
| (local.get 0) |
| ) |
| ) |
| ) |
| |
| ;; cmpxchg copies also cannot be optimized. |
| (module |
| ;; CHECK: (type $A (shared (struct (field (mut i32))))) |
| (type $A (shared (struct (mut i32)))) |
| |
| ;; CHECK: (type $1 (func (result (ref $A)))) |
| |
| ;; CHECK: (type $2 (func (param (ref $A) i32 (ref $A)) (result i32))) |
| |
| ;; CHECK: (type $3 (func (param (ref $A) i32) (result i32))) |
| |
| ;; CHECK: (type $4 (func (param (ref $A)) (result i32))) |
| |
| ;; CHECK: (func $init (type $1) (result (ref $A)) |
| ;; CHECK-NEXT: (struct.new_default $A) |
| ;; CHECK-NEXT: ) |
| (func $init (result (ref $A)) |
| (struct.new_default $A) |
| ) |
| |
| ;; CHECK: (func $rmw-cmpxchg (type $2) (param $0 (ref $A)) (param $1 i32) (param $2 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: (struct.atomic.get $A 0 |
| ;; CHECK-NEXT: (local.get $2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-cmpxchg (param (ref $A) i32 (ref $A)) (result i32) |
| ;; This cmpxchg copies the field, so does not change the value. It still |
| ;; cannot be optimized. |
| (struct.atomic.rmw.cmpxchg $A 0 |
| (local.get 0) |
| (local.get 1) |
| (struct.atomic.get $A 0 |
| (local.get 2) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $rmw-cmpxchg-acqrel (type $3) (param $0 (ref $A)) (param $1 i32) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg acqrel acqrel $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-cmpxchg-acqrel (param (ref $A) i32) (result i32) |
| ;; Acqrel accesses to constant fields do not make a difference. |
| (struct.atomic.rmw.cmpxchg acqrel acqrel $A 0 |
| (local.get 0) |
| (local.get 1) |
| (i32.const 0) |
| ) |
| ) |
| |
| ;; CHECK: (func $get (type $4) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $get (param (ref $A)) (result i32) |
| ;; This get is optimizable. |
| (struct.get $A 0 |
| (local.get 0) |
| ) |
| ) |
| ) |
| |
| ;; In principle this version can be optimized because the cmpxchg will never |
| ;; perform a write and there are no other writes for it to synchronize with. |
| ;; TODO: optimize this. |
| (module |
| ;; CHECK: (type $A (shared (struct (field (mut i32))))) |
| (type $A (shared (struct (mut i32)))) |
| |
| ;; CHECK: (type $1 (func (param (ref $A)) (result i32))) |
| |
| ;; CHECK: (type $2 (func (result (ref $A)))) |
| |
| ;; CHECK: (func $init (type $2) (result (ref $A)) |
| ;; CHECK-NEXT: (struct.new_default $A) |
| ;; CHECK-NEXT: ) |
| (func $init (result (ref $A)) |
| (struct.new_default $A) |
| ) |
| |
| ;; CHECK: (func $rmw-cmpxchg-mutate (type $1) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.atomic.rmw.cmpxchg $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $rmw-cmpxchg-mutate (param (ref $A)) (result i32) |
| ;; This cmpxchg will never change the field because the compare will never |
| ;; succeed. |
| (struct.atomic.rmw.cmpxchg $A 0 |
| (local.get 0) |
| (i32.const 1) |
| (i32.const 1) |
| ) |
| ) |
| |
| ;; CHECK: (func $get (type $1) (param $0 (ref $A)) (result i32) |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param (ref $A)) (result i32) |
| ;; This get is optimizable in principle. |
| (struct.get $A 0 |
| (local.get 0) |
| ) |
| ) |
| ) |