;; 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)
    )
  )
)
