;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --heap-store-optimization -all -S -o - \
;; RUN:   | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (descriptor $desc) (struct (field (mut i32))))
    (type $struct (descriptor $desc) (struct (field (mut i32))))
    ;; CHECK:       (type $desc (describes $struct) (descriptor $meta) (struct))
    (type $desc (describes $struct) (descriptor $meta) (struct))
    ;; CHECK:       (type $meta (describes $desc) (struct))
    (type $meta (describes $desc) (struct))
  )

  ;; CHECK:      (import "" "" (func $effect (type $3)))
  (import "" "" (func $effect))

  ;; CHECK:      (func $no-reorder (type $3)
  ;; CHECK-NEXT:  (local $struct (ref $struct))
  ;; CHECK-NEXT:  (local.set $struct
  ;; CHECK-NEXT:   (struct.new_default_desc $struct
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (block $block (result i32)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-reorder
    (local $struct (ref $struct))
    (local.set $struct
      ;; This traps on the null descriptor, so we cannot optimize the struct.set
      ;; into the struct.new (which would call $effect before the trap).
      (struct.new_default_desc $struct
        (ref.null none)
      )
    )
    (struct.set $struct 0
      (local.get $struct)
      (block $block (result i32)
        (call $effect)
        (i32.const 0)
      )
    )
  )

  ;; CHECK:      (func $no-reorder-nested (type $3)
  ;; CHECK-NEXT:  (local $struct (ref $struct))
  ;; CHECK-NEXT:  (local.set $struct
  ;; CHECK-NEXT:   (struct.new_default_desc $struct
  ;; CHECK-NEXT:    (struct.new_default_desc $desc
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $struct 0
  ;; CHECK-NEXT:   (local.get $struct)
  ;; CHECK-NEXT:   (block $block (result i32)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-reorder-nested
    (local $struct (ref $struct))
    ;; As above, but now it is not the top-level allocation that traps, but
    ;; rather its descriptor operand. We still cannot optimize.
    (local.set $struct
      (struct.new_default_desc $struct
        (struct.new_desc $desc
          (ref.null none)
        )
      )
    )
    (struct.set $struct 0
      (local.get $struct)
      (block $block (result i32)
        (call $effect)
        (i32.const 0)
      )
    )
  )

  ;; CHECK:      (func $yes-reorder (type $3)
  ;; CHECK-NEXT:  (local $struct (ref $struct))
  ;; CHECK-NEXT:  (local.set $struct
  ;; CHECK-NEXT:   (struct.new_desc $struct
  ;; CHECK-NEXT:    (block $block (result i32)
  ;; CHECK-NEXT:     (call $effect)
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (struct.new_default_desc $desc
  ;; CHECK-NEXT:     (struct.new_default $meta)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $yes-reorder
    (local $struct (ref $struct))
    ;; Nothing traps, so we can reorder.
    (local.set $struct
      (struct.new_default_desc $struct
        (struct.new_desc $desc
          (struct.new $meta)
        )
      )
    )
    (struct.set $struct 0
      (local.get $struct)
      (block $block (result i32)
        (call $effect)
        (i32.const 0)
      )
    )
  )
)
