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

(module
  ;; CHECK:      (type $A (sub (struct)))
  (type $A (sub (struct)))

  ;; CHECK:      (type $B (sub $A (struct)))
  (type $B (sub $A (struct)))

  ;; CHECK:      (type $void (func))

  ;; CHECK:      (type $D (array (mut i32)))
  (type $D (array (mut i32)))

  (type $void (func))

  ;; CHECK:      (global $a (mut i32) (i32.const 0))
  (global $a (mut i32) (i32.const 0))

  ;; CHECK:      (func $ref.as (type $8) (param $x (ref null $A))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref.as (param $x (ref null $A))
    ;; We duplicate the ref.as to the first local.get, since it is more refined
    ;; than the local.get alone. We then use the refined index throughout.
    (drop
      (local.get $x)
    )
    (drop
      ;; In this case we don't need this ref.as here after the pass as it is
      ;; duplicated above, but we leave that to later opts.
      (ref.as_non_null
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    ;; In this case we don't really need the last ref.as here, because of earlier
    ;; ref.as expressions, but we leave that for later opts.
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $ref.as-no (type $6) (param $x (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref.as-no (param $x (ref $A))
    ;; As above, but the param is now non-nullable anyhow, so we should do
    ;; nothing.

    ;; Because of this, a ref.as_non_null cast is not moved up to the first
    ;; local.get $x even though it could because it would make no difference.
    (drop
      (local.get $x)
    )
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $ref.cast (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref.cast (param $x (ref struct))
    ;; As $ref.as but with ref.casts: we should use the cast value after it has
    ;; been computed, in both gets.
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $not-past-set (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (call $get)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $not-past-set (param $x (ref struct))
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    ;; The local.set in the middle stops us from helping the last get.
    (local.set $x
      (call $get)
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $yes-past-call (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (call $get)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $yes-past-call (param $x (ref struct))
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; The call in the middle does not stops us from helping the last get, since
    ;; if we branch out it doesn't matter what we have below.
    (drop
      (call $get)
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $not-past-call_ref (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $void)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $not-past-call_ref (param $x (ref struct))
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; As in the last function, but a call_ref.
    (call_ref $void
      (ref.func $void)
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $not-backwards-past-call (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $void)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $not-backwards-past-call (param $x (ref struct))
    ;; As above, but here we would like to move a cast *earlier*. We must not do
    ;; that past a possible branch.
    (drop
      (local.get $x)
    )
    (call $void)
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $not-backwards-past-call_ref (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $void
  ;; CHECK-NEXT:   (ref.func $void)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $not-backwards-past-call_ref (param $x (ref struct))
    ;; As above, but with a call_ref.
    (drop
      (local.get $x)
    )
    (call_ref $void
      (ref.func $void)
    )
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $best (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (local $2 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $a
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $a
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $2
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $a
  ;; CHECK-NEXT:   (i32.const 30)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $best (param $x (ref struct))
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; global.sets prevent casts from being moved before them
    ;; but uses can be added after them.
    (global.set $a
      (i32.const 10)
    )
    ;; Here we should use $A.
    (drop
      (local.get $x)
    )
    (global.set $a
      (i32.const 20)
    )
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (global.set $a
      (i32.const 30)
    )
    ;; Here we should use $B, which is even better.
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $best-2 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $best-2 (param $x (ref struct))
    ;; As above, but with the casts reversed. Now we should use $B in both
    ;; gets.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $fallthrough (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (block (result (ref struct))
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $fallthrough (param $x (ref struct))
    (drop
      (ref.cast (ref $A)
        ;; We look through the block, and optimize.
        (block (result (ref struct))
          (local.get $x)
        )
      )
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $past-basic-block (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $past-basic-block (param $x (ref struct))
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; The if means the later get is in another basic block. We do not handle
    ;; this atm.
    (if
      (i32.const 0)
      (then
        (return)
      )
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $multiple (type $7) (param $x (ref struct)) (param $y (ref struct))
  ;; CHECK-NEXT:  (local $a (ref struct))
  ;; CHECK-NEXT:  (local $b (ref struct))
  ;; CHECK-NEXT:  (local $4 (ref $A))
  ;; CHECK-NEXT:  (local $5 (ref $A))
  ;; CHECK-NEXT:  (local.set $a
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $b
  ;; CHECK-NEXT:   (local.get $y)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $4
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $a)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $5
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $b)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $4)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $5)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $b
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $4)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $b)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $multiple (param $x (ref struct)) (param $y (ref struct))
    (local $a (ref struct))
    (local $b (ref struct))
    ;; Two different locals, with overlapping lives.
    (local.set $a
      (local.get $x)
    )
    (local.set $b
      (local.get $y)
    )
    (drop
      (ref.cast (ref $A)
        (local.get $a)
      )
    )
    (drop
      (ref.cast (ref $A)
        (local.get $b)
      )
    )
    ;; These two can be optimized.
    (drop
      (local.get $a)
    )
    (drop
      (local.get $b)
    )
    (local.set $b
      (local.get $x)
    )
    ;; Now only the first can be, since $b changed.
    (drop
      (local.get $a)
    )
    (drop
      (local.get $b)
    )
  )

  ;; CHECK:      (func $move-cast-1 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.tee $1
  ;; CHECK-NEXT:     (ref.cast (ref $B)
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-1 (param $x (ref struct))
    (drop
      ;; The later cast to $B will be moved between ref.cast $A
      ;; and local.get $x. This will cause this ref.cast $A to be
      ;; converted to a second ref.cast $B due to ReFinalize().
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    (drop
      ;; The most refined cast of $x is to $B, which we can move up to
      ;; the top and reuse from there.
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-cast-2 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.tee $1
  ;; CHECK-NEXT:     (ref.cast (ref $B)
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-2 (param $x (ref struct))
    (drop
      ;; As in $move-cast-1, the later cast to $B will be moved
      ;; between ref.cast $A and local.get $x, causing ref.cast $A
      ;; to be converted into a second ref.cast $B by ReFinalize();
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      ;; This will be moved up to the first local.get $x.
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $move-cast-3 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-3 (param $x (ref struct))
    (drop
      (local.get $x)
    )
    (drop
      ;; Converted to $B by ReFinalize().
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      ;; This will be moved up to the first local.get $x.
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-cast-4 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-4 (param $x (ref struct))
    (drop
      (local.get $x)
    )
    (drop
      ;; This will be moved up to the first local.get $x.
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      ;; Converted to $B by ReFinalize().
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-cast-5 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-5 (param $x (ref struct))
    (drop
      ;; The first location is already the most refined cast, so nothing will be moved up.
      ;; (But we will save the cast to a local and re-use it below.)
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      ;; Converted to $B by ReFinalize().
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $move-cast-6 (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-6 (param $x (ref struct))
    (drop
      ;; This is already the most refined cast, so nothing will be moved.
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
    (drop
      ;; Converted to $B by ReFinalize().
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $no-move-already-refined-local (type $9) (param $x (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-move-already-refined-local (param $x (ref $B))
    (drop
      (local.get $x)
    )
    (drop
      ;; Since we know $x is of type $B, this cast to a less refined type $A
      ;; will not be moved higher.
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $no-move-ref.as-to-non-nullable-local (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-move-ref.as-to-non-nullable-local (param $x (ref struct))
    (drop
      (local.get $x)
    )
    (drop
      ;; Since $x is non-nullable, this cast is useless. Hence, this
      ;; will not be duplicated to the first local.get, since doing
      ;; so would also be useless.
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $avoid-erroneous-cast-move (type $6) (param $x (ref $A))
  ;; CHECK-NEXT:  (local $a (ref $A))
  ;; CHECK-NEXT:  (local.set $a
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $D)
  ;; CHECK-NEXT:    (block (result anyref)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $avoid-erroneous-cast-move (param $x (ref $A))
    ;; This test shows that we avoid moving a cast earlier if doing so would
    ;; violate typing rules.
    (local $a (ref $A))
    (local.set $a
      ;; We could move the ref.cast $D here. However, as $a is already known
      ;; to have type ref null $A, not type $D, it would fail, since those
      ;; types are incompatible. Moving the cast will also cause the
      ;; local.set $b to fail, since $b is of type ref null $A, not $D.
      (local.get $x)
    )
    (drop
      (ref.cast (ref $D)
        (block (result anyref)
          (local.get $x)
        )
      )
    )
  )

  ;; CHECK:      (func $move-as-1 (type $3) (param $x structref)
  ;; CHECK-NEXT:  (local $1 (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-as-1 (param $x (ref null struct))
    (drop
      (local.get $x)
    )
    (drop
      ;; The most refined cast of $x is this ref.as_non_null, so we will move it
      ;; and reuse from there.
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-as-2 (type $3) (param $x structref)
  ;; CHECK-NEXT:  (local $1 (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-as-2 (param $x (ref null struct))
    (drop
      ;; This is already the most refined cast, so the cast is not copied
      ;; (but we do save it to a local and use it below).
      (ref.as_non_null
        (local.get $x)
      )
    )
    (drop
      (local.get $x)
    )
  )

  ;; CHECK:      (func $move-cast-side-effects (type $7) (param $x (ref struct)) (param $y (ref struct))
  ;; CHECK-NEXT:  (local $2 (ref $A))
  ;; CHECK-NEXT:  (local $3 (ref $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $a
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $2
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $3
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $y)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (local.get $3)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $3)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-cast-side-effects (param $x (ref struct)) (param $y (ref struct))
    ;; This verifies that casts cannot be moved past side-effect producing
    ;; operations like global.set, and that casts cannot be moved past a local.set
    ;; to its own local index.
    (drop
      (local.get $x)
    )
    ;; Cannot move past global set due to trap possibility, so the cast to $A will
    ;; move up to here but not further up.
    (global.set $a
      (i32.const 10)
    )
    (drop
      (local.get $x)
    )
    (drop
      (local.get $y)
    )
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; Casts to $x cannot be moved past local.set $x, but the cast of $y can and will be.
    (local.set $x
      (local.get $y)
    )
    (drop
      ;; This can be moved past local.set $x.
      (ref.cast (ref $B)
        (local.get $y)
      )
    )
    (drop
      ;; This cannot be moved past local.set $x.
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-ref.as-for-separate-index (type $5) (param $x structref) (param $y structref)
  ;; CHECK-NEXT:  (local $2 (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $2
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $y)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (local.get $2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-ref.as-for-separate-index (param $x (ref null struct)) (param $y (ref null struct))
    ;; This test shows that local index $x and local index $y are tracked separately.
    (drop
      ;; The later local.set $x will prevent casts from being moved here.
      (local.get $x)
    )
    (drop
      ;; A ref.as_non_null will be moved here, because the local.set
      ;; will only prevent casts involving local.get $x from being moved.
      (local.get $y)
    )
    (local.set $x
      (local.get $y)
    )
    (drop
      (ref.as_non_null
        (local.get $y)
      )
    )
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-ref.as-and-ref.cast (type $3) (param $x structref)
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (ref.cast (ref null $A)
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-ref.as-and-ref.cast (param $x (ref null struct))
    ;; This test shows how a nested ref.as_non_null and ref.cast can be
    ;; moved to the same local.get.
    (drop
      (local.get $x)
    )
    (drop
      ;; Here these two nested casts will be moved up to the earlier local.get.
      (ref.as_non_null
        ;; This will be converted to a non-nullable cast because the local we
        ;; save to in the optimization ($1) is now non-nullable.
        (ref.cast (ref null $A)
          (local.get $x)
        )
      )
    )
  )

  ;; CHECK:      (func $move-ref.as-and-ref.cast-2 (type $3) (param $x structref)
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (ref.cast (ref null $A)
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-ref.as-and-ref.cast-2 (param $x (ref null struct))
    ;; This test shows how a ref.cast followed by a ref.as_non_null
    ;; can both be moved to an earlier local.get.
    (drop
      ;; The separate ref.as_non_null and the ref.cast below will both be moved here.
      (local.get $x)
    )
    (drop
      ;; This is converted to ref.cast $A, because we will save $x to
      ;; a non-nullable $A local as part of the optimization.
      (ref.cast (ref null $A)
        (local.get $x)
      )
    )
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $move-ref.as-and-ref.cast-3 (type $3) (param $x structref)
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (ref.cast (ref null $A)
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-ref.as-and-ref.cast-3 (param $x (ref null struct))
    ;; This test shows how a ref.as_non_null followed by a ref.cast can be
    ;; both moved to an earlier local.get.
    (drop
      ;; Even though the ref.as_non_null appears first, it will still
      ;; be the outer cast when both casts are moved here.
      (local.get $x)
    )
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
    (drop
      ;; This is converted to ref.cast $A, because we will save $x to
      ;; a non-nullable $A local as part of the optimization.
      (ref.cast (ref null $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $unoptimizable-nested-casts (type $3) (param $x structref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $unoptimizable-nested-casts (param $x (ref null struct))
    ;; No optimizations should be made here for this nested cast.
    ;; This test is here to ensure this.
    (drop
      (ref.cast (ref $B)
        (ref.as_non_null
          (local.get $x)
        )
      )
    )
  )

  ;; CHECK:      (func $no-move-over-self-tee (type $5) (param $x structref) (param $y structref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.tee $x
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-move-over-self-tee (param $x (ref null struct)) (param $y (ref null struct))
    (drop
      (local.get $x)
    )
    (drop
      ;; We do not move this ref.cast of $x because $x is set by the local.tee,
      ;; and we do not move casts past a set of a local index. This is treated
      ;; like a local.set and we do not have a special case for this.
      (ref.cast (ref $A)
        (local.tee $x
          (local.get $x)
        )
      )
    )
  )

  ;; CHECK:      (func $move-over-tee (type $5) (param $x structref) (param $y structref)
  ;; CHECK-NEXT:  (local $a structref)
  ;; CHECK-NEXT:  (local $3 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $3
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.tee $a
  ;; CHECK-NEXT:     (local.get $3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-over-tee (param $x (ref null struct)) (param $y (ref null struct))
    (local $a (ref null struct))
    (drop
      (local.get $x)
    )
    (drop
      ;; We can move this ref.cast because the local.tee sets another local index.
      (ref.cast (ref $A)
        (local.tee $a
          (local.get $x)
        )
      )
    )
  )

  ;; CHECK:      (func $move-identical-repeated-casts (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $1 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $1
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $move-identical-repeated-casts (param $x (ref struct))
    ;; This tests the case where there are two casts with equal type which can
    ;; be moved to an earlier local.get. Only one of the casts will be duplicated
    ;; to the earliest local.get (which one is not visible to the test).
    (drop
      (local.get $x)
    )
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $no-move-past-non-linear (type $3) (param $x structref)
  ;; CHECK-NEXT:  (local $1 (ref struct))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.tee $1
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $x)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $no-move-past-non-linear (param $x (ref null struct))
    (drop
      ;; No cast can be moved up here, since this is immediately
      ;; followed by the if statement, which resets the state of
      ;; the optimization pass and blocks subsequent casts from
      ;; being moved past it.
      (local.get $x)
    )
    (if
      (i32.const 0)
      (then
        (block
          (drop
            ;; The ref.as_non_null can be moved here because
            ;; it is in the same block in the same arm of the
            ;; if statement.
            (local.get $x)
          )
          (drop
            (ref.as_non_null
              (local.get $x)
            )
          )
        )
      )
    )
    (drop
      ;; This cannot be moved earlier because it is blocked by
      ;; the if statement. All state information is cleared when
      ;; entering and leaving the if statement.
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $local-tee (type $2) (param $x (ref struct))
  ;; CHECK-NEXT:  (local $y (ref struct))
  ;; CHECK-NEXT:  (local $2 (ref $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $2
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.tee $y
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $local-tee (param $x (ref struct))
    (local $y (ref struct))
    ;; We should use the cast value after it has been computed, in both gets.
    (drop
      (ref.cast (ref $A)
        (local.tee $y
          (local.get $x)
        )
      )
    )
    (drop
      (local.get $x)
    )
    (drop
      (local.get $y)
    )
  )

  ;; CHECK:      (func $get (type $11) (result (ref struct))
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $get (result (ref struct))
    ;; Helper for the above.
    (unreachable)
  )

  ;; CHECK:      (func $void (type $void)
  ;; CHECK-NEXT: )
  (func $void
    ;; Helper for the above.
  )
)