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

;; RUN: foreach %s %t wasm-opt --string-gathering -all -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-opt --string-lowering  -all -S -o - | filecheck %s --check-prefix=LOWER

;; All the strings should be collected into globals and used from there. They
;; should also be sorted deterministically (alphabetically).
;;
;; LOWER also lowers away strings entirely, leaving only imports and a custom
;; section (that part is tested in string-lowering.wast). It also removes all
;; uses of the string heap type, leaving extern instead for the imported
;; strings.

(module
  ;; Note that $global will be reused: no new global will be added for "foo".
  ;; $global2 almost can, but it has the wrong type, so it won't.

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

  ;; CHECK:      (global $global (ref string) (string.const "foo"))
  (global $global (ref string) (string.const "foo"))

  ;; CHECK:      (global $"string.const_\"bar\"" (ref string) (string.const "bar"))

  ;; CHECK:      (global $"string.const_\"other\"" (ref string) (string.const "other"))

  ;; CHECK:      (global $global2 stringref (global.get $"string.const_\"bar\""))
  ;; LOWER:      (type $0 (array (mut i16)))

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

  ;; LOWER:      (type $2 (func (param externref externref) (result i32)))

  ;; LOWER:      (type $3 (func (param externref) (result i32)))

  ;; LOWER:      (type $4 (func (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (type $5 (func (param i32) (result (ref extern))))

  ;; LOWER:      (type $6 (func (param externref externref) (result (ref extern))))

  ;; LOWER:      (type $7 (func (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (type $8 (func (param externref i32) (result i32)))

  ;; LOWER:      (type $9 (func (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (import "string.const" "0" (global $global (ref extern)))

  ;; LOWER:      (import "string.const" "1" (global $"string.const_\"bar\"" (ref extern)))

  ;; LOWER:      (import "string.const" "2" (global $"string.const_\"other\"" (ref extern)))

  ;; LOWER:      (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (global $global2 externref (global.get $"string.const_\"bar\""))
  (global $global2 (ref null string) (string.const "bar"))

  ;; CHECK:      (func $a (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $"string.const_\"bar\"")
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $global)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; LOWER:      (func $a (type $1)
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $"string.const_\"bar\"")
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $global)
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT: )
  (func $a
    (drop
      (string.const "bar")
    )
    (drop
      (string.const "foo")
    )
  )

  ;; CHECK:      (func $b (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $"string.const_\"bar\"")
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $"string.const_\"other\"")
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $global)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $global2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; LOWER:      (func $b (type $1)
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $"string.const_\"bar\"")
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $"string.const_\"other\"")
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $global)
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $global2)
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT: )
  (func $b
    (drop
      (string.const "bar")
    )
    (drop
      (string.const "other")
    )
    ;; Existing global.gets are not modified (but after this pass,
    ;; SimplifyGlobals could help; though in practice the globals would have
    ;; been propagated here anyhow).
    (drop
      (global.get $global)
    )
    (drop
      (global.get $global2)
    )
  )
)

;; Multiple possible reusable globals. Also test ignoring of imports.
(module
  ;; CHECK:      (import "a" "b" (global $import (ref string)))
  ;; LOWER:      (type $0 (array (mut i16)))

  ;; LOWER:      (type $1 (func (param externref externref) (result i32)))

  ;; LOWER:      (type $2 (func (param externref) (result i32)))

  ;; LOWER:      (type $3 (func (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (type $4 (func (param i32) (result (ref extern))))

  ;; LOWER:      (type $5 (func (param externref externref) (result (ref extern))))

  ;; LOWER:      (type $6 (func (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (type $7 (func (param externref i32) (result i32)))

  ;; LOWER:      (type $8 (func (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (import "string.const" "0" (global $global1 (ref extern)))

  ;; LOWER:      (import "string.const" "1" (global $global4 (ref extern)))

  ;; LOWER:      (import "a" "b" (global $import (ref extern)))
  (import "a" "b" (global $import (ref string)))

  ;; CHECK:      (global $global1 (ref string) (string.const "foo"))
  (global $global1 (ref string) (string.const "foo"))

  ;; CHECK:      (global $global4 (ref string) (string.const "bar"))

  ;; CHECK:      (global $global2 (ref string) (global.get $global1))
  ;; LOWER:      (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $3) (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $4) (param i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "concat" (func $concat (type $5) (param externref externref) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $6) (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $7) (param externref i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "substring" (func $substring (type $8) (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (global $global2 (ref extern) (global.get $global1))
  (global $global2 (ref string) (string.const "foo"))

  ;; CHECK:      (global $global3 (ref string) (global.get $global1))
  ;; LOWER:      (global $global3 (ref extern) (global.get $global1))
  (global $global3 (ref string) (string.const "foo"))

  (global $global4 (ref string) (string.const "bar"))

  ;; CHECK:      (global $global5 (ref string) (global.get $global4))
  ;; LOWER:      (global $global5 (ref extern) (global.get $global4))
  (global $global5 (ref string) (string.const "bar"))

  ;; CHECK:      (global $global6 (ref string) (global.get $global4))
  ;; LOWER:      (global $global6 (ref extern) (global.get $global4))
  (global $global6 (ref string) (string.const "bar"))
)

;; Mutable globals cannot be reused: we will create a new global for "foo".
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (global $"string.const_\"foo\"" (ref string) (string.const "foo"))

  ;; CHECK:      (global $global (mut (ref string)) (global.get $"string.const_\"foo\""))
  ;; LOWER:      (type $0 (array (mut i16)))

  ;; LOWER:      (type $1 (func (param externref externref) (result i32)))

  ;; LOWER:      (type $2 (func (param externref) (result i32)))

  ;; LOWER:      (type $3 (func))

  ;; LOWER:      (type $4 (func (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (type $5 (func (param i32) (result (ref extern))))

  ;; LOWER:      (type $6 (func (param externref externref) (result (ref extern))))

  ;; LOWER:      (type $7 (func (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (type $8 (func (param externref i32) (result i32)))

  ;; LOWER:      (type $9 (func (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (import "string.const" "0" (global $"string.const_\"foo\"" (ref extern)))

  ;; LOWER:      (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (global $global (mut (ref extern)) (global.get $"string.const_\"foo\""))
  (global $global (mut (ref string)) (string.const "foo"))

  ;; CHECK:      (func $a (type $0)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (global.get $"string.const_\"foo\"")
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  ;; LOWER:      (func $a (type $3)
  ;; LOWER-NEXT:  (drop
  ;; LOWER-NEXT:   (global.get $"string.const_\"foo\"")
  ;; LOWER-NEXT:  )
  ;; LOWER-NEXT: )
  (func $a
    (drop
      (string.const "foo")
    )
  )
)

;; A module where a string (in this case the empty string) appears twice, so we
;; will use a single global for both. The first use of the string appears in a
;; nested position, inside a struct constructor, so we cannot use that one as
;; our defining global, but there is an appropriate global after it. We must be
;; careful to then sort the globals, as $string must then appear before $struct.
(module
  ;; CHECK:      (type $struct (struct (field stringref)))
  ;; LOWER:      (type $0 (array (mut i16)))

  ;; LOWER:      (type $struct (struct (field externref)))
  (type $struct (struct (field stringref)))

  ;; CHECK:      (global $string (ref string) (string.const ""))

  ;; CHECK:      (global $struct (ref $struct) (struct.new $struct
  ;; CHECK-NEXT:  (global.get $string)
  ;; CHECK-NEXT: ))
  ;; LOWER:      (type $2 (func (param externref externref) (result i32)))

  ;; LOWER:      (type $3 (func (param externref) (result i32)))

  ;; LOWER:      (type $4 (func (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (type $5 (func (param i32) (result (ref extern))))

  ;; LOWER:      (type $6 (func (param externref externref) (result (ref extern))))

  ;; LOWER:      (type $7 (func (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (type $8 (func (param externref i32) (result i32)))

  ;; LOWER:      (type $9 (func (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (import "string.const" "0" (global $string (ref extern)))

  ;; LOWER:      (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern))))

  ;; LOWER:      (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32)))

  ;; LOWER:      (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern))))

  ;; LOWER:      (global $struct (ref $struct) (struct.new $struct
  ;; LOWER-NEXT:  (global.get $string)
  ;; LOWER-NEXT: ))
  (global $struct (ref $struct) (struct.new $struct
   (string.const "")
  ))

  (global $string (ref string) (string.const ""))
)

