;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --gsi -all --closed-world -S -o - | filecheck %s

(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; A non-reference global does not confuse us.
  ;; CHECK:      (global $global-other i32 (i32.const 123456))
  (global $global-other i32 (i32.const 123456))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; We can infer that this get can reference either $global1 or $global2,
    ;; and nothing else (aside from a null), and can emit a select between
    ;; those values.
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but now the field is mutable, so we cannot optimize.
(module
  ;; CHECK:      (type $struct (struct (field (mut i32))))
  (type $struct (struct (mut i32)))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Just one global.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct1 (struct (field i32)))
    (type $struct1 (struct i32))
    ;; CHECK:       (type $struct2 (struct (field i32)))
    (type $struct2 (struct i32))
  )

  ;; CHECK:      (type $2 (func (param (ref null $struct1) (ref null $struct2))))

  ;; CHECK:      (import "a" "b" (global $imported i32))
  (import "a" "b" (global $imported i32))

  ;; CHECK:      (global $global1 (ref $struct1) (struct.new $struct1
  ;; CHECK-NEXT:  (global.get $imported)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct1) (struct.new $struct1
    (global.get $imported)
  ))

  ;; CHECK:      (global $global2 (ref $struct2) (struct.new $struct2
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct2) (struct.new $struct2
    (i32.const 42)
  ))

  ;; CHECK:      (func $test1 (type $2) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct1 0
  ;; CHECK-NEXT:    (block (result (ref $struct1))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $struct1)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct2 0
  ;; CHECK-NEXT:    (block (result (ref $struct2))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $struct2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test1 (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
    ;; Even though the value here is not known at compile time - it reads an
    ;; imported global - we can still infer that we are reading from $global1.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    ;; We can infer that this get must reference $global2 and make the reference
    ;; point to that. Note that we do not infer the value of 42 here, but leave
    ;; it for other passes to do.
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
  )
)

;; Three globals. For now, we do not optimize here.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 99999)
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.const 99999)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Three globals, as above, but now two agree on their values. We can optimize
;; by comparing to the one that has a single value.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but move the different value of the three to the middle.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but move the different value of the three to the end.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Four values, two pairs of equal ones. We do not optimize this.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global4 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global4 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Four values, three equal and one unique. We can optimize this with a single
;; comparison on the unique one.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global4 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global4 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; A struct.new inside a function stops us from optimizing.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (i32.const 1)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; A struct.new in a non-toplevel position in a global stops us from
;; optimizing.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $tuple (struct (field anyref) (field anyref)))
  (type $tuple (struct anyref anyref))

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global-tuple (ref $tuple) (struct.new $tuple
  ;; CHECK-NEXT:  (struct.new $struct
  ;; CHECK-NEXT:   (i32.const 999999)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (ref.null none)
  ;; CHECK-NEXT: ))
  (global $global-tuple (ref $tuple) (struct.new $tuple
    (struct.new $struct
      (i32.const 999999)
    )
    (ref.null any)
  ))

  ;; CHECK:      (func $test (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but remove the struct.new in a nested position, while keeping all
;; the other stuff in the above test. Now we should optimize.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $tuple (struct (field anyref) (field anyref)))
  (type $tuple (struct anyref anyref))

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global-tuple (ref $tuple) (struct.new $tuple
  ;; CHECK-NEXT:  (ref.null none)
  ;; CHECK-NEXT:  (ref.null none)
  ;; CHECK-NEXT: ))
  (global $global-tuple (ref $tuple) (struct.new $tuple
    (ref.null any)
    (ref.null any)
  ))

  ;; CHECK:      (func $test (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; When one of the globals is mutable, we cannot optimize.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (mut (ref $struct)) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (mut (ref $struct)) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; A subtype is not optimizable, which prevents $struct from being optimized.
(module
  ;; CHECK:      (type $struct (sub (struct (field i32))))
  (type $struct (sub (struct i32)))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (type $sub-struct (sub $struct (struct (field i32))))
  (type $sub-struct (sub $struct (struct i32)))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $sub-struct
  ;; CHECK-NEXT:    (i32.const 999999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $sub-struct
        (i32.const 999999)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; A *super*-type is not optimizable, but that does not block us, and we can
;; optimize.
(module
  ;; CHECK:      (type $super-struct (sub (struct (field i32))))
  (type $super-struct (sub (struct i32)))

  ;; CHECK:      (type $struct (sub $super-struct (struct (field i32))))
  (type $struct (sub $super-struct (struct i32)))

  ;; CHECK:      (type $2 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $2) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $super-struct
  ;; CHECK-NEXT:    (i32.const 999999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $super-struct
        (i32.const 999999)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; One global for each of the type and the subtype. The optimization will pick
;; between their 2 values.
(module
  ;; CHECK:      (type $super-struct (sub (struct (field i32))))
  (type $super-struct (sub (struct i32)))

  ;; CHECK:      (type $struct (sub $super-struct (struct (field i32))))
  (type $struct (sub $super-struct (struct i32)))

  ;; CHECK:      (type $2 (func (param (ref null $struct) (ref null $super-struct))))

  ;; CHECK:      (global $global1 (ref $super-struct) (struct.new $super-struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $super-struct) (struct.new $super-struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $2) (param $struct (ref null $struct)) (param $super-struct (ref null $super-struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (block (result (ref $struct))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $struct)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $super-struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct)) (param $super-struct (ref null $super-struct))
    ;; The first has just one global, which we switch the reference to, while
    ;; the second will consider the struct and sub-struct, find 2 possible
    ;; values, and optimize.
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
    (drop
      (struct.get $super-struct 0
        (local.get $super-struct)
      )
    )
  )
)

;; One global has a non-constant field. We can still optimize, if we move that
;; field out into another global, that is, if we un-nest it. The select will
;; then pick either the constant or a global.get of the new un-nested global.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1.unnested.0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 41)
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $global1.unnested.0)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 41)
      (i32.const 1)
    )
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.0)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but with the globals flipped. Now the second global has a non-
;; constant field.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global2.unnested.0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 41)
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $global2.unnested.0)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 41)
      (i32.const 1)
    )
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (global.get $global2.unnested.0)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but now both globals have non-constant fields. We un-nest both.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1.unnested.0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 13)
  ;; CHECK-NEXT:  (i32.const 37)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global2.unnested.0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 41)
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $global1.unnested.0)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 13)
      (i32.const 37)
    )
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $global2.unnested.0)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 41)
      (i32.const 1)
    )
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.0)
  ;; CHECK-NEXT:    (global.get $global2.unnested.0)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Multiple and overlapping un-nesting situations.
(module
  ;; CHECK:      (type $struct (struct (field i32) (field i32)))
  (type $struct (struct i32 i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1.unnested.0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 13)
  ;; CHECK-NEXT:  (i32.const 37)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global2.unnested.0 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 41)
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global1.unnested.1 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 99)
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $global1.unnested.0)
  ;; CHECK-NEXT:  (global.get $global1.unnested.1)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 13)
      (i32.const 37)
    )
    (i32.add
      (i32.const 99)
      (i32.const 1)
    )
  ))

  ;; CHECK:      (global $global2.unnested.1 i32 (i32.add
  ;; CHECK-NEXT:  (i32.const 100)
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $global2.unnested.0)
  ;; CHECK-NEXT:  (global.get $global2.unnested.1)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 41)
      (i32.const 1)
    )
    (i32.add
      (i32.const 100)
      (i32.const 2)
    )
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.0)
  ;; CHECK-NEXT:    (global.get $global2.unnested.0)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.0)
  ;; CHECK-NEXT:    (global.get $global2.unnested.0)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; We only need to un-nest once for these two.
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )

  ;; CHECK:      (func $test2 (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.0)
  ;; CHECK-NEXT:    (global.get $global2.unnested.0)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.1)
  ;; CHECK-NEXT:    (global.get $global2.unnested.1)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $global1.unnested.1)
  ;; CHECK-NEXT:    (global.get $global2.unnested.1)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test2 (param $struct (ref null $struct))
    ;; Add another get of 0 in another function.
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
    ;; Add gets of the second field in the struct.
    (drop
      (struct.get $struct 1
        (local.get $struct)
      )
    )
    (drop
      (struct.get $struct 1
        (local.get $struct)
      )
    )
  )
)

;; Three globals with non-constant fields. We do not optimize as we cannot pick
;; between three values with a single comparison.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.add
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 42)
      (i32.const 0)
    )
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.add
  ;; CHECK-NEXT:   (i32.const 1337)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 1337)
      (i32.const 0)
    )
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.add
  ;; CHECK-NEXT:   (i32.const 99999)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 99999)
      (i32.const 0)
    )
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but now two of the three's non-constant fields are identical. That
;; does not help us: they are still non-constant, and we do nothing. (But, other
;; passes might simplify things by un-nesting the identical code.)
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.add
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 42)
      (i32.const 0)
    )
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.add
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 42)
      (i32.const 0)
    )
  ))

  ;; CHECK:      (global $global3 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.add
  ;; CHECK-NEXT:   (i32.const 99999)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $global3 (ref $struct) (struct.new $struct
    (i32.add
      (i32.const 99999)
      (i32.const 0)
    )
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; One global each for two subtypes of a common supertype, and one for the
;; supertype.
(module
  ;; CHECK:      (type $super-struct (sub (struct (field i32))))
  (type $super-struct (sub (struct i32)))

  ;; CHECK:      (type $struct1 (sub $super-struct (struct (field i32) (field f32))))
  (type $struct1 (sub $super-struct (struct i32 f32)))

  ;; CHECK:      (type $struct2 (sub $super-struct (struct (field i32) (field f64))))
  (type $struct2 (sub $super-struct (struct i32 f64)))


  ;; CHECK:      (type $3 (func (param (ref null $super-struct) (ref null $struct1) (ref null $struct2))))

  ;; CHECK:      (global $global0 (ref $super-struct) (struct.new $super-struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global0 (ref $super-struct) (struct.new $super-struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global1 (ref $struct1) (struct.new $struct1
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT:  (f32.const 3.141590118408203)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct1) (struct.new $struct1
    (i32.const 1337)
    (f32.const 3.14159)
  ))

  ;; CHECK:      (global $global2 (ref $struct2) (struct.new $struct2
  ;; CHECK-NEXT:  (i32.const 99999)
  ;; CHECK-NEXT:  (f64.const 2.71828)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct2) (struct.new $struct2
    (i32.const 99999)
    (f64.const 2.71828)
  ))

  ;; CHECK:      (func $test (type $3) (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $super-struct 0
  ;; CHECK-NEXT:    (local.get $super-struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct1 0
  ;; CHECK-NEXT:    (block (result (ref $struct1))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $struct1)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct2 0
  ;; CHECK-NEXT:    (block (result (ref $struct2))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $struct2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
    ;; This has three possible values due to the two children, so we do not
    ;; optimize.
    (drop
      (struct.get $super-struct 0
        (local.get $super-struct)
      )
    )
    ;; These each have one possible value, which we can switch the references
    ;; to.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
  )
)

;; As above, but now the subtypes each have 2 values, and we can optimize.
(module
  ;; CHECK:      (type $super-struct (sub (struct (field i32))))
  (type $super-struct (sub (struct i32)))

  ;; CHECK:      (type $struct1 (sub $super-struct (struct (field i32) (field f32))))
  (type $struct1 (sub $super-struct (struct i32 f32)))

  ;; CHECK:      (type $struct2 (sub $super-struct (struct (field i32) (field f64))))
  (type $struct2 (sub $super-struct (struct i32 f64)))


  ;; CHECK:      (type $3 (func (param (ref null $super-struct) (ref null $struct1) (ref null $struct2))))

  ;; CHECK:      (global $global0 (ref $super-struct) (struct.new $super-struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global0 (ref $super-struct) (struct.new $super-struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global1 (ref $struct1) (struct.new $struct1
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT:  (f32.const 3.141590118408203)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct1) (struct.new $struct1
    (i32.const 1337)
    (f32.const 3.14159)
  ))

  ;; CHECK:      (global $global1b (ref $struct1) (struct.new $struct1
  ;; CHECK-NEXT:  (i32.const 1338)
  ;; CHECK-NEXT:  (f32.const 3.141590118408203)
  ;; CHECK-NEXT: ))
  (global $global1b (ref $struct1) (struct.new $struct1
    (i32.const 1338)
    (f32.const 3.14159)
  ))

  ;; CHECK:      (global $global2 (ref $struct2) (struct.new $struct2
  ;; CHECK-NEXT:  (i32.const 99999)
  ;; CHECK-NEXT:  (f64.const 2.71828)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct2) (struct.new $struct2
    (i32.const 99999)
    (f64.const 2.71828)
  ))

  ;; CHECK:      (global $global2b (ref $struct2) (struct.new $struct2
  ;; CHECK-NEXT:  (i32.const 99998)
  ;; CHECK-NEXT:  (f64.const 2.71828)
  ;; CHECK-NEXT: ))
  (global $global2b (ref $struct2) (struct.new $struct2
    (i32.const 99998)
    (f64.const 2.71828)
  ))

  ;; CHECK:      (func $test (type $3) (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $super-struct 0
  ;; CHECK-NEXT:    (local.get $super-struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (i32.const 1338)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 99999)
  ;; CHECK-NEXT:    (i32.const 99998)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $super-struct (ref null $super-struct)) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2))
    ;; This still cannot be optimized.
    (drop
      (struct.get $super-struct 0
        (local.get $super-struct)
      )
    )
    ;; These can be optimized, and will be different from one another.
    (drop
      (struct.get $struct1 0
        (local.get $struct1)
      )
    )
    (drop
      (struct.get $struct2 0
        (local.get $struct2)
      )
    )
  )
)

;; Multiple globals, but all the same value, so we do not even need a select and
;; can just apply the value.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; One global is declared as heap type |any|, which we cannot do a ref.eq on, so
;; we do not optimize.
(module
  ;; CHECK:      (type $A (struct (field i32)))
  (type $A (struct (field i32)))

  ;; CHECK:      (type $1 (func (param (ref null $A)) (result i32)))

  ;; CHECK:      (global $A0 (ref any) (struct.new $A
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $A0 (ref any) (struct.new $A
    (i32.const 1337)
  ))

  ;; CHECK:      (global $A1 (ref $A) (struct.new $A
  ;; CHECK-NEXT:  (i32.const 9999)
  ;; CHECK-NEXT: ))
  (global $A1 (ref $A) (struct.new $A
    (i32.const 9999)
  ))

  ;; CHECK:      (func $func (type $1) (param $ref (ref null $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $func (param $ref (ref null $A)) (result i32)
    (struct.get $A 0
      (local.get $ref)
    )
  )
)

;; As above, but now there is just a single global. Again, we should not
;; optimize because the global is not declared as a struct type (which means we
;; cannot do a struct.get on a global.get of that global - we'd need a cast; it
;; is simpler to not optimize here and let other passes first refine the global
;; type).
(module
  ;; CHECK:      (type $A (struct (field i32)))
  (type $A (struct (field i32)))

  ;; CHECK:      (type $1 (func (param (ref null $A)) (result i32)))

  ;; CHECK:      (global $A0 (ref any) (struct.new $A
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $A0 (ref any) (struct.new $A
    (i32.const 1337)
  ))

  ;; CHECK:      (func $func (type $1) (param $ref (ref null $A)) (result i32)
  ;; CHECK-NEXT:  (struct.get $A 0
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $func (param $ref (ref null $A)) (result i32)
    (struct.get $A 0
      (local.get $ref)
    )
  )
)

(module
  ;; CHECK:      (type $A (struct (field i32)))
  (type $A (struct (field i32)))

  ;; CHECK:      (type $1 (func (param (ref null $A)) (result i32)))

  ;; CHECK:      (global $A0 (ref $A) (struct.new $A
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $A0 (ref $A) (struct.new $A
    (i32.const 1337)
  ))

  ;; CHECK:      (func $func (type $1) (param $ref (ref null $A)) (result i32)
  ;; CHECK-NEXT:  (block ;; (replaces unreachable StructGet we can't emit)
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $func (param $ref (ref null $A)) (result i32)
    ;; Test that we do not error when we see a struct.get of a bottom type.
    (struct.get $A 0
      (ref.null none)
    )
  )
)

;; Two subtypes, each with a global. A get of the parent can be optimized into
;; a select, as it must read one of the children.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (sub (struct (field i32))))
    (type $struct (sub (struct i32)))

    ;; CHECK:       (type $sub-struct1 (sub $struct (struct (field i32))))
    (type $sub-struct1 (sub $struct (struct i32)))

    ;; CHECK:       (type $sub-struct2 (sub $struct (struct (field i32))))
    (type $sub-struct2 (sub $struct (struct i32)))
  )

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, but now the parent is unoptimizable due to a struct.new in a
;; function. We must not optimize here to a select.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (sub (struct (field i32))))
    (type $struct (sub (struct i32)))

    ;; CHECK:       (type $sub-struct1 (sub $struct (struct (field i32))))
    (type $sub-struct1 (sub $struct (struct i32)))

    ;; CHECK:       (type $sub-struct2 (sub $struct (struct (field i32))))
    (type $sub-struct2 (sub $struct (struct i32)))
  )

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $struct
  ;; CHECK-NEXT:    (i32.const 999999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $struct
        (i32.const 999999)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; As above, the struct.new in a function is of a subtype. Again, we cannot
;; optimize, as unoptimizability spreads to supertypes.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (sub (struct (field i32))))
    (type $struct (sub (struct i32)))

    ;; CHECK:       (type $sub-struct1 (sub $struct (struct (field i32))))
    (type $sub-struct1 (sub $struct (struct i32)))

    ;; CHECK:       (type $sub-struct2 (sub $struct (struct (field i32))))
    (type $sub-struct2 (sub $struct (struct i32)))
  )

  ;; CHECK:      (type $3 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $sub-struct1) (struct.new $sub-struct1
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $sub-struct2) (struct.new $sub-struct2
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $3) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $sub-struct1
  ;; CHECK-NEXT:    (i32.const 999999)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.new $sub-struct1
        (i32.const 999999)
      )
    )
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (sub (struct (field funcref))))
    (type $A (sub (struct (field funcref))))
    ;; CHECK:       (type $B (sub $A (struct (field (ref func)))))
    (type $B (sub $A (struct (field (ref func)))))
  )

  ;; CHECK:      (type $2 (func (param (ref null $A) (ref null $B)) (result funcref)))

  ;; CHECK:      (global $global (ref $B) (struct.new $B
  ;; CHECK-NEXT:  (ref.func $func)
  ;; CHECK-NEXT: ))
  (global $global (ref $B) (struct.new $B
    (ref.func $func)
  ))

  ;; CHECK:      (func $func (type $2) (param $a (ref null $A)) (param $b (ref null $B)) (result funcref)
  ;; CHECK-NEXT:  (struct.get $B 0
  ;; CHECK-NEXT:   (block (result (ref $B))
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.tee $a
  ;; CHECK-NEXT:       (local.get $b)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $global)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $func (param $a (ref null $A)) (param $b (ref null $B)) (result funcref)
    (struct.get $A 0
      ;; We can infer that we read from $global here, since it is the only place
      ;; a $B is created (the tee in the middle to $A does not confuse us).
      ;; After that, the struct.get will be reading a global.get of $global,
      ;; which is of type $B, and compared to $A from before we will read a more
      ;; refined type from the field, ref func vs funcref, which must be updated
      ;; in the IR.
      (local.tee $a
        (local.get $b)
      )
    )
  )
)

;; Test that we can optimize global.get operations on immutable globals.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $one i32 (i32.const 1))
  (global $one i32 (i32.const 1))

  ;; CHECK:      (global $two i32 (i32.const 2))
  (global $two i32 (i32.const 2))

  ;; CHECK:      (global $global1 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $one)
  ;; CHECK-NEXT: ))
  (global $global1 (ref $struct) (struct.new $struct
    (global.get $one)
  ))

  ;; CHECK:      (global $global2 (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $two)
  ;; CHECK-NEXT: ))
  (global $global2 (ref $struct) (struct.new $struct
    (global.get $two)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (global.get $one)
  ;; CHECK-NEXT:    (global.get $two)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; The get here will read one of the two globals, so we can use a select.
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Test packed fields.
(module
  ;; CHECK:      (type $struct (struct (field i8)))
  (type $struct (struct (field i8)))

  ;; CHECK:      (type $1 (func (param (ref $struct)) (result i32)))

  ;; CHECK:      (global $A (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 257)
  ;; CHECK-NEXT: ))
  (global $A (ref $struct) (struct.new $struct
    (i32.const 257)
  ))

  ;; CHECK:      (global $B (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 258)
  ;; CHECK-NEXT: ))
  (global $B (ref $struct) (struct.new $struct
    (i32.const 258)
  ))

  ;; CHECK:      (func $test (type $1) (param $x (ref $struct)) (result i32)
  ;; CHECK-NEXT:  (select
  ;; CHECK-NEXT:   (i32.and
  ;; CHECK-NEXT:    (i32.const 257)
  ;; CHECK-NEXT:    (i32.const 255)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (i32.and
  ;; CHECK-NEXT:    (i32.const 258)
  ;; CHECK-NEXT:    (i32.const 255)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.eq
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $x (ref $struct)) (result i32)
    ;; We can infer this value is one of two things since only two objects exist
    ;; of this type. We must emit the proper truncated value for them, as the
    ;; values are truncated into i8.
    (struct.get_u $struct 0
     (local.get $x)
    )
  )
)

;; Test atomic gets.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $one (shared (struct (field i32))))
    (type $one (shared (struct (field i32))))
    ;; CHECK:       (type $two (shared (struct (field i32))))
    (type $two (shared (struct (field i32))))
    ;; CHECK:       (type $two-same (shared (struct (field i32))))
    (type $two-same (shared (struct (field i32))))
  )

  ;; CHECK:      (type $3 (func (param (ref $one))))

  ;; CHECK:      (type $4 (func (param (ref $two))))

  ;; CHECK:      (type $5 (func (param (ref $two-same))))

  ;; CHECK:      (global $one (ref $one) (struct.new $one
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $one (ref $one) (struct.new $one (i32.const 42)))

  ;; CHECK:      (global $two-a (ref $two) (struct.new $two
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $two-a (ref $two) (struct.new $two (i32.const 42)))

  ;; CHECK:      (global $two-b (ref $two) (struct.new $two
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $two-b (ref $two) (struct.new $two (i32.const 1337)))

  ;; CHECK:      (global $two-same-a (ref $two-same) (struct.new $two-same
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $two-same-a (ref $two-same) (struct.new $two-same (i32.const 42)))

  ;; CHECK:      (global $two-same-b (ref $two-same) (struct.new $two-same
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $two-same-b (ref $two-same) (struct.new $two-same (i32.const 42)))

  ;; CHECK:      (func $one (type $3) (param $0 (ref $one))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $one 0
  ;; CHECK-NEXT:    (block (result (ref $one))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $one)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.get acqrel $one 0
  ;; CHECK-NEXT:    (block (result (ref $one))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $one)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.atomic.get $one 0
  ;; CHECK-NEXT:    (block (result (ref $one))
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $one)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $one (param (ref $one))
    (drop
      (struct.get $one 0
        (local.get 0)
      )
    )
    (drop
      (struct.atomic.get acqrel $one 0
        (local.get 0)
      )
    )
    (drop
      (struct.atomic.get $one 0
        (local.get 0)
      )
    )
  )

  ;; CHECK:      (func $two (type $4) (param $0 (ref $two))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $two-a)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $two-a)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $two-a)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $two (param (ref $two))
    (drop
      (struct.get $two 0
        (local.get 0)
      )
    )
    (drop
      ;; This is optimized normally because there cannot be any writes it
      ;; synchronizes with.
      (struct.atomic.get acqrel $two 0
        (local.get 0)
      )
    )
    (drop
      ;; Even though this is seqcst, it still can't synchronize with anything.
      (struct.atomic.get $two 0
        (local.get 0)
      )
    )
  )

  ;; CHECK:      (func $two-same (type $5) (param $0 (ref $two-same))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $two-same (param (ref $two-same))
    (drop
      (struct.get $two-same 0
        (local.get 0)
      )
    )
    (drop
      ;; This is optimized normally because there cannot be any writes it
      ;; synchronizes with.
      (struct.atomic.get acqrel $two-same 0
        (local.get 0)
      )
    )
    (drop
      ;; Even though this is seqcst, it still can't synchronize with anything.
      (struct.atomic.get $two-same 0
        (local.get 0)
      )
    )
  )
)

;; The basic case, but now the globals have type eqref. This should still work.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 eqref (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 eqref (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 eqref (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 eqref (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select
  ;; CHECK-NEXT:    (i32.const 42)
  ;; CHECK-NEXT:    (i32.const 1337)
  ;; CHECK-NEXT:    (ref.eq
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $struct)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (global.get $global1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    ;; We can infer that this get can reference either $global1 or $global2,
    ;; and nothing else (aside from a null), and can emit a select between
    ;; those values.
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; Same, but now the globals have type anyref, so they are not comparable and we
;; cannot optimize.
(module
  ;; CHECK:      (type $struct (struct (field i32)))
  (type $struct (struct i32))

  ;; CHECK:      (type $1 (func (param (ref null $struct))))

  ;; CHECK:      (global $global1 anyref (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: ))
  (global $global1 anyref (struct.new $struct
    (i32.const 42)
  ))

  ;; CHECK:      (global $global2 anyref (struct.new $struct
  ;; CHECK-NEXT:  (i32.const 1337)
  ;; CHECK-NEXT: ))
  (global $global2 anyref (struct.new $struct
    (i32.const 1337)
  ))

  ;; CHECK:      (func $test (type $1) (param $struct (ref null $struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $struct)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $struct (ref null $struct))
    (drop
      (struct.get $struct 0
        (local.get $struct)
      )
    )
  )
)

;; The field has type nullable $A, but we can infer it contains null (as both
;; globals do). This leads to refining the types of the parents.
(module
  ;; CHECK:      (type $A (struct (field (ref null $A))))
  (type $A (struct (field (ref null $A))))

  ;; CHECK:      (type $1 (func (param (ref $A)) (result (ref $A))))

  ;; CHECK:      (global $global$1 (ref (exact $A)) (struct.new_default $A))
  (global $global$1 (ref (exact $A)) (struct.new_default $A))

  ;; CHECK:      (global $global$2 (ref (exact $A)) (struct.new_default $A))
  (global $global$2 (ref (exact $A)) (struct.new_default $A))

  ;; CHECK:      (func $func (type $1) (param $A (ref $A)) (result (ref $A))
  ;; CHECK-NEXT:  (ref.as_non_null
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $A)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $func (param $A (ref $A)) (result (ref $A))
    ;; The block's result should refine.
    (block (result (ref $A))
      (ref.as_non_null
        (struct.get $A 0
          (local.get $A)
        )
      )
    )
  )
)
