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

;; RUN: foreach %s %t wasm-opt --optimize-j2cl -all -S -o - | filecheck %s

;; Simple primitives are hoisted.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $field-i32@Foo i32 (i32.const 1))
  (global $field-i32@Foo (mut i32) (i32.const 0))
  ;; CHECK:      (global $field-f64@Foo f64 (f64.const 1))
  (global $field-f64@Foo (mut f64) (f64.const 0))

  ;; CHECK:      (func $clinit_<once>_@Foo (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $clinit_<once>_@Foo
    (global.set $field-i32@Foo (i32.const 1))
    (global.set $field-f64@Foo (f64.const 1))
  )
)

;; Fields with more complex constant initialization are hoisted.
(module

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

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

  ;; CHECK:      (global $referredField@Foo i32 (i32.const 42))
  (global $referredField@Foo i32 (i32.const 42))

  ;; CHECK:      (global $referredFieldMut@Foo (mut i32) (i32.const 42))
  (global $referredFieldMut@Foo (mut i32) (i32.const 42))

  ;; CHECK:      (global $field1@Foo anyref (struct.new $A
  ;; CHECK-NEXT:  (global.get $referredField@Foo)
  ;; CHECK-NEXT: ))
  (global $field1@Foo (mut anyref) (ref.null none))

  ;; CHECK:      (global $field2@Foo (mut anyref) (ref.null none))
  (global $field2@Foo (mut anyref) (ref.null none))

  ;; CHECK:      (global $field3@Foo anyref (global.get $field1@Foo))
  (global $field3@Foo (mut anyref) (ref.null none))

  ;; CHECK:      (func $clinit_<once>_@Foo (type $1)
  ;; CHECK-NEXT:  (global.set $field2@Foo
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (global.get $referredFieldMut@Foo)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $clinit_<once>_@Foo
    ;; Referred field is immutable, should hoist
    (global.set $field1@Foo (struct.new $A (
      global.get $referredField@Foo)
    ))

    ;; Referred field is mutable, should not hoist
    (global.set $field2@Foo (struct.new $A
      (global.get $referredFieldMut@Foo)
    ))

    ;; Referred field is mutable but hoistable hence also this one.
    ;; (Note that requires multiple iterations in a single run)
    (global.set $field3@Foo (global.get $field1@Foo))
  )
)

;; Fields initialized to a non-default value shouldn't be hoisted.
(module
  ;; CHECK:      (type $A (struct))
  (type $A (struct))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (global $field-i32@Foo (mut i32) (i32.const 2))
  (global $field-i32@Foo (mut i32) (i32.const 2))

  ;; CHECK:      (global $field-any@Foo (mut anyref) (struct.new_default $A))
  (global $field-any@Foo (mut anyref) (struct.new $A))

  ;; CHECK:      (func $clinit_<once>_@Foo (type $1)
  ;; CHECK-NEXT:  (global.set $field-i32@Foo
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $field-any@Foo
  ;; CHECK-NEXT:   (struct.new_default $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $clinit_<once>_@Foo
    (global.set $field-i32@Foo (i32.const 1))
    (global.set $field-any@Foo (struct.new $A))
  )
)

;; Non-block body is optimized
(module

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

  ;; CHECK:      (global $field@Foo i32 (i32.const 1))
  (global $field@Foo (mut i32) (i32.const 0))

  ;; CHECK:      (func $clinit_<once>_@Foo (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $clinit_<once>_@Foo
    (global.set $field@Foo (i32.const 1))
  )
)

;; $$class-initialized are not hoisted
(module

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

  ;; CHECK:      (global $$class-initialized@Foo (mut i32) (i32.const 0))
  (global $$class-initialized@Foo (mut i32) (i32.const 0))

  ;; CHECK:      (func $clinit_<once>_@Foo (type $0)
  ;; CHECK-NEXT:  (global.set $$class-initialized@Foo
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $clinit_<once>_@Foo
    (global.set $$class-initialized@Foo (i32.const 1))
  )
)

;; Fields from different classes are not hoisted.
(module

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

  ;; CHECK:      (global $field@Foo (mut i32) (i32.const 0))
  (global $field@Foo (mut i32) (i32.const 0))

  ;; CHECK:      (func $clinit_<once>_@Bar (type $0)
  ;; CHECK-NEXT:  (global.set $field@Foo
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $clinit_<once>_@Bar
    ;; Note that $clinit is @Bar and field is @Foo.
    (global.set $field@Foo (i32.const 1))
  )
)

;; Getters are transitively inlined and their constants hoisted.
(module
  ;; CHECK:      (type $0 (func (result i32)))

  ;; CHECK:      (global $$var1@Zoo i32 (i32.const 2))
  (global $$var1@Zoo (mut i32) (i32.const 0))
  ;; CHECK:      (global $$var2@Zoo i32 (i32.const 2))
  (global $$var2@Zoo (mut i32) (i32.const 0))
  ;; CHECK:      (global $$var3@Zoo i32 (i32.const 2))
  (global $$var3@Zoo (mut i32) (i32.const 0))

  ;; CHECK:      (func $getVar1_<once>_@Zoo (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT: )
  (func $getVar1_<once>_@Zoo (result i32)
    (if (global.get $$var1@Zoo)
      (then
        (return (global.get $$var1@Zoo))
      )
    )
    (global.set $$var1@Zoo (i32.const 2))
    (return (global.get $$var1@Zoo))
  )

  ;; CHECK:      (func $getVar2_<once>_@Zoo (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT: )
  (func $getVar2_<once>_@Zoo (result i32)
    (if (global.get $$var2@Zoo)
      (then
        (return (global.get $$var2@Zoo))
      )
    )
    (global.set $$var2@Zoo (call $getVar1_<once>_@Zoo))
    (return (global.get $$var2@Zoo))
  )

  ;; CHECK:      (func $getVar3_<once>_@Zoo (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT: )
  (func $getVar3_<once>_@Zoo (result i32)
    (if (global.get $$var3@Zoo)
      (then
        (return (global.get $$var3@Zoo))
      )
    )
    (global.set $$var3@Zoo (call $getVar2_<once>_@Zoo))
    (return (global.get $$var3@Zoo))
  )
)

;; Simple once functions are inlined
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (type $1 (func (result i32)))

  ;; CHECK:      (global $$var1@Zoo (mut i32) (i32.const 2))
  (global $$var1@Zoo (mut i32) (i32.const 2))
  ;; CHECK:      (global $$var2@Zoo (mut i32) (i32.const 3))
  (global $$var2@Zoo (mut i32) (i32.const 3))


  ;; CHECK:      (func $notOnceFunction@Zoo (type $0)
  ;; CHECK-NEXT: )
  (func $notOnceFunction@Zoo
  )

  ;; CHECK:      (func $nop_<once>_@Zoo (type $0)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $nop_<once>_@Zoo
    (nop)
  )

  ;; CHECK:      (func $empty_<once>_@Zoo (type $0)
  ;; CHECK-NEXT: )
  (func $empty_<once>_@Zoo
  )

  ;; CHECK:      (func $simpleCall_<once>_@Zoo (type $0)
  ;; CHECK-NEXT:  (call $notOnceFunction@Zoo)
  ;; CHECK-NEXT: )
  (func $simpleCall_<once>_@Zoo
    (call $notOnceFunction@Zoo)
  )

  ;; CHECK:      (func $globalGet_<once>_@Zoo (type $1) (result i32)
  ;; CHECK-NEXT:  (global.get $$var1@Zoo)
  ;; CHECK-NEXT: )
  (func $globalGet_<once>_@Zoo (result i32)
    (global.get $$var1@Zoo)
  )

  ;; CHECK:      (func $globalSet_<once>_@Zoo (type $0)
  ;; CHECK-NEXT:  (global.set $$var2@Zoo
  ;; CHECK-NEXT:   (i32.const 3)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $globalSet_<once>_@Zoo
    (global.set $$var2@Zoo (i32.const 3))
  )

  ;; CHECK:      (func $caller_@Zoo (type $1) (result i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (block
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $notOnceFunction@Zoo)
  ;; CHECK-NEXT:  (global.set $$var2@Zoo
  ;; CHECK-NEXT:   (i32.const 3)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.get $$var1@Zoo)
  ;; CHECK-NEXT: )
  (func $caller_@Zoo (result i32)
    (call $nop_<once>_@Zoo)
    (call $empty_<once>_@Zoo)
    (call $simpleCall_<once>_@Zoo)
    (call $globalSet_<once>_@Zoo)
    (call $globalGet_<once>_@Zoo)
  )
)

;; Simple once functions that would be inlined if cleaned up.
(module
  ;; CHECK:      (type $0 (func (result i32)))

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

  ;; CHECK:      (global $$var1@Zoo (mut i32) (i32.const 2))
  (global $$var1@Zoo (mut i32) (i32.const 2))


  ;; CHECK:      (func $justReturn_<once>_@Zoo (type $1)
  ;; CHECK-NEXT:  (return)
  ;; CHECK-NEXT: )
  (func $justReturn_<once>_@Zoo
    (return)
  )

  ;; CHECK:      (func $returnGlobalGet_<once>_@Zoo (type $0) (result i32)
  ;; CHECK-NEXT:  (return
  ;; CHECK-NEXT:   (global.get $$var1@Zoo)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $returnGlobalGet_<once>_@Zoo (result i32)
    (return (global.get $$var1@Zoo))
  )

  ;; CHECK:      (func $caller_@Zoo (type $0) (result i32)
  ;; CHECK-NEXT:  (call $justReturn_<once>_@Zoo)
  ;; CHECK-NEXT:  (call $returnGlobalGet_<once>_@Zoo)
  ;; CHECK-NEXT: )
  (func $caller_@Zoo (result i32)
    (call $justReturn_<once>_@Zoo)
    (call $returnGlobalGet_<once>_@Zoo)
  )
)

;; Hoist constants for getters that have transitive dependencies.
(module
  ;; CHECK:      (type $A (struct (field i32)))
  (type $A (struct (field i32)))

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

  ;; CHECK:      (global $$class@X (ref null $A) (struct.new $A
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT: ))
  (global $$class@X (mut (ref null $A)) (ref.null $A))
  ;; CHECK:      (global $$class@Y (ref null $A) (global.get $$class@X))
  (global $$class@Y (mut (ref null $A)) (ref.null $A))

  ;; CHECK:      (func $f_<once>_@X (type $1) (result (ref null $A))
  ;; CHECK-NEXT:  (global.get $$class@X)
  ;; CHECK-NEXT: )
  (func $f_<once>_@X (result (ref null $A))
    (block (result (ref null $A))
      (if (i32.eqz (ref.is_null (global.get $$class@X)))
        (then
          (return (global.get $$class@X))
        )
      )
      (global.set $$class@X (struct.new $A (i32.const 2)))
      (global.get $$class@X)
    )
  )

  ;; CHECK:      (func $f_<once>_@Y (type $1) (result (ref null $A))
  ;; CHECK-NEXT:  (global.get $$class@Y)
  ;; CHECK-NEXT: )
  (func $f_<once>_@Y (result (ref null $A))
    (block (result (ref null $A))
      (if
        (i32.eqz (ref.is_null (global.get $$class@Y)))
        (then
          (return (global.get $$class@Y))
        )
      )
      (global.set $$class@Y (call $f_<once>_@X))
      (global.get $$class@Y)
    )
  )
)

;; Imported globals are ignored.
(module
  ;; CHECK:      (import "a" "b" (global $a.b (ref func)))
  (import "a" "b" (global $a.b (ref func)))
)

