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

(module
  ;; CHECK:      (tag $e (param i32))

  ;; CHECK:      (func $dummy (type $0)
  ;; CHECK-NEXT: )
  (func $dummy)
  (tag $e (param i32))

  ;; The following are the unit tests for Properties::getFallthrough for EH
  ;; instructions, which are used in one of binary optimizations in
  ;; OptimizeInstructions::visitBinary().

  ;; When a pattern of (i32.add (expr) (expr with all 1s)) is detected and
  ;; 'expr' is guaranteed to take equal or less bits than the number of bits in
  ;; the second expression, the i32.add can be dropped and we can only leave
  ;; (expr). For example:
  ;; (i32.add (local.get $x) (i32.const 7)) can be just (local.get $x) when $x
  ;; is guaranteed to contain a value equal to or less than 7.


  ;; CHECK:      (func $getFallthrough-try-no-throw (type $0)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (try (result i32)
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch_all
  ;; CHECK-NEXT:     (i32.const 3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $getFallthrough-try-no-throw
    (local $x i32)
    (local.set $x
      (try (result i32)
        (do
          (i32.const 1)
        )
        (catch_all
          (i32.const 3)
        )
      )
    )
    ;; The 'try' above is guaranteed not to throw, so we can be sure the $x
    ;; contains 1 at this point, which is smaller than 7 (0b111), so we know the
    ;; masking with 0b111 is not ncessary and we can only leave (local.set $x).
    (drop (i32.and (local.get $x) (i32.const 7)))
  )

  ;; CHECK:      (func $getFallthrough-try-may-throw (type $0)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (try (result i32)
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (call $dummy)
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch_all
  ;; CHECK-NEXT:     (i32.const 3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.and
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (i32.const 7)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $getFallthrough-try-may-throw
    (local $x i32)
    (local.set $x
      (try (result i32)
        (do
          (call $dummy)
          (i32.const 1)
        )
        (catch_all
          (i32.const 3)
        )
      )
    )
    ;; The 'try' body above may throw because of the call, so we are not sure
    ;; what $x contains at this point, so we can't remove the masking here.
    (drop (i32.and (local.get $x) (i32.const 7)))
  )

  ;; CHECK:      (func $getFallthrough-nested-try-0 (type $0)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (try (result i32)
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (try
  ;; CHECK-NEXT:      (do
  ;; CHECK-NEXT:       (call $dummy)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (catch $e
  ;; CHECK-NEXT:       (drop
  ;; CHECK-NEXT:        (pop i32)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch $e
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (pop i32)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.and
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (i32.const 7)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $getFallthrough-nested-try-0
    (local $x i32)
    (local.set $x
      (try (result i32)
        (do
          (try
            (do
              (call $dummy)
            )
            (catch $e
              (drop (pop i32))
            )
          )
          (i32.const 1)
        )
        (catch $e
          (drop (pop i32))
          (i32.const 3)
        )
      )
    )
    ;; The inner 'try' may throw and it may not be caught by both the inner and
    ;; outer catches, so we are not sure what $x contains at this point, which
    ;; prevents the masking optimization.
    (drop (i32.and (local.get $x) (i32.const 7)))
  )

  ;; CHECK:      (func $getFallthrough-nested-try-1 (type $0)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (try (result i32)
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (try
  ;; CHECK-NEXT:      (do
  ;; CHECK-NEXT:       (call $dummy)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (catch_all
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch_all
  ;; CHECK-NEXT:     (i32.const 3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $getFallthrough-nested-try-1
    (local $x i32)
    (local.set $x
      (try (result i32)
        (do
          (try
            (do
              (call $dummy)
            )
            (catch_all)
          )
          (i32.const 1)
        )
        (catch_all
          (i32.const 3)
        )
      )
    )
    ;; The inner try may throw, but it will caught by the inner catch_all, and
    ;; $x will be set to 1. So we can do the masking optimization and remove
    ;; i32.and here.
    (drop (i32.and (local.get $x) (i32.const 7)))
  )

  ;; CHECK:      (func $getFallthrough-nested-try-2 (type $0)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (try (result i32)
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (try
  ;; CHECK-NEXT:      (do
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (catch_all
  ;; CHECK-NEXT:       (call $dummy)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch_all
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.and
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:    (i32.const 7)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $getFallthrough-nested-try-2
    (local $x i32)
    (local.set $x
      (try (result i32)
        (do
          (try
            (do)
            (catch_all
              (call $dummy)
            )
          )
          (i32.const 3)
        )
        (catch_all
          (i32.const 1)
        )
      )
    )
    ;; Depending on whether (call $dummy) throws or not, we are not sure whether
    ;; $x will contain 1 or 3 at this point, which prevents the masking
    ;; optimization.
    (drop (i32.and (local.get $x) (i32.const 7)))
  )
)