;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

;; RUN: foreach %s %t wasm-opt --type-ssa -all -S -o - | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (descriptor $desc) (struct))
    (type $struct (descriptor $desc) (struct))
    ;; CHECK:       (type $desc (sub (describes $struct) (struct (field i32))))
    (type $desc (sub (describes $struct) (struct (field i32))))
  )
  ;; CHECK:      (type $2 (func (result (ref $desc))))

  ;; CHECK:      (func $no-opt-desc (type $2) (result (ref $desc))
  ;; CHECK-NEXT:  (struct.new_default $desc)
  ;; CHECK-NEXT: )
  (func $no-opt-desc (result (ref $desc))
    ;; If we tried to optimize this allocation, we would have to create a new
    ;; type for our new descriptor type to describe. This isn't very useful
    ;; unless we can optimize a corresponding allocation of the described type,
    ;; so do not optimize here.
    (struct.new_default $desc)
  )
)

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

  ;; CHECK:      (type $2 (func (result (ref $struct))))

  ;; CHECK:      (func $final-desc (type $2) (result (ref $struct))
  ;; CHECK-NEXT:  (struct.new_default_desc $struct
  ;; CHECK-NEXT:   (struct.new_default $desc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $final-desc (result (ref $struct))
    ;; We could optimize the allocation of the struct, but we would need to
    ;; update the descriptor to be a corresponding new subtype. The descriptor
    ;; is final, so this is not posssible and we cannot optimize.
    (struct.new_default_desc $struct
      (struct.new $desc)
    )
  )
)

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

  ;; CHECK:      (type $2 (func (result (ref $struct))))

  ;; CHECK:      (func $opt (type $2) (result (ref $struct))
  ;; CHECK-NEXT:  (struct.new_default_desc $struct
  ;; CHECK-NEXT:   (struct.new_default $desc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $opt (result (ref $struct))
    ;; Now we could optimize, but we would still have to introduce a new
    ;; descriptor subtype. We do not support this yet.
    (struct.new_default_desc $struct
      (struct.new $desc)
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (sub (descriptor $B) (struct (field i32))))
    (type $A (sub (descriptor $B) (struct (field i32))))
    ;; CHECK:       (type $B (sub (describes $A) (descriptor $C) (struct)))
    (type $B (sub (describes $A) (descriptor $C) (struct)))
    ;; CHECK:       (type $C (sub (describes $B) (descriptor $D) (struct)))
    (type $C (sub (describes $B) (descriptor $D) (struct)))
    ;; CHECK:       (type $D (sub (describes $C) (struct)))
    (type $D (sub (describes $C) (struct)))
  )

  ;; CHECK:      (type $4 (func (result (ref $A))))

  ;; CHECK:      (func $opt-chain (type $4) (result (ref $A))
  ;; CHECK-NEXT:  (struct.new_default_desc $A
  ;; CHECK-NEXT:   (struct.new_default_desc $B
  ;; CHECK-NEXT:    (struct.new_default_desc $C
  ;; CHECK-NEXT:     (struct.new_default $D)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $opt-chain (result (ref $A))
    ;; Now optimizing would require updating a whole chain of descriptors.
    (struct.new_default_desc $A
      (struct.new_desc $B
        (struct.new_desc $C
          (struct.new $D)
        )
      )
    )
  )
)

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

  ;; CHECK:      (type $2 (func (result (ref $struct))))

  ;; CHECK:      (global $desc (ref (exact $desc)) (struct.new_default $desc))
  (global $desc (ref (exact $desc)) (struct.new $desc))

  ;; CHECK:      (func $global-desc (type $2) (result (ref $struct))
  ;; CHECK-NEXT:  (struct.new_default_desc $struct
  ;; CHECK-NEXT:   (global.get $desc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $global-desc (result (ref $struct))
    ;; Optimizing below would require creating a new global to hold the new
    ;; descriptor subtype.
    (struct.new_default_desc $struct
      (global.get $desc)
    )
  )
)

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

  ;; CHECK:      (type $2 (func (result i32)))

  ;; CHECK:      (global $desc (ref (exact $desc)) (struct.new_default $desc))
  (global $desc (ref (exact $desc)) (struct.new $desc))

  ;; CHECK:      (func $no-opt-desc-identity (type $2) (result i32)
  ;; CHECK-NEXT:  (ref.eq
  ;; CHECK-NEXT:   (ref.get_desc $struct
  ;; CHECK-NEXT:    (block (result (ref (exact $struct)))
  ;; CHECK-NEXT:     (struct.new_default_desc $struct
  ;; CHECK-NEXT:      (global.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (global.get $desc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-opt-desc-identity (result i32)
    ;; Same as above, but now we cannot optimize because the identity of the
    ;; descriptors matters and we cannot replace them with different subtypes.
    (ref.eq
      (ref.get_desc $struct
        ;; Use a block to stop the ref.get_desc from observing the exactness of
        ;; the allocation, which would separately inhibit optimization.
        (block (result (ref $struct))
          (struct.new_default_desc $struct
            (global.get $desc)
          )
        )
      )
      (global.get $desc)
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (sub (descriptor $B) (struct (field i32))))
    (type $A (sub (descriptor $B) (struct (field i32))))
    ;; CHECK:       (type $B (sub (describes $A) (descriptor $C) (struct)))
    (type $B (sub (describes $A) (descriptor $C) (struct)))
    ;; CHECK:       (type $C (sub (describes $B) (descriptor $D) (struct)))
    (type $C (sub (describes $B) (descriptor $D) (struct)))
    ;; CHECK:       (type $D (sub (describes $C) (struct)))
    (type $D (sub (describes $C) (struct)))
  )

  ;; CHECK:      (type $4 (func (result (ref $A))))

  ;; CHECK:      (global $D (ref (exact $D)) (struct.new_default $D))
  (global $D (ref (exact $D)) (struct.new $D))
  ;; CHECK:      (global $C (ref (exact $C)) (struct.new_default_desc $C
  ;; CHECK-NEXT:  (global.get $D)
  ;; CHECK-NEXT: ))
  (global $C (ref (exact $C)) (struct.new_desc $C (global.get $D)))
  ;; CHECK:      (global $B (ref (exact $B)) (struct.new_default_desc $B
  ;; CHECK-NEXT:  (global.get $C)
  ;; CHECK-NEXT: ))
  (global $B (ref (exact $B)) (struct.new_desc $B (global.get $C)))

  ;; CHECK:      (func $opt-global-chain (type $4) (result (ref $A))
  ;; CHECK-NEXT:  (struct.new_default_desc $A
  ;; CHECK-NEXT:   (global.get $B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $opt-global-chain (result (ref $A))
    ;; Same as above, but now we would have to copy a whole chain of descriptor
    ;; globals.
    (struct.new_default_desc $A
      (global.get $B)
    )
  )
)
