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

;; Part of cast-and-recast.wast, but containing tuples. This is split out
;; because we do not roundtrip tuple-containing code properly. We also use only
;; one roundtrip because of the accumulation of tuple logic, which would
;; otherwise make the output here very hard to read.

;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (sub (struct)))
    (type $A (sub (struct)))
    ;; CHECK:       (type $B (sub $A (struct)))
    (type $B (sub $A (struct)))
  )

  ;; CHECK:      (func $test-local-tuple-1 (type $2) (param $B (ref $B)) (param $x i32) (result anyref i32)
  ;; CHECK-NEXT:  (local $scratch (tuple (ref $B) i32))
  ;; CHECK-NEXT:  (local $scratch_3 (ref $B))
  ;; CHECK-NEXT:  (block $block (type $3) (result (ref $A) i32)
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (local.set $scratch_3
  ;; CHECK-NEXT:      (tuple.extract 2 0
  ;; CHECK-NEXT:       (local.tee $scratch
  ;; CHECK-NEXT:        (br_if $block
  ;; CHECK-NEXT:         (tuple.make 2
  ;; CHECK-NEXT:          (local.get $B)
  ;; CHECK-NEXT:          (i32.const 3)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.get $x)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (tuple.extract 2 1
  ;; CHECK-NEXT:       (local.get $scratch)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test-local-tuple-1 (param $B (ref $B)) (param $x i32) (result anyref i32)
    ;; A dropped tuple that contains a ref. As it is dropped, we do not need to
    ;; do anything for this br_if. However, due to our general handling of
    ;; tuples the code here will grow quite a bit due the roundtrip, but we can
    ;; at least verify that there is no ref.cast added anywhere here.
    (block $out (result (ref $A) i32)
      (tuple.drop 2
        (br_if $out
          (tuple.make 2
            (local.get $B)
            (i32.const 3)
          )
          (local.get $x)
        )
      )
      (unreachable)
    )
  )

  ;; CHECK:      (func $test-local-tuple-2 (type $4) (param $B (ref $B)) (param $x i32) (result i32 i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (local $3 i32)
  ;; CHECK-NEXT:  (local $scratch (tuple i32 i32))
  ;; CHECK-NEXT:  (local $scratch_5 i32)
  ;; CHECK-NEXT:  (block $block (type $6) (result i32 i32)
  ;; CHECK-NEXT:   (local.set $temp
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (local.set $scratch_5
  ;; CHECK-NEXT:      (tuple.extract 2 0
  ;; CHECK-NEXT:       (local.tee $scratch
  ;; CHECK-NEXT:        (br_if $block
  ;; CHECK-NEXT:         (tuple.make 2
  ;; CHECK-NEXT:          (i32.const -1)
  ;; CHECK-NEXT:          (i32.const 3)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.get $x)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.set $3
  ;; CHECK-NEXT:      (tuple.extract 2 1
  ;; CHECK-NEXT:       (local.get $scratch)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_5)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test-local-tuple-2 (param $B (ref $B)) (param $x i32) (result i32 i32)
    (local $temp (tuple i32 i32))
    ;; This tuple is not dropped, but it contains no references, so we do not
    ;; need to do anything for the br_if, and we add no casts.
    (block $out (result i32 i32)
      (local.set $temp
        (br_if $out
          (tuple.make 2
            (i32.const -1)
            (i32.const 3)
          )
          (local.get $x)
        )
      )
      (unreachable)
    )
  )

  ;; CHECK:      (func $test-local-tuple-3 (type $2) (param $B (ref $B)) (param $x i32) (result anyref i32)
  ;; CHECK-NEXT:  (local $temp (ref $B))
  ;; CHECK-NEXT:  (local $3 i32)
  ;; CHECK-NEXT:  (local $scratch (tuple (ref $B) i32))
  ;; CHECK-NEXT:  (local $scratch_5 (ref $B))
  ;; CHECK-NEXT:  (block $block (type $7) (result (ref $B) i32)
  ;; CHECK-NEXT:   (local.set $temp
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (local.set $scratch_5
  ;; CHECK-NEXT:      (tuple.extract 2 0
  ;; CHECK-NEXT:       (local.tee $scratch
  ;; CHECK-NEXT:        (br_if $block
  ;; CHECK-NEXT:         (tuple.make 2
  ;; CHECK-NEXT:          (local.get $B)
  ;; CHECK-NEXT:          (i32.const 3)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.get $x)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.set $3
  ;; CHECK-NEXT:      (tuple.extract 2 1
  ;; CHECK-NEXT:       (local.get $scratch)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_5)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test-local-tuple-3 (param $B (ref $B)) (param $x i32) (result anyref i32)
    (local $temp (tuple (ref $B) i32))
    ;; This is not dropped and has a reference, but it has the right type, so no
    ;; cast is needed.
    (block $out (result (ref $B) i32)
      (local.set $temp
        (br_if $out
          (tuple.make 2
            (local.get $B)
            (i32.const 3)
          )
          (local.get $x)
        )
      )
      (unreachable)
    )
  )

  ;; CHECK:      (func $test-local-tuple-4-bad (type $2) (param $B (ref $B)) (param $x i32) (result anyref i32)
  ;; CHECK-NEXT:  (local $temp (ref $B))
  ;; CHECK-NEXT:  (local $3 (ref $B))
  ;; CHECK-NEXT:  (local $4 i32)
  ;; CHECK-NEXT:  (local $5 i32)
  ;; CHECK-NEXT:  (local $6 i32)
  ;; CHECK-NEXT:  (local $scratch i32)
  ;; CHECK-NEXT:  (local $scratch_8 (ref $B))
  ;; CHECK-NEXT:  (local $scratch_9 (tuple (ref $B) i32))
  ;; CHECK-NEXT:  (local $scratch_10 (ref $B))
  ;; CHECK-NEXT:  (local $scratch_11 (ref $B))
  ;; CHECK-NEXT:  (block $block (type $3) (result (ref $A) i32)
  ;; CHECK-NEXT:   (local.set $3
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (local.set $scratch_8
  ;; CHECK-NEXT:      (local.get $B)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.set $6
  ;; CHECK-NEXT:      (block (result i32)
  ;; CHECK-NEXT:       (local.set $scratch
  ;; CHECK-NEXT:        (i32.const 3)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (local.set $5
  ;; CHECK-NEXT:        (local.get $x)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (local.get $scratch)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_8)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (local.set $scratch_10
  ;; CHECK-NEXT:      (tuple.extract 2 0
  ;; CHECK-NEXT:       (local.tee $scratch_9
  ;; CHECK-NEXT:        (br_if $block
  ;; CHECK-NEXT:         (tuple.make 2
  ;; CHECK-NEXT:          (local.get $3)
  ;; CHECK-NEXT:          (local.get $6)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.get $5)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (tuple.extract 2 1
  ;; CHECK-NEXT:       (local.get $scratch_9)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_10)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.set $temp
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (local.set $scratch_11
  ;; CHECK-NEXT:      (local.get $3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.set $4
  ;; CHECK-NEXT:      (local.get $6)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_11)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test-local-tuple-4-bad (param $B (ref $B)) (param $x i32) (result anyref i32)
    (local $temp (tuple (ref $B) i32))
    ;; As above, but none of the mitigating circumstances happens: we have a
    ;; tuple with a reference that is refined compared to the break target. As a
    ;; result we must fix this up, which we do by adding locals, saving the
    ;; br_if's refined input to them, reloading those values for the br_if, then
    ;; reloading them again afterwards.
    ;;
    ;; Comparing to $test-local-tuple-4, we end up with 6 more locals.
    (block $out (result (ref $A) i32)
      (local.set $temp
        (br_if $out
          (tuple.make 2
            (local.get $B)
            (i32.const 3)
          )
          (local.get $x)
        )
      )
      (unreachable)
    )
  )

  ;; CHECK:      (func $test-local-tuple-4-bad-dupes (type $5) (param $B (ref $B)) (param $x i32) (result i32 anyref i32)
  ;; CHECK-NEXT:  (local $temp (ref $B))
  ;; CHECK-NEXT:  (local $3 (ref $B))
  ;; CHECK-NEXT:  (local $4 (ref $B))
  ;; CHECK-NEXT:  (local $5 i32)
  ;; CHECK-NEXT:  (local $scratch i32)
  ;; CHECK-NEXT:  (local $7 i32)
  ;; CHECK-NEXT:  (local $8 i32)
  ;; CHECK-NEXT:  (local $9 i32)
  ;; CHECK-NEXT:  (local $10 i32)
  ;; CHECK-NEXT:  (local $scratch_11 i32)
  ;; CHECK-NEXT:  (local $scratch_12 (ref $B))
  ;; CHECK-NEXT:  (local $scratch_13 i32)
  ;; CHECK-NEXT:  (local $scratch_14 (tuple i32 (ref $B) i32))
  ;; CHECK-NEXT:  (local $scratch_15 (ref $B))
  ;; CHECK-NEXT:  (local $scratch_16 i32)
  ;; CHECK-NEXT:  (local $scratch_17 (ref $B))
  ;; CHECK-NEXT:  (local $scratch_18 i32)
  ;; CHECK-NEXT:  (local $scratch_19 (ref $B))
  ;; CHECK-NEXT:  (block $block (type $8) (result i32 (ref $A) i32)
  ;; CHECK-NEXT:   (local.set $10
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (local.set $scratch_13
  ;; CHECK-NEXT:      (i32.const -3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.set $4
  ;; CHECK-NEXT:      (block (result (ref $B))
  ;; CHECK-NEXT:       (local.set $scratch_12
  ;; CHECK-NEXT:        (local.get $B)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (local.set $9
  ;; CHECK-NEXT:        (block (result i32)
  ;; CHECK-NEXT:         (local.set $scratch_11
  ;; CHECK-NEXT:          (i32.const 3)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.set $8
  ;; CHECK-NEXT:          (local.get $x)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.get $scratch_11)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (local.get $scratch_12)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_13)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (local.set $scratch_16
  ;; CHECK-NEXT:      (tuple.extract 3 0
  ;; CHECK-NEXT:       (local.tee $scratch_14
  ;; CHECK-NEXT:        (br_if $block
  ;; CHECK-NEXT:         (tuple.make 3
  ;; CHECK-NEXT:          (local.get $10)
  ;; CHECK-NEXT:          (local.get $4)
  ;; CHECK-NEXT:          (local.get $9)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:         (local.get $8)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (block (result (ref $B))
  ;; CHECK-NEXT:       (local.set $scratch_15
  ;; CHECK-NEXT:        (tuple.extract 3 1
  ;; CHECK-NEXT:         (local.get $scratch_14)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (drop
  ;; CHECK-NEXT:        (tuple.extract 3 2
  ;; CHECK-NEXT:         (local.get $scratch_14)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (local.get $scratch_15)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_16)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (local.tee $scratch
  ;; CHECK-NEXT:     (block (result i32)
  ;; CHECK-NEXT:      (local.set $scratch_18
  ;; CHECK-NEXT:       (local.get $10)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $3
  ;; CHECK-NEXT:       (block (result (ref $B))
  ;; CHECK-NEXT:        (local.set $scratch_17
  ;; CHECK-NEXT:         (local.get $4)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (local.set $7
  ;; CHECK-NEXT:         (local.get $9)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (local.get $scratch_17)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.get $scratch_18)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.set $temp
  ;; CHECK-NEXT:    (block (result (ref $B))
  ;; CHECK-NEXT:     (local.set $scratch_19
  ;; CHECK-NEXT:      (local.get $3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.set $5
  ;; CHECK-NEXT:      (local.get $7)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (local.get $scratch_19)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test-local-tuple-4-bad-dupes (param $B (ref $B)) (param $x i32) (result i32 anyref i32)
    (local $temp (tuple (ref $B) i32))
    ;; As above, but now the tuple has multiple appearances of the same type in
    ;; it, each of which needs its own scratch local. We can see in the output
    ;; that the tuple.extracts use different locals for the first and last i32.
    ;; For easier reading, here is the wami output of the binary:
    ;;
    ;;   (func $func0 (param $var0 (ref $type1)) (param $var1 i32) (result i32) (result anyref) (result i32)
    ;;     (local $var2 (ref $type1))
    ;;     (local $var3 (ref $type1))
    ;;     (local $var4 (ref $type1))
    ;;     (local $var5 i32)
    ;;     (local $var6 i32)
    ;;     (local $var7 i32)
    ;;     (local $var8 i32)
    ;;     (local $var9 i32)
    ;;     (local $var10 i32)
    ;;     block $label0 (result i32) (result (ref $type0)) (result i32)
    ;;       i32.const -3
    ;;       local.get $var0
    ;;       i32.const 3
    ;;       local.get $var1
    ;;       local.set $var8  ;; saves condition
    ;;       local.set $var9  ;; saves 3
    ;;       local.set $var4  ;; saves ref
    ;;       local.set $var10 ;; saves -3
    ;;       local.get $var10 ;; gets -3
    ;;       local.get $var4  ;; gets ref
    ;;       local.get $var9  ;; gets 3
    ;;       local.get $var8  ;; gets condition
    ;;       br_if $label0
    ;;       drop
    ;;       drop
    ;;       drop
    ;;       local.get $var10 ;; gets -3
    ;;       local.get $var4  ;; gets ref
    ;;       local.get $var9  ;; gets 3
    ;;       local.set $var7
    ;;       local.set $var3
    ;;       local.tee $var6
    ;;       drop
    ;;       local.get $var3
    ;;       local.get $var7
    ;;       local.set $var5
    ;;       local.set $var2
    ;;       unreachable
    ;;     end $label0
    ;;   )
    ;;
    (block $out (result i32 (ref $A) i32)
      (local.set $temp
        (br_if $out
          (tuple.make 3
            (i32.const -3) ;; this was added
            (local.get $B)
            (i32.const 3)
          )
          (local.get $x)
        )
      )
      (unreachable)
    )
  )
)
