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

;; RUN: wasm-opt %s --remove-unused-names --precompute-propagate --fuzz-exec -all -S -o - \
;; RUN:   | filecheck %s

(module
 ;; CHECK:      (global $g (mut i32) (i32.const 10))
 (global $g (mut i32) (i32.const 10))

 ;; CHECK:      (func $loop (type $1)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (local.set $temp
 ;; CHECK-NEXT:   (i32.const 10)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $loop
  (local $temp i32)
  ;; We should not try to precompute this loop. If we attempted to replace it
  ;; with its children, we'd need to handle the effects of chidren properly,
  ;; which we do not do in this pass.
  (loop
   (local.set $temp
    (i32.const 10)
   )
  )
 )

 ;; CHECK:      (func $local.set (type $1)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (local.set $temp
 ;; CHECK-NEXT:   (i32.const 10)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.tee $temp
 ;; CHECK-NEXT:    (i32.const 20)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $local.set
  (local $temp i32)
  ;; We should not try to precompute a set or tee.
  (local.set $temp
   (i32.const 10)
  )
  (drop
   (local.tee $temp
    (i32.const 20)
   )
  )
 )

 ;; CHECK:      (func $global.set (type $1)
 ;; CHECK-NEXT:  (global.set $g
 ;; CHECK-NEXT:   (i32.const 20)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $global.set
  ;; We should not try to precompute a global set.
  (global.set $g
   (i32.const 20)
  )
 )

 ;; CHECK:      (func $binary-tee (type $0) (result i32)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.tee $temp
 ;; CHECK-NEXT:    (i32.const 10)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (i32.const 20)
 ;; CHECK-NEXT: )
 (func $binary-tee (result i32)
  (local $temp i32)
  ;; We can precompute this and remove the add, but must keep the tee.
  (i32.add
   (local.tee $temp
    (i32.const 10)
   )
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $binary-tee-2 (type $0) (result i32)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.tee $temp
 ;; CHECK-NEXT:    (i32.const 10)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (i32.const 10)
 ;; CHECK-NEXT: )
 (func $binary-tee-2 (result i32)
  (local $temp i32)
  ;; A tee on the other side.
  (i32.add
   (local.get $temp)
   (local.tee $temp
    (i32.const 10)
   )
  )
 )

 ;; CHECK:      (func $binary-both (type $0) (result i32)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.tee $temp
 ;; CHECK-NEXT:    (i32.const 10)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.tee $temp
 ;; CHECK-NEXT:    (i32.const 20)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (i32.const 30)
 ;; CHECK-NEXT: )
 (func $binary-both (result i32)
  (local $temp i32)
  ;; Now we must keep both tees.
  (i32.add
   (local.tee $temp
    (i32.const 10)
   )
   (local.tee $temp
    (i32.const 20)
   )
  )
 )

 ;; CHECK:      (func $nested-global (type $0) (result i32)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block (result i32)
 ;; CHECK-NEXT:    (local.set $temp
 ;; CHECK-NEXT:     (i32.const 10)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (i32.const 20)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block (result i32)
 ;; CHECK-NEXT:    (global.set $g
 ;; CHECK-NEXT:     (i32.const 30)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (i32.const 40)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (i32.const 60)
 ;; CHECK-NEXT: )
 (func $nested-global (result i32)
  (local $temp i32)
  ;; Nested effects inside arms, and one is a global effect.
  (i32.add
   (block (result i32)
    (local.set $temp
     (i32.const 10)
    )
    (i32.const 20)
   )
   (block (result i32)
    (global.set $g
     (i32.const 30)
    )
    (i32.const 40)
   )
  )
 )

 ;; CHECK:      (func $if (type $0) (result i32)
 ;; CHECK-NEXT:  (i32.const 2)
 ;; CHECK-NEXT: )
 (func $if (result i32)
  ;; We precompute simple ifs.
  (if (result i32)
   (i32.const 1)
   (then
    (i32.const 2)
   )
   (else
    (i32.const 3)
   )
  )
 )

 ;; CHECK:      (func $if-no (type $0) (result i32)
 ;; CHECK-NEXT:  (if (result i32)
 ;; CHECK-NEXT:   (i32.const 1)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (global.set $g
 ;; CHECK-NEXT:     (i32.const 20)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (i32.const 2)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (i32.const 3)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $if-no (result i32)
  ;; We do not precompute ifs with effects.
  (if (result i32)
   (i32.const 1)
   (then
    (block (result i32)
     (global.set $g
      (i32.const 20)
     )
     (i32.const 2)
    )
   )
   (else
    (i32.const 3)
   )
  )
 )

 ;; CHECK:      (func $try (type $0) (result i32)
 ;; CHECK-NEXT:  (try (result i32)
 ;; CHECK-NEXT:   (do
 ;; CHECK-NEXT:    (i32.const 1)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (catch_all
 ;; CHECK-NEXT:    (i32.const 2)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $try (result i32)
  ;; We don't precompute trys.
  (try (result i32)
   (do
    (i32.const 1)
   )
   (catch_all
    (i32.const 2)
   )
  )
 )

 ;; CHECK:      (func $ordering (type $0) (result i32)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  (block $out (result i32)
 ;; CHECK-NEXT:   (select
 ;; CHECK-NEXT:    (block (result i32)
 ;; CHECK-NEXT:     (local.set $temp
 ;; CHECK-NEXT:      (i32.const 0)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:     (i32.const 20)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (br $out
 ;; CHECK-NEXT:     (i32.const 10)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (block (result i32)
 ;; CHECK-NEXT:     (global.set $g
 ;; CHECK-NEXT:      (i32.const 30)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:     (i32.const 40)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $ordering (result i32)
  (local $temp i32)
  ;; Nested effects inside arms. The br in the middle arm will execute, so we
  ;; want to precompute the entire select into a br, but we must keep alive the
  ;; children before and after. While doing so, we must not *reorder* the middle
  ;; child against them: if we just remove the middle child (and add a br at the
  ;; end) then we are changing the order of execution, as the global.set would
  ;; happen, when before it did not. For simplicity, we do not optimize here.
  (block $out (result i32)
   (select
    (block (result i32)
     (local.set $temp
      (i32.const 0)
     )
     (i32.const 20)
    )
    (block (result i32)
     (br $out
      (i32.const 10)
     )
     (i32.const 20)
    )
    (block (result i32)
     (global.set $g
      (i32.const 30)
     )
     (i32.const 40)
    )
   )
  )
 )
)

