;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: wasm-opt %s -all --type-ssa --preserve-type-order -S -o - | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $used-in-struct-rmw (sub (struct (field anyref))))
    (type $used-in-struct-rmw (sub (struct (field anyref))))
    ;; CHECK:       (type $used-in-struct-rmw-ok (sub (struct (field anyref))))
    (type $used-in-struct-rmw-ok (sub (struct (field anyref))))
    ;; CHECK:       (type $expected-in-struct-cmpxchg (sub (struct (field anyref))))
    (type $expected-in-struct-cmpxchg (sub (struct (field anyref))))
    ;; CHECK:       (type $used-in-struct-cmpxchg (sub $expected-in-struct-cmpxchg (struct (field anyref))))
    (type $used-in-struct-cmpxchg (sub $expected-in-struct-cmpxchg (struct (field anyref))))
    ;; CHECK:       (type $used-in-struct-cmpxchg-ok (sub $expected-in-struct-cmpxchg (struct (field anyref))))
    (type $used-in-struct-cmpxchg-ok (sub $expected-in-struct-cmpxchg (struct (field anyref))))
  )

  ;; CHECK:      (type $struct-rmw-exact (struct (field (mut (ref (exact $used-in-struct-rmw))))))
  (type $struct-rmw-exact (struct (field (mut (ref (exact $used-in-struct-rmw))))))
  ;; CHECK:      (type $struct-rmw-inexact (struct (field (mut (ref $used-in-struct-rmw-ok)))))
  (type $struct-rmw-inexact (struct (field (mut (ref $used-in-struct-rmw-ok)))))

  ;; CHECK:      (type $struct-cmpxchg-exact (struct (field (mut (ref (exact $used-in-struct-cmpxchg))))))
  (type $struct-cmpxchg-exact (struct (field (mut (ref (exact $used-in-struct-cmpxchg))))))
  ;; CHECK:      (type $struct-cmpxchg-inexact (struct (field (mut (ref $used-in-struct-cmpxchg-ok)))))
  (type $struct-cmpxchg-inexact (struct (field (mut (ref $used-in-struct-cmpxchg-ok)))))

  ;; CHECK:      (type $9 (func (param (ref (exact $used-in-struct-rmw)) (ref $struct-rmw-exact))))

  ;; CHECK:      (type $10 (func (param (ref (exact $used-in-struct-rmw-ok)) (ref $struct-rmw-inexact))))

  ;; CHECK:      (type $11 (func (param (ref (exact $used-in-struct-cmpxchg)) (ref (exact $expected-in-struct-cmpxchg)) (ref $struct-cmpxchg-exact))))

  ;; CHECK:      (type $12 (func (param (ref (exact $used-in-struct-cmpxchg-ok)) (ref (exact $expected-in-struct-cmpxchg)) (ref $struct-cmpxchg-inexact))))

  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $used-in-struct-rmw-ok_1 (sub $used-in-struct-rmw-ok (struct (field anyref))))

  ;; CHECK:       (type $expected-in-struct-cmpxchg_2 (sub $expected-in-struct-cmpxchg (struct (field anyref))))

  ;; CHECK:       (type $expected-in-struct-cmpxchg_3 (sub $expected-in-struct-cmpxchg (struct (field anyref))))

  ;; CHECK:       (type $used-in-struct-cmpxchg-ok_4 (sub $used-in-struct-cmpxchg-ok (struct (field anyref))))

  ;; CHECK:      (func $struct-rmw (type $9) (param $used (ref (exact $used-in-struct-rmw))) (param $ref (ref $struct-rmw-exact))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.rmw.xchg $struct-rmw-exact 0
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:    (local.get $used)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $used-in-struct-rmw
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $struct-rmw (param $used (ref (exact $used-in-struct-rmw))) (param $ref (ref $struct-rmw-exact))
    (drop
      ;; This requires and observes an exact $used-in-struct-rmw
      (struct.atomic.rmw.xchg $struct-rmw-exact 0
        (local.get $ref)
        (local.get $used)
      )
    )
    (drop
      ;; So this cannot be optimized since we don't do a flow analysis and don't
      ;; know that this particular allocation is never observed to be exact.
      (struct.new $used-in-struct-rmw
        (ref.null none)
      )
    )
  )

  ;; CHECK:      (func $struct-rmw-ok (type $10) (param $used (ref (exact $used-in-struct-rmw-ok))) (param $ref (ref $struct-rmw-inexact))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.rmw.xchg $struct-rmw-inexact 0
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:    (local.get $used)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $used-in-struct-rmw-ok_1
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $struct-rmw-ok (param $used (ref (exact $used-in-struct-rmw-ok))) (param $ref (ref $struct-rmw-inexact))
    (drop
      ;; But this time the operand is not required to be exact.
      (struct.atomic.rmw.xchg $struct-rmw-inexact 0
        (local.get $ref)
        (local.get $used)
      )
    )
    (drop
      ;; So this can be optimized.
      (struct.new $used-in-struct-rmw-ok
        (ref.null none)
      )
    )
  )

  ;; CHECK:      (func $struct-cmpxchg (type $11) (param $used (ref (exact $used-in-struct-cmpxchg))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-exact))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.rmw.cmpxchg $struct-cmpxchg-exact 0
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:    (local.get $expected)
  ;; CHECK-NEXT:    (local.get $used)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $expected-in-struct-cmpxchg_2
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $used-in-struct-cmpxchg
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $struct-cmpxchg (param $used (ref (exact $used-in-struct-cmpxchg))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-exact))
    (drop
      (struct.atomic.rmw.cmpxchg $struct-cmpxchg-exact 0
        (local.get $ref)
        (local.get $expected)
        (local.get $used)
      )
    )
    (drop
      ;; This can still be optimized. Only the written value is affected.
      (struct.new $expected-in-struct-cmpxchg
        (ref.null none)
      )
    )
    (drop
      (struct.new $used-in-struct-cmpxchg
        (ref.null none)
      )
    )
  )

  ;; CHECK:      (func $struct-cmpxchg-ok (type $12) (param $used (ref (exact $used-in-struct-cmpxchg-ok))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-inexact))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.rmw.cmpxchg $struct-cmpxchg-inexact 0
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:    (local.get $expected)
  ;; CHECK-NEXT:    (local.get $used)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $expected-in-struct-cmpxchg_3
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $used-in-struct-cmpxchg-ok_4
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $struct-cmpxchg-ok (param $used (ref (exact $used-in-struct-cmpxchg-ok))) (param $expected (ref (exact $expected-in-struct-cmpxchg))) (param $ref (ref $struct-cmpxchg-inexact))
    (drop
      (struct.atomic.rmw.cmpxchg $struct-cmpxchg-inexact 0
        (local.get $ref)
        (local.get $expected)
        (local.get $used)
      )
    )
    ;; Now both can be optimized.
    (drop
     (struct.new $expected-in-struct-cmpxchg
        (ref.null none)
      )
    )
    (drop
      (struct.new $used-in-struct-cmpxchg-ok
        (ref.null none)
      )
    )
  )
)
