current commit
261007.1 · [multibyte] Encode typeidx before offset (#9222)
;; Current Syntax: (type $i8_array)
;;
;; (func $test2 (export "test2")
;;   (i32.store (type $typeIdx)
;;     (global.get $arr_4)
;;     (i32.const 1)
;;     (i32.const 1337)
;;   )
;; )

;; Alternative Syntax 1: type=array
;;
;; (func $test2 (export "test2")
;;   (i32.store type=array
;;     (global.get $arr_4)
;;     (i32.const 1)
;;     (i32.const 1337)
;;   )
;; )

;; Alternative Syntax 2: array
;;
;; (func $test2 (export "test2")
;;   (i32.store array
;;     (global.get $arr_4)
;;     (i32.const 1)
;;     (i32.const 1337)
;;   )
;; )

;; Alternative Syntax 3: new opcodes
;;
;; (func $test2 (export "test2")
;;   (i32.array.store
;;     (global.get $arr_4)
;;     (i32.const 1)
;;     (i32.const 1337)
;;   )
;; )

(module
  (type $i8_array (array (mut i8)))

  (global $arr_4 (ref $i8_array)
    (array.new_default $i8_array (i32.const 4))
  )

  (global $arr_8 (ref $i8_array)
    (array.new_default $i8_array (i32.const 8))
  )

  (func $get_array_4_byte (export "get_array_4_byte") (param $idx i32) (result i32)
    (array.get_u $i8_array (global.get $arr_4) (local.get $idx))
  )

  (func $get_array_8_byte (export "get_array_8_byte") (param $idx i32) (result i32)
    (array.get_u $i8_array (global.get $arr_8) (local.get $idx))
  )

  (func $i32_set_i8 (export "i32_set_i8") (param $index i32) (param $value i32)
    (i32.store8 (type $i8_array)
      (global.get $arr_4)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $i32_set_i16 (export "i32_set_i16") (param $index i32) (param $value i32)
    (i32.store16 (type $i8_array)
      (global.get $arr_4)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $i32_set_i32 (export "i32_set_i32") (param $index i32) (param $value i32)
    (i32.store (type $i8_array)
      (global.get $arr_4)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $f32_set (export "f32_set") (param $index i32) (param $value f32)
    (f32.store (type $i8_array)
      (global.get $arr_4)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $i64_set_i8 (export "i64_set_i8") (param $index i32) (param $value i64)
    (i64.store8 (type $i8_array)
      (global.get $arr_8)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $i64_set_i16 (export "i64_set_i16") (param $index i32) (param $value i64)
    (i64.store16 (type $i8_array)
      (global.get $arr_8)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $i64_set_i32 (export "i64_set_i32") (param $index i32) (param $value i64)
    (i64.store32 (type $i8_array)
      (global.get $arr_8)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $i64_set_i64 (export "i64_set_i64") (param $index i32) (param $value i64)
    (i64.store (type $i8_array)
      (global.get $arr_8)
      (local.get $index)
      (local.get $value)
    )
  )

  (func $f64_set (export "f64_set") (param $index i32) (param $value f64)
    (f64.store (type $i8_array)
      (global.get $arr_8)
      (local.get $index)
      (local.get $value)
    )
  )

  ;; TODO: Do we even want to spec out this instruction since array.set is the
  ;; same thing? See
  ;; https://github.com/WebAssembly/multibyte-array-access/issues/2
  (func $i32_set_and_get_i8 (export "i32_set_and_get_i8") (param $value i32) (result i32)
    (i32.store8 (type $i8_array)
      (global.get $arr_4)
      (i32.const 0)
      (local.get $value)
    )
    (i32.load8_u (type $i8_array) (global.get $arr_4) (i32.const 0))
  )

  (func $i32_set_and_get_i16 (export "i32_set_and_get_i16") (param $value i32) (result i32)
    (i32.store16 (type $i8_array)
      (global.get $arr_4)
      (i32.const 0)
      (local.get $value)
    )
    (i32.load16_u (type $i8_array) (global.get $arr_4) (i32.const 0))
  )

  (func $i32_set_and_get_i32 (export "i32_set_and_get_i32") (param $value i32) (result i32)
    (i32.store (type $i8_array)
      (global.get $arr_4)
      (i32.const 0)
      (local.get $value)
    )
    (i32.load (type $i8_array) (global.get $arr_4) (i32.const 0))
  )

  (func $set_and_get_f32 (export "set_and_get_f32") (param $value f32) (result f32)
    (f32.store (type $i8_array)
      (global.get $arr_4)
      (i32.const 0)
      (local.get $value)
    )
    (i32.load (type $i8_array) (global.get $arr_4) (i32.const 0))
    (f32.reinterpret_i32)
  )

  (func $i64_set_and_get_i8 (export "i64_set_and_get_i8") (param $value i64) (result i64)
    (i64.store8 (type $i8_array)
      (global.get $arr_8)
      (i32.const 0)
      (local.get $value)
    )
    (i64.load8_u (type $i8_array) (global.get $arr_8) (i32.const 0))
  )

  (func $i64_set_and_get_i16 (export "i64_set_and_get_i16") (param $value i64) (result i64)
    (i64.store16 (type $i8_array)
      (global.get $arr_8)
      (i32.const 0)
      (local.get $value)
    )
    (i64.load16_u (type $i8_array) (global.get $arr_8) (i32.const 0))
  )

  (func $i64_set_and_get_i32 (export "i64_set_and_get_i32") (param $value i64) (result i64)
    (i64.store32 (type $i8_array)
      (global.get $arr_8)
      (i32.const 0)
      (local.get $value)
    )
    (i64.load32_u (type $i8_array) (global.get $arr_8) (i32.const 0))
  )

  (func $i64_set_and_get_i64 (export "i64_set_and_get_i64") (param $value i64) (result i64)
    (i64.store (type $i8_array)
      (global.get $arr_8)
      (i32.const 0)
      (local.get $value)
    )
    (i64.load (type $i8_array) (global.get $arr_8) (i32.const 0))
  )

  (func $set_and_get_f64 (export "set_and_get_f64") (param $value f64) (result f64)
    (f64.store (type $i8_array)
      (global.get $arr_8)
      (i32.const 0)
      (local.get $value)
    )
    (i64.load (type $i8_array) (global.get $arr_8) (i32.const 0))
    (f64.reinterpret_i64)
  )
  (func $load_i32_16_u (export "load_i32_16_u") (param $idx i32) (result i32)
    (i32.load16_u (type $i8_array) (global.get $arr_4) (local.get $idx))
  )

  (func $load_i32_16_s (export "load_i32_16_s") (param $idx i32) (result i32)
    (i32.load16_s (type $i8_array) (global.get $arr_4) (local.get $idx))
  )

  (func $load_i32 (export "load_i32") (param $idx i32) (result i32)
    (i32.load (type $i8_array) (global.get $arr_4) (local.get $idx))
  )
  (func $load_i64 (export "load_i64") (param $idx i32) (result i64)
    (i64.load (type $i8_array) (global.get $arr_8) (local.get $idx))
  )

  (func $load_null (export "load_null") (result i32)
    (local $null (ref null $i8_array))
    (i32.load (type $i8_array) (local.get $null) (i32.const 0))
  )
)

;;
;; 32 bit round trip tests
;;

(assert_return (invoke "i32_set_and_get_i8" (i32.const 0)) (i32.const 0))
(assert_return (invoke "i32_set_and_get_i8" (i32.const 0xFF)) (i32.const 0xFF))
;; ensure high bits are ignored
(assert_return (invoke "i32_set_and_get_i8" (i32.const 0xFFFFFF12)) (i32.const 0x12))

(assert_return (invoke "i32_set_and_get_i16" (i32.const 0)) (i32.const 0))
(assert_return (invoke "i32_set_and_get_i16" (i32.const 0xFFFF)) (i32.const 0xFFFF))
;; ensure high bits are ignored
(assert_return (invoke "i32_set_and_get_i16" (i32.const 0xFFFF1234)) (i32.const 0x1234))

(assert_return (invoke "i32_set_and_get_i32" (i32.const 0)) (i32.const 0))
(assert_return (invoke "i32_set_and_get_i32" (i32.const 1)) (i32.const 1))
(assert_return (invoke "i32_set_and_get_i32" (i32.const 256)) (i32.const 256))
(assert_return (invoke "i32_set_and_get_i32" (i32.const -1)) (i32.const -1))
(assert_return (invoke "i32_set_and_get_i32" (i32.const 2147483647)) (i32.const 2147483647))
(assert_return (invoke "i32_set_and_get_i32" (i32.const -2147483648)) (i32.const -2147483648))

(assert_return (invoke "set_and_get_f32" (f32.const 0)) (f32.const 0))
(assert_return (invoke "set_and_get_f32" (f32.const -1)) (f32.const -1))
(assert_return (invoke "set_and_get_f32" (f32.const 3.3)) (f32.const 3.3))
(assert_return (invoke "set_and_get_f32" (f32.const -2.000000238418579)) (f32.const -2.000000238418579))
(assert_return (invoke "set_and_get_f32" (f32.const nan)) (f32.const nan))
(assert_return (invoke "set_and_get_f32" (f32.const nan:0x123456)) (f32.const nan:0x123456))
(assert_return (invoke "set_and_get_f32" (f32.const -nan:0x654321)) (f32.const -nan:0x654321))

;;
;; 64 bit round trip tests
;;

(assert_return (invoke "i64_set_and_get_i8" (i64.const 0)) (i64.const 0))
(assert_return (invoke "i64_set_and_get_i8" (i64.const 0xFF)) (i64.const 0xFF))
;; ensure high bits are ignored
(assert_return (invoke "i64_set_and_get_i8" (i64.const 0xFFFFFFFFFFFFFF12)) (i64.const 0x12))

(assert_return (invoke "i64_set_and_get_i16" (i64.const 0)) (i64.const 0))
(assert_return (invoke "i64_set_and_get_i16" (i64.const 0xFFFF)) (i64.const 0xFFFF))
;; ensure high bits are ignored
(assert_return (invoke "i64_set_and_get_i16" (i64.const 0xFFFFFFFFFFFF1234)) (i64.const 0x1234))

(assert_return (invoke "i64_set_and_get_i32" (i64.const 0)) (i64.const 0))
(assert_return (invoke "i64_set_and_get_i32" (i64.const 0x7FFFFFFF)) (i64.const 0x7FFFFFFF))
;; unsigned extend
(assert_return (invoke "i64_set_and_get_i32" (i64.const 0xFFFFFFFF80000000)) (i64.const 0x0000000080000000))
;; ensure high bits are ignored
(assert_return (invoke "i64_set_and_get_i32" (i64.const 0xFFFFFFFF12345678)) (i64.const 0x12345678))

(assert_return (invoke "i64_set_and_get_i64" (i64.const 0)) (i64.const 0))
(assert_return (invoke "i64_set_and_get_i64" (i64.const 9223372036854775807)) (i64.const 9223372036854775807))
(assert_return (invoke "i64_set_and_get_i64" (i64.const -9223372036854775808)) (i64.const -9223372036854775808))

(assert_return (invoke "set_and_get_f64" (f64.const 0)) (f64.const 0))
(assert_return (invoke "set_and_get_f64" (f64.const -1)) (f64.const -1))
(assert_return (invoke "set_and_get_f64" (f64.const 3.3)) (f64.const 3.3))
(assert_return (invoke "set_and_get_f64" (f64.const -2.00000000000000044409)) (f64.const -2.00000000000000044409))
(assert_return (invoke "set_and_get_f64" (f64.const nan)) (f64.const nan))
(assert_return (invoke "set_and_get_f64" (f64.const nan:0x123456789abcd)) (f64.const nan:0x123456789abcd))
(assert_return (invoke "set_and_get_f64" (f64.const -nan:0xedcba98765432)) (f64.const -nan:0xedcba98765432))

;;
;; Byte-wise store and unaligned store tests (32 bit)
;;

(invoke "i32_set_i32" (i32.const 0) (i32.const 0x00000000)) ;; clear
(invoke "i32_set_i16" (i32.const 0) (i32.const 0x1234))
(assert_return (invoke "get_array_4_byte" (i32.const 0)) (i32.const 0x34))
(assert_return (invoke "get_array_4_byte" (i32.const 1)) (i32.const 0x12))
(assert_return (invoke "get_array_4_byte" (i32.const 2)) (i32.const 0x00))
(assert_return (invoke "get_array_4_byte" (i32.const 3)) (i32.const 0x00))

(invoke "i32_set_i32" (i32.const 0) (i32.const 0x12345678))
(assert_return (invoke "get_array_4_byte" (i32.const 0)) (i32.const 0x78))
(assert_return (invoke "get_array_4_byte" (i32.const 1)) (i32.const 0x56))
(assert_return (invoke "get_array_4_byte" (i32.const 2)) (i32.const 0x34))
(assert_return (invoke "get_array_4_byte" (i32.const 3)) (i32.const 0x12))

(invoke "i32_set_i16" (i32.const 1) (i32.const 0xABCD))
(assert_return (invoke "get_array_4_byte" (i32.const 0)) (i32.const 0x78))
(assert_return (invoke "get_array_4_byte" (i32.const 1)) (i32.const 0xCD))
(assert_return (invoke "get_array_4_byte" (i32.const 2)) (i32.const 0xAB))
(assert_return (invoke "get_array_4_byte" (i32.const 3)) (i32.const 0x12))

;;
;; Byte-wise store and unaligned store tests (64 bit)
;;

(invoke "i64_set_i64" (i32.const 0) (i64.const 0x123456789ABCDEF0))
(assert_return (invoke "get_array_8_byte" (i32.const 0)) (i32.const 0xF0))
(assert_return (invoke "get_array_8_byte" (i32.const 1)) (i32.const 0xDE))
(assert_return (invoke "get_array_8_byte" (i32.const 2)) (i32.const 0xBC))
(assert_return (invoke "get_array_8_byte" (i32.const 3)) (i32.const 0x9A))
(assert_return (invoke "get_array_8_byte" (i32.const 4)) (i32.const 0x78))
(assert_return (invoke "get_array_8_byte" (i32.const 5)) (i32.const 0x56))
(assert_return (invoke "get_array_8_byte" (i32.const 6)) (i32.const 0x34))
(assert_return (invoke "get_array_8_byte" (i32.const 7)) (i32.const 0x12))

(invoke "i64_set_i32" (i32.const 3) (i64.const 0x11223344))
(assert_return (invoke "get_array_8_byte" (i32.const 0)) (i32.const 0xF0))
(assert_return (invoke "get_array_8_byte" (i32.const 1)) (i32.const 0xDE))
(assert_return (invoke "get_array_8_byte" (i32.const 2)) (i32.const 0xBC))
(assert_return (invoke "get_array_8_byte" (i32.const 3)) (i32.const 0x44))
(assert_return (invoke "get_array_8_byte" (i32.const 4)) (i32.const 0x33))
(assert_return (invoke "get_array_8_byte" (i32.const 5)) (i32.const 0x22))
(assert_return (invoke "get_array_8_byte" (i32.const 6)) (i32.const 0x11))
(assert_return (invoke "get_array_8_byte" (i32.const 7)) (i32.const 0x12))

;;
;; Byte-wise load tests
;;

(invoke "i32_set_i8" (i32.const 0) (i32.const 0x12))
(invoke "i32_set_i8" (i32.const 1) (i32.const 0x34))
(invoke "i32_set_i8" (i32.const 2) (i32.const 0x56))
(invoke "i32_set_i8" (i32.const 3) (i32.const 0x78))

(assert_return (invoke "load_i32_16_u" (i32.const 0)) (i32.const 0x3412))
(assert_return (invoke "load_i32_16_s" (i32.const 0)) (i32.const 0x3412))

;; Test sign extension
(invoke "i32_set_i8" (i32.const 0) (i32.const 0xFF))
(invoke "i32_set_i8" (i32.const 1) (i32.const 0x7F))
(assert_return (invoke "load_i32_16_u" (i32.const 0)) (i32.const 0x7FFF))
(assert_return (invoke "load_i32_16_s" (i32.const 0)) (i32.const 0x7FFF))

(invoke "i32_set_i8" (i32.const 0) (i32.const 0xFF))
(invoke "i32_set_i8" (i32.const 1) (i32.const 0xFF))
(invoke "i32_set_i8" (i32.const 2) (i32.const 0x56))
(invoke "i32_set_i8" (i32.const 3) (i32.const 0x78))
(assert_return (invoke "load_i32_16_u" (i32.const 0)) (i32.const 0x0000FFFF))
(assert_return (invoke "load_i32_16_s" (i32.const 0)) (i32.const 0xFFFFFFFF))

(assert_return (invoke "load_i32" (i32.const 0)) (i32.const 0x7856FFFF))

;;
;; Bounds checks (32 bit with a 4-byte array)
;;

(invoke "i32_set_i32" (i32.const 0) (i32.const 0))

;; i32_set_i8: Writes 1 byte
;; Valid range: [0, 3]
(assert_trap (invoke "i32_set_i8" (i32.const -1) (i32.const 0)) "out of bounds")
(assert_return (invoke "i32_set_i8" (i32.const 0) (i32.const 0)))
(assert_return (invoke "i32_set_i8" (i32.const 1) (i32.const 0)))
(assert_return (invoke "i32_set_i8" (i32.const 2) (i32.const 0)))
(assert_return (invoke "i32_set_i8" (i32.const 3) (i32.const 0)))
(assert_trap (invoke "i32_set_i8" (i32.const 4) (i32.const 0xFFFF)) "out of bounds")

;; i32_set_i16: Writes 2 bytes
;; Valid range: offset + 2 <= 4 -> Max offset 2
(invoke "i32_set_i32" (i32.const 0) (i32.const 0))
(assert_trap (invoke "i32_set_i16" (i32.const -1) (i32.const 0)) "out of bounds")
(assert_return (invoke "i32_set_i16" (i32.const 0) (i32.const 0)))
(assert_return (invoke "i32_set_i16" (i32.const 1) (i32.const 0)))
(assert_return (invoke "i32_set_i16" (i32.const 2) (i32.const 0)))
(assert_trap (invoke "i32_set_i16" (i32.const 3) (i32.const 0xFFFF)) "out of bounds")
(assert_return (invoke "get_array_4_byte" (i32.const 3)) (i32.const 0))

;; i32_set_i32: Writes 4 bytes
;; Valid range: offset + 4 <= 4 -> Max offset 0
(invoke "i32_set_i32" (i32.const 0) (i32.const 0))
(assert_trap (invoke "i32_set_i32" (i32.const -1) (i32.const 0)) "out of bounds")
(assert_return (invoke "i32_set_i32" (i32.const 0) (i32.const 0)))
(assert_trap (invoke "i32_set_i32" (i32.const 1) (i32.const 0xFFFFFFFF)) "out of bounds")
(assert_return (invoke "get_array_4_byte" (i32.const 1)) (i32.const 0))

;; f32_set: Writes 4 bytes
;; Valid range: offset + 4 <= 4 -> Max offset 0
(invoke "i32_set_i32" (i32.const 0) (i32.const 0))
(assert_trap (invoke "f32_set" (i32.const -1) (f32.const 0)) "out of bounds")
(assert_return (invoke "f32_set" (i32.const 0) (f32.const 0)))
(assert_trap (invoke "f32_set" (i32.const 1) (f32.const 1.0)) "out of bounds")
(assert_return (invoke "get_array_4_byte" (i32.const 1)) (i32.const 0))

;;
;; Bounds checks (64 bit with an 8-byte array)
;;

(invoke "i64_set_i64" (i32.const 0) (i64.const 0))

;; i64_set_i8: Writes 1 byte
;; Valid range: [0, 7]
(assert_trap (invoke "i64_set_i8" (i32.const -1) (i64.const 0)) "out of bounds")
(assert_return (invoke "i64_set_i8" (i32.const 0) (i64.const 0)))
(assert_return (invoke "i64_set_i8" (i32.const 1) (i64.const 0)))
(assert_return (invoke "i64_set_i8" (i32.const 6) (i64.const 0)))
(assert_return (invoke "i64_set_i8" (i32.const 7) (i64.const 0)))
(assert_trap (invoke "i64_set_i8" (i32.const 8) (i64.const 0xFFFF)) "out of bounds")

;; i64_set_i16: Writes 2 bytes
;; Valid range: offset + 2 <= 8 -> Max offset 6
(invoke "i64_set_i64" (i32.const 0) (i64.const 0))
(assert_trap (invoke "i64_set_i16" (i32.const -1) (i64.const 0)) "out of bounds")
(assert_return (invoke "i64_set_i16" (i32.const 0) (i64.const 0)))
(assert_return (invoke "i64_set_i16" (i32.const 1) (i64.const 0)))
(assert_return (invoke "i64_set_i16" (i32.const 5) (i64.const 0)))
(assert_return (invoke "i64_set_i16" (i32.const 6) (i64.const 0)))
(assert_trap (invoke "i64_set_i16" (i32.const 7) (i64.const 0xFFFF)) "out of bounds")
(assert_return (invoke "get_array_8_byte" (i32.const 7)) (i32.const 0))

;; i64_set_i32: Writes 4 bytes
;; Valid range: offset + 4 <= 8 -> Max offset 4
(invoke "i64_set_i64" (i32.const 0) (i64.const 0))
(assert_trap (invoke "i64_set_i32" (i32.const -1) (i64.const 0)) "out of bounds")
(assert_return (invoke "i64_set_i32" (i32.const 0) (i64.const 0)))
(assert_return (invoke "i64_set_i32" (i32.const 1) (i64.const 0)))
(assert_return (invoke "i64_set_i32" (i32.const 3) (i64.const 0)))
(assert_return (invoke "i64_set_i32" (i32.const 4) (i64.const 0)))
(assert_trap (invoke "i64_set_i32" (i32.const 5) (i64.const 0xFFFFFFFF)) "out of bounds")
(assert_return (invoke "get_array_8_byte" (i32.const 5)) (i32.const 0))

;; i64_set_i64: Writes 8 bytes
;; Valid range: offset + 8 <= 8 -> Max offset 0
(invoke "i64_set_i64" (i32.const 0) (i64.const 0))
(assert_trap (invoke "i64_set_i64" (i32.const -1) (i64.const 0)) "out of bounds")
(assert_return (invoke "i64_set_i64" (i32.const 0) (i64.const 0)))
(assert_trap (invoke "i64_set_i64" (i32.const 1) (i64.const 0xFFFFFFFFFFFFFFFF)) "out of bounds")
(assert_return (invoke "get_array_8_byte" (i32.const 1)) (i32.const 0))

;; f64_set: Writes 8 bytes
;; Valid range: offset + 8 <= 8 -> Max offset 0
(invoke "i64_set_i64" (i32.const 0) (i64.const 0))
(assert_trap (invoke "f64_set" (i32.const -1) (f64.const 0)) "out of bounds")
(assert_return (invoke "f64_set" (i32.const 0) (f64.const 0)))
(assert_trap (invoke "f64_set" (i32.const 1) (f64.const 1.0)) "out of bounds")
(assert_return (invoke "get_array_8_byte" (i32.const 1)) (i32.const 0))


(assert_invalid
  (module
    (type $a (array i8))
    (func (export "i32_set_immutable") (param $a (ref $a))
      (i32.store (type $a) (local.get $a) (i32.const 0) (i32.const 1))
    )
  )
  "array is immutable"
)

(assert_invalid
  (module
    (type $a (array (mut anyref)))
    (func (export "i32_set_non_numeric") (param $a (ref $a))
      (i32.store (type $a) (local.get $a) (i32.const 0) (i32.const 1))
    )
  )
  "array element type must be numeric"
)

;; New OOB Load Tests
(assert_trap (invoke "load_i32_16_u" (i32.const 3)) "out of bounds")
(assert_trap (invoke "load_i32" (i32.const 1)) "out of bounds")
(assert_trap (invoke "load_i64" (i32.const 1)) "out of bounds")

;; Null reference for load
(assert_trap (invoke "load_null") "null array")

;; Unaligned reads
(invoke "i32_set_i8" (i32.const 0) (i32.const 0x12))
(invoke "i32_set_i8" (i32.const 1) (i32.const 0x34))
(invoke "i32_set_i8" (i32.const 2) (i32.const 0x56))
(invoke "i32_set_i8" (i32.const 3) (i32.const 0x78))

(assert_return (invoke "load_i32_16_u" (i32.const 1)) (i32.const 0x5634))
(assert_return (invoke "load_i32_16_u" (i32.const 2)) (i32.const 0x7856))

;; Null dereference

(module
  (type $t (array (mut i8)))
  ;; (func (export "array.get-null")
  ;;   (local (ref null $t)) (drop (array.get $t (local.get 0) (i32.const 0)))
  ;; )
  (func (export "i32.store_array_null")
    (local (ref null $t)) (i32.store (type $t) (local.get 0) (i32.const 0) (i32.const 0))
  )
)

;; (assert_trap (invoke "array.get-null") "null array")
(assert_trap (invoke "i32.store_array_null") "null array")

;; ============================================================================
;; Multibyte accesses on arrays of every numeric element type.
;;
;; The accesses operate on the array's payload bytes, so the element type of
;; the array is independent of the type and the size of the access. Each of the
;; arrays below has a 16-byte payload, so the same accesses and the same bounds
;; apply to all of them.
;; ============================================================================

(module
  (type $i8_array (array (mut i8)))
  (type $i16_array (array (mut i16)))
  (type $i32_array (array (mut i32)))
  (type $i64_array (array (mut i64)))
  (type $f32_array (array (mut f32)))
  (type $f64_array (array (mut f64)))
  (type $v128_array (array (mut v128)))
  (type $imm_i32_array (array i32))

  (global $i8 (ref $i8_array) (array.new_default $i8_array (i32.const 16)))
  (global $i16 (ref $i16_array) (array.new_default $i16_array (i32.const 8)))
  (global $i32 (ref $i32_array) (array.new_default $i32_array (i32.const 4)))
  (global $i64 (ref $i64_array) (array.new_default $i64_array (i32.const 2)))
  (global $f32 (ref $f32_array) (array.new_default $f32_array (i32.const 4)))
  (global $f64 (ref $f64_array) (array.new_default $f64_array (i32.const 2)))
  (global $v128 (ref $v128_array) (array.new_default $v128_array (i32.const 1)))
  (global $v128_2 (ref $v128_array) (array.new_default $v128_array (i32.const 2)))
  (global $imm_i32 (ref $imm_i32_array)
    (array.new_fixed $imm_i32_array 2 (i32.const 0x11223344) (i32.const 0x55667788))
  )
  (global $empty (ref $i32_array) (array.new_default $i32_array (i32.const 0)))

  (func (export "clear")
    (v128.store (type $i8_array) (global.get $i8) (i32.const 0) (v128.const i32x4 0 0 0 0))
    (v128.store (type $i16_array) (global.get $i16) (i32.const 0) (v128.const i32x4 0 0 0 0))
    (v128.store (type $i32_array) (global.get $i32) (i32.const 0) (v128.const i32x4 0 0 0 0))
    (v128.store (type $i64_array) (global.get $i64) (i32.const 0) (v128.const i32x4 0 0 0 0))
    (v128.store (type $f32_array) (global.get $f32) (i32.const 0) (v128.const i32x4 0 0 0 0))
    (v128.store (type $f64_array) (global.get $f64) (i32.const 0) (v128.const i32x4 0 0 0 0))
    (v128.store (type $v128_array) (global.get $v128) (i32.const 0) (v128.const i32x4 0 0 0 0))
  )

  ;; Store an i32 and read it back, for an array of each element type.

  (func (export "i8_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $i8_array) (global.get $i8) (local.get $i) (local.get $v))
    (i32.load (type $i8_array) (global.get $i8) (local.get $i))
  )
  (func (export "i16_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $i16_array) (global.get $i16) (local.get $i) (local.get $v))
    (i32.load (type $i16_array) (global.get $i16) (local.get $i))
  )
  (func (export "i32_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $i32_array) (global.get $i32) (local.get $i) (local.get $v))
    (i32.load (type $i32_array) (global.get $i32) (local.get $i))
  )
  (func (export "i64_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $i64_array) (global.get $i64) (local.get $i) (local.get $v))
    (i32.load (type $i64_array) (global.get $i64) (local.get $i))
  )
  (func (export "f32_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $f32_array) (global.get $f32) (local.get $i) (local.get $v))
    (i32.load (type $f32_array) (global.get $f32) (local.get $i))
  )
  (func (export "f64_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i32.load (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "v128_arr_i32") (param $i i32) (param $v i32) (result i32)
    (i32.store (type $v128_array) (global.get $v128) (local.get $i) (local.get $v))
    (i32.load (type $v128_array) (global.get $v128) (local.get $i))
  )

  ;; Store an i64 and read it back, for an array of each element type.

  (func (export "i8_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $i8_array) (global.get $i8) (local.get $i) (local.get $v))
    (i64.load (type $i8_array) (global.get $i8) (local.get $i))
  )
  (func (export "i16_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $i16_array) (global.get $i16) (local.get $i) (local.get $v))
    (i64.load (type $i16_array) (global.get $i16) (local.get $i))
  )
  (func (export "i32_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $i32_array) (global.get $i32) (local.get $i) (local.get $v))
    (i64.load (type $i32_array) (global.get $i32) (local.get $i))
  )
  (func (export "i64_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $i64_array) (global.get $i64) (local.get $i) (local.get $v))
    (i64.load (type $i64_array) (global.get $i64) (local.get $i))
  )
  (func (export "f32_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $f32_array) (global.get $f32) (local.get $i) (local.get $v))
    (i64.load (type $f32_array) (global.get $f32) (local.get $i))
  )
  (func (export "f64_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "v128_arr_i64") (param $i i32) (param $v i64) (result i64)
    (i64.store (type $v128_array) (global.get $v128) (local.get $i) (local.get $v))
    (i64.load (type $v128_array) (global.get $v128) (local.get $i))
  )

  ;; Float accesses on arrays of integer, float, and vector elements.

  (func (export "i32_arr_f32") (param $i i32) (param $v f32) (result f32)
    (f32.store (type $i32_array) (global.get $i32) (local.get $i) (local.get $v))
    (f32.load (type $i32_array) (global.get $i32) (local.get $i))
  )
  (func (export "i16_arr_f64") (param $i i32) (param $v f64) (result f64)
    (f64.store (type $i16_array) (global.get $i16) (local.get $i) (local.get $v))
    (f64.load (type $i16_array) (global.get $i16) (local.get $i))
  )
  (func (export "f64_arr_f32") (param $i i32) (param $v f32) (result f32)
    (f32.store (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (f32.load (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "v128_arr_f64") (param $i i32) (param $v f64) (result f64)
    (f64.store (type $v128_array) (global.get $v128) (local.get $i) (local.get $v))
    (f64.load (type $v128_array) (global.get $v128) (local.get $i))
  )

  ;; v128 accesses, including on an array of v128 elements.

  (func (export "i32_arr_v128") (param $i i32) (param $v v128) (result v128)
    (v128.store (type $i32_array) (global.get $i32) (local.get $i) (local.get $v))
    (v128.load (type $i32_array) (global.get $i32) (local.get $i))
  )
  (func (export "i8_arr_v128_lane0") (param $i i32) (param $v v128) (result i64)
    (v128.store (type $i8_array) (global.get $i8) (local.get $i) (local.get $v))
    (i64.load (type $i8_array) (global.get $i8) (local.get $i))
  )
  (func (export "v128_arr_v128_lane1") (param $i i32) (param $v v128) (result i64)
    (v128.store (type $v128_array) (global.get $v128) (local.get $i) (local.get $v))
    (i64.load (type $v128_array) (global.get $v128) (i32.add (local.get $i) (i32.const 8)))
  )
  (func (export "v128_arr_unaligned") (param $v0 v128) (param $v1 v128) (result v128)
    (v128.store (type $v128_array) (global.get $v128_2) (i32.const 0) (local.get $v0))
    (v128.store (type $v128_array) (global.get $v128_2) (i32.const 16) (local.get $v1))
    (v128.load (type $v128_array) align=1 (global.get $v128_2) (i32.const 8))
  )

  ;; Sign and zero extension when loading from an array of wider elements.

  (func (export "i64_arr_load8_s") (param $i i32) (param $v i32) (result i32)
    (i32.store8 (type $i64_array) (global.get $i64) (local.get $i) (local.get $v))
    (i32.load8_s (type $i64_array) (global.get $i64) (local.get $i))
  )
  (func (export "i64_arr_load8_u") (param $i i32) (param $v i32) (result i32)
    (i32.store8 (type $i64_array) (global.get $i64) (local.get $i) (local.get $v))
    (i32.load8_u (type $i64_array) (global.get $i64) (local.get $i))
  )
  (func (export "i64_arr_load16_s") (param $i i32) (param $v i32) (result i32)
    (i32.store16 (type $i64_array) (global.get $i64) (local.get $i) (local.get $v))
    (i32.load16_s (type $i64_array) (global.get $i64) (local.get $i))
  )
  (func (export "i64_arr_load16_u") (param $i i32) (param $v i32) (result i32)
    (i32.store16 (type $i64_array) (global.get $i64) (local.get $i) (local.get $v))
    (i32.load16_u (type $i64_array) (global.get $i64) (local.get $i))
  )
  (func (export "f64_arr_load8_s") (param $i i32) (param $v i64) (result i64)
    (i64.store8 (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load8_s (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "f64_arr_load8_u") (param $i i32) (param $v i64) (result i64)
    (i64.store8 (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load8_u (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "f64_arr_load16_s") (param $i i32) (param $v i64) (result i64)
    (i64.store16 (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load16_s (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "f64_arr_load16_u") (param $i i32) (param $v i64) (result i64)
    (i64.store16 (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load16_u (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "f64_arr_load32_s") (param $i i32) (param $v i64) (result i64)
    (i64.store32 (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load32_s (type $f64_array) (global.get $f64) (local.get $i))
  )
  (func (export "f64_arr_load32_u") (param $i i32) (param $v i64) (result i64)
    (i64.store32 (type $f64_array) (global.get $f64) (local.get $i) (local.get $v))
    (i64.load32_u (type $f64_array) (global.get $f64) (local.get $i))
  )

  ;; Narrow stores wrap the value and leave surrounding bytes of wider elements
  ;; untouched.

  (func (export "i32_arr_store_widths") (param $init i32) (param $b0 i32) (param $h1 i32) (result i32)
    (i32.store (type $i32_array) (global.get $i32) (i32.const 0) (local.get $init))
    (i32.store8 (type $i32_array) (global.get $i32) (i32.const 0) (local.get $b0))
    (i32.store16 (type $i32_array) (global.get $i32) (i32.const 2) (local.get $h1))
    (i32.load (type $i32_array) (global.get $i32) (i32.const 0))
  )
  (func (export "i64_arr_store_widths") (param $init i64) (param $b0 i64) (param $h1 i64) (param $w1 i64) (result i64)
    (i64.store (type $i64_array) (global.get $i64) (i32.const 0) (local.get $init))
    (i64.store8 (type $i64_array) (global.get $i64) (i32.const 0) (local.get $b0))
    (i64.store16 (type $i64_array) (global.get $i64) (i32.const 2) (local.get $h1))
    (i64.store32 (type $i64_array) (global.get $i64) (i32.const 4) (local.get $w1))
    (i64.load (type $i64_array) (global.get $i64) (i32.const 0))
  )

  ;; Multibyte accesses and element accesses see the same payload bytes.

  (func (export "i32_arr_store_then_get") (param $v i32) (result i32)
    (i32.store (type $i32_array) (global.get $i32) (i32.const 8) (local.get $v))
    (array.get $i32_array (global.get $i32) (i32.const 2))
  )
  (func (export "i32_arr_set_then_load") (param $v i32) (result i32)
    (array.set $i32_array (global.get $i32) (i32.const 2) (local.get $v))
    (i32.load (type $i32_array) (global.get $i32) (i32.const 8))
  )
  (func (export "i16_arr_store_then_get_u") (param $v i32) (param $elem i32) (result i32)
    (i32.store (type $i16_array) (global.get $i16) (i32.const 4) (local.get $v))
    (array.get_u $i16_array (global.get $i16) (local.get $elem))
  )
  (func (export "i16_arr_store_then_get_s") (param $v i32) (param $elem i32) (result i32)
    (i32.store (type $i16_array) (global.get $i16) (i32.const 4) (local.get $v))
    (array.get_s $i16_array (global.get $i16) (local.get $elem))
  )
  (func (export "i16_arr_set_then_load") (param $v0 i32) (param $v1 i32) (result i32)
    (array.set $i16_array (global.get $i16) (i32.const 2) (local.get $v0))
    (array.set $i16_array (global.get $i16) (i32.const 3) (local.get $v1))
    (i32.load (type $i16_array) (global.get $i16) (i32.const 4))
  )
  (func (export "f64_arr_store_then_get") (param $v f64) (result f64)
    (f64.store (type $f64_array) (global.get $f64) (i32.const 8) (local.get $v))
    (array.get $f64_array (global.get $f64) (i32.const 1))
  )
  (func (export "i32_arr_v128_store_then_get") (param $v v128) (param $elem i32) (result i32)
    (v128.store (type $i32_array) (global.get $i32) (i32.const 0) (local.get $v))
    (array.get $i32_array (global.get $i32) (local.get $elem))
  )

  ;; Loads from an immutable array are allowed.

  (func (export "immutable_load") (param $i i32) (result i32)
    (i32.load (type $imm_i32_array) (global.get $imm_i32) (local.get $i))
  )

  ;; Any access on an empty array is out of bounds.

  (func (export "empty_load")
    (drop (i32.load8_u (type $i32_array) (global.get $empty) (i32.const 0)))
  )

  ;; Null references trap, before the bounds check.

  (func (export "null_load") (result i32)
    (i32.load (type $v128_array) (ref.null $v128_array) (i32.const 999))
  )
  (func (export "null_store")
    (f64.store (type $f64_array) (ref.null $f64_array) (i32.const 999) (f64.const 0))
  )
)

;; i32 accesses work the same on all element types.
(invoke "clear")
(assert_return (invoke "i8_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))
(assert_return (invoke "i16_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))
(assert_return (invoke "i32_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))
(assert_return (invoke "i64_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))
(assert_return (invoke "f32_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))
(assert_return (invoke "f64_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))
(assert_return (invoke "v128_arr_i32" (i32.const 3) (i32.const 0x12345678)) (i32.const 0x12345678))

;; The last in-bounds i32 access of a 16-byte payload is at index 12.
(invoke "clear")
(assert_return (invoke "i8_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))
(assert_return (invoke "i16_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))
(assert_return (invoke "i32_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))
(assert_return (invoke "i64_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))
(assert_return (invoke "f32_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))
(assert_return (invoke "f64_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))
(assert_return (invoke "v128_arr_i32" (i32.const 12) (i32.const -1)) (i32.const -1))

(assert_trap (invoke "i8_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")
(assert_trap (invoke "i16_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")
(assert_trap (invoke "i32_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")
(assert_trap (invoke "i64_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")
(assert_trap (invoke "f32_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")
(assert_trap (invoke "f64_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")
(assert_trap (invoke "v128_arr_i32" (i32.const 13) (i32.const 0)) "out of bounds")

;; The index is unsigned, so a negative index is far out of bounds.
(assert_trap (invoke "i8_arr_i32" (i32.const -1) (i32.const 0)) "out of bounds")
(assert_trap (invoke "i32_arr_i32" (i32.const -4) (i32.const 0)) "out of bounds")
(assert_trap (invoke "v128_arr_i32" (i32.const -16) (i32.const 0)) "out of bounds")

;; i64 accesses work the same on all element types.
(invoke "clear")
(assert_return (invoke "i8_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))
(assert_return (invoke "i16_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))
(assert_return (invoke "i32_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))
(assert_return (invoke "i64_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))
(assert_return (invoke "f32_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))
(assert_return (invoke "f64_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))
(assert_return (invoke "v128_arr_i64" (i32.const 5) (i64.const 0x1122334455667788)) (i64.const 0x1122334455667788))

;; The last in-bounds i64 access of a 16-byte payload is at index 8.
(invoke "clear")
(assert_return (invoke "i8_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_return (invoke "i16_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_return (invoke "i32_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_return (invoke "i64_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_return (invoke "f32_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_return (invoke "f64_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_return (invoke "v128_arr_i64" (i32.const 8) (i64.const -1)) (i64.const -1))
(assert_trap (invoke "i8_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")
(assert_trap (invoke "i16_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")
(assert_trap (invoke "i32_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")
(assert_trap (invoke "i64_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")
(assert_trap (invoke "f32_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")
(assert_trap (invoke "f64_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")
(assert_trap (invoke "v128_arr_i64" (i32.const 9) (i64.const 0)) "out of bounds")

;; Floats keep their bits, even when the element type is an integer type, and
;; even for NaNs with a payload.
(invoke "clear")
(assert_return (invoke "i32_arr_f32" (i32.const 2) (f32.const 3.3)) (f32.const 3.3))
(assert_return (invoke "i32_arr_f32" (i32.const 2) (f32.const -nan:0x654321)) (f32.const -nan:0x654321))
(assert_return (invoke "i16_arr_f64" (i32.const 3) (f64.const 3.3)) (f64.const 3.3))
(assert_return (invoke "i16_arr_f64" (i32.const 3) (f64.const nan:0x123456789ABCD)) (f64.const nan:0x123456789ABCD))
(assert_return (invoke "f64_arr_f32" (i32.const 6) (f32.const -1)) (f32.const -1))
(assert_return (invoke "v128_arr_f64" (i32.const 4) (f64.const 1.5)) (f64.const 1.5))

;; v128 accesses read and write 16 bytes.
(invoke "clear")
(assert_return (invoke "i32_arr_v128" (i32.const 0) (v128.const i32x4 1 2 3 4)) (v128.const i32x4 1 2 3 4))
(assert_return (invoke "i8_arr_v128_lane0" (i32.const 0) (v128.const i64x2 1 2)) (i64.const 1))
(assert_return (invoke "v128_arr_v128_lane1" (i32.const 0) (v128.const i64x2 1 2)) (i64.const 2))
(assert_return (invoke "v128_arr_unaligned" (v128.const i64x2 1 2) (v128.const i64x2 3 4)) (v128.const i64x2 2 3))
(assert_trap (invoke "i8_arr_v128_lane0" (i32.const 1) (v128.const i64x2 0 0)) "out of bounds")

;; Sign and zero extension.
(invoke "clear")
(assert_return (invoke "i64_arr_load8_s" (i32.const 1) (i32.const 0x80)) (i32.const 0xFFFFFF80))
(assert_return (invoke "i64_arr_load8_u" (i32.const 1) (i32.const 0x80)) (i32.const 0x00000080))
(assert_return (invoke "i64_arr_load8_s" (i32.const 1) (i32.const 0x7F)) (i32.const 0x0000007F))
(assert_return (invoke "i64_arr_load16_s" (i32.const 2) (i32.const 0x8000)) (i32.const 0xFFFF8000))
(assert_return (invoke "i64_arr_load16_u" (i32.const 2) (i32.const 0x8000)) (i32.const 0x00008000))
(assert_return (invoke "i64_arr_load16_s" (i32.const 2) (i32.const 0x7FFF)) (i32.const 0x00007FFF))
(assert_return (invoke "i64_arr_load16_u" (i32.const 2) (i32.const 0x7FFF)) (i32.const 0x00007FFF))
(assert_return (invoke "f64_arr_load8_s" (i32.const 1) (i64.const 0x80)) (i64.const 0xFFFFFFFFFFFFFF80))
(assert_return (invoke "f64_arr_load8_u" (i32.const 1) (i64.const 0x80)) (i64.const 0x0000000000000080))
(assert_return (invoke "f64_arr_load16_s" (i32.const 2) (i64.const 0x8000)) (i64.const 0xFFFFFFFFFFFF8000))
(assert_return (invoke "f64_arr_load16_u" (i32.const 2) (i64.const 0x8000)) (i64.const 0x0000000000008000))
(assert_return (invoke "f64_arr_load32_s" (i32.const 4) (i64.const 0x80000000)) (i64.const 0xFFFFFFFF80000000))
(assert_return (invoke "f64_arr_load32_u" (i32.const 4) (i64.const 0x80000000)) (i64.const 0x0000000080000000))

;; Narrow stores wrap the value and preserve surrounding bytes.
(invoke "clear")
(assert_return
  (invoke "i32_arr_store_widths" (i32.const 0xFFFFFFFF) (i32.const 0xDEAD12) (i32.const 0xDEAD3456))
  (i32.const 0x3456FF12)
)
(assert_return
  (invoke "i64_arr_store_widths" (i64.const 0xFFFFFFFFFFFFFFFF) (i64.const 0xDEAD12) (i64.const 0xDEAD3456) (i64.const 0xDEAD789ABCDE))
  (i64.const 0x789ABCDE3456FF12)
)

;; Multibyte accesses and element accesses agree.
(invoke "clear")
(assert_return (invoke "i32_arr_store_then_get" (i32.const 1337)) (i32.const 1337))
(assert_return (invoke "i32_arr_set_then_load" (i32.const 1337)) (i32.const 1337))
(assert_return (invoke "i16_arr_store_then_get_u" (i32.const 0x80001234) (i32.const 2)) (i32.const 0x00001234))
(assert_return (invoke "i16_arr_store_then_get_u" (i32.const 0x80001234) (i32.const 3)) (i32.const 0x00008000))
(assert_return (invoke "i16_arr_store_then_get_s" (i32.const 0x80001234) (i32.const 3)) (i32.const 0xFFFF8000))
(assert_return (invoke "i16_arr_set_then_load" (i32.const 0x1234) (i32.const 0x5678)) (i32.const 0x56781234))
(assert_return (invoke "f64_arr_store_then_get" (f64.const 3.3)) (f64.const 3.3))
(assert_return (invoke "i32_arr_v128_store_then_get" (v128.const i32x4 10 20 30 40) (i32.const 1)) (i32.const 20))
(assert_return (invoke "i32_arr_v128_store_then_get" (v128.const i32x4 10 20 30 40) (i32.const 3)) (i32.const 40))

;; Loads from immutable arrays.
(assert_return (invoke "immutable_load" (i32.const 0)) (i32.const 0x11223344))
(assert_return (invoke "immutable_load" (i32.const 4)) (i32.const 0x55667788))
(assert_return (invoke "immutable_load" (i32.const 2)) (i32.const 0x77881122))
(assert_trap (invoke "immutable_load" (i32.const 5)) "out of bounds")

;; Empty arrays and null references.
(assert_trap (invoke "empty_load") "out of bounds")
(assert_trap (invoke "null_load") "null array")
(assert_trap (invoke "null_store") "null array")

;; ============================================================================
;; Static offset and alignment immediates.
;; ============================================================================

(module
  (type $i8_array (array (mut i8)))
  (type $i32_array (array (mut i32)))

  (global $i8 (ref $i8_array) (array.new_default $i8_array (i32.const 16)))
  (global $i32 (ref $i32_array) (array.new_default $i32_array (i32.const 4)))

  ;; The effective address is the sum of the dynamic index and the static
  ;; offset, so these two functions access the same bytes.
  (func (export "store_offset") (param $i i32) (param $v i32)
    (i32.store (type $i8_array) offset=4 (global.get $i8) (local.get $i) (local.get $v))
  )
  (func (export "load_index") (param $i i32) (result i32)
    (i32.load (type $i8_array) (global.get $i8) (local.get $i))
  )
  (func (export "load_offset") (param $i i32) (result i32)
    (i32.load (type $i8_array) offset=4 (global.get $i8) (local.get $i))
  )

  ;; The offset alone can address the array.
  (func (export "load_offset_only") (result i32)
    (i32.load (type $i8_array) offset=12 (global.get $i8) (i32.const 0))
  )

  ;; The offset is included in the bounds check, and the sum of the index and
  ;; the offset does not wrap around.
  (func (export "load_offset_oob") (result i32)
    (i32.load (type $i8_array) offset=13 (global.get $i8) (i32.const 0))
  )
  (func (export "load_offset_no_wrap") (result i32)
    (i32.load8_u (type $i8_array) offset=1 (global.get $i8) (i32.const -1))
  )
  (func (export "load_offset_max") (result i32)
    (i32.load8_u (type $i8_array) offset=4294967295 (global.get $i8) (i32.const 1))
  )

  ;; Alignment is only a hint: it does not affect the result, and it does not
  ;; need to match the alignment of the array's elements.
  (func (export "unaligned_store") (param $i i32) (param $v i32)
    (i32.store (type $i32_array) align=1 (global.get $i32) (local.get $i) (local.get $v))
  )
  (func (export "aligned_store") (param $i i32) (param $v i32)
    (i32.store (type $i32_array) align=4 (global.get $i32) (local.get $i) (local.get $v))
  )
  (func (export "unaligned_load") (param $i i32) (result i32)
    (i32.load (type $i32_array) align=1 (global.get $i32) (local.get $i))
  )
  (func (export "aligned_load") (param $i i32) (result i32)
    (i32.load (type $i32_array) align=4 (global.get $i32) (local.get $i))
  )
)

(invoke "store_offset" (i32.const 2) (i32.const 0x12345678))
(assert_return (invoke "load_index" (i32.const 6)) (i32.const 0x12345678))
(assert_return (invoke "load_offset" (i32.const 2)) (i32.const 0x12345678))
(invoke "store_offset" (i32.const 0) (i32.const 0x11223344))
(assert_return (invoke "load_index" (i32.const 4)) (i32.const 0x11223344))
;; The second store overwrote the first two bytes of the first one.
(assert_return (invoke "load_index" (i32.const 6)) (i32.const 0x12341122))

(invoke "store_offset" (i32.const 8) (i32.const 0x55667788))
(assert_return (invoke "load_offset_only") (i32.const 0x55667788))

(assert_trap (invoke "load_offset_oob") "out of bounds")
(assert_trap (invoke "load_offset_no_wrap") "out of bounds")
(assert_trap (invoke "load_offset_max") "out of bounds")

;; An unaligned store of an i32 array crosses two elements.
(invoke "aligned_store" (i32.const 0) (i32.const 0))
(invoke "aligned_store" (i32.const 4) (i32.const 0))
(invoke "unaligned_store" (i32.const 2) (i32.const 0x12345678))
(assert_return (invoke "unaligned_load" (i32.const 2)) (i32.const 0x12345678))
(assert_return (invoke "aligned_load" (i32.const 0)) (i32.const 0x56780000))
(assert_return (invoke "aligned_load" (i32.const 4)) (i32.const 0x00001234))

;; ============================================================================
;; Binary format: memarg ::= flags:u32 typeidx:u32 offset:u32
;; ============================================================================

(module binary
  "\00asm\01\00\00\00"

  "\01\0a\03"         ;; Type section: 3 types
    "\5f\00"          ;; Type 0: (struct)
    "\5e\78\01"       ;; Type 1: (array (mut i8))
    "\60\00\01\7f"    ;; Type 2: (func (result i32))

  "\03\02\01\02"      ;; Function section: 1 function of type 2

  "\07\08\01"         ;; Export section: 1 export
    "\04test\00\00"   ;; (export "test" (func 0))

  "\0a\1e\01"         ;; Code section: 1 function
    "\1c\01"          ;; Body size 28, 1 local entry
      "\01\63\01"     ;; (local (ref null 1))
      "\41\08"        ;; i32.const 8
      "\fb\07\01"     ;; array.new_default 1
      "\22\00"        ;; local.tee 0
      "\41\00"        ;; i32.const 0
      "\41\2a"        ;; i32.const 42
      "\36\22\01\04"  ;; i32.store flags=0x22 (array, align=4) typeidx=1 offset=4
      "\20\00"        ;; local.get 0
      "\41\00"        ;; i32.const 0
      "\28\22\01\04"  ;; i32.load flags=0x22 (array, align=4) typeidx=1 offset=4
      "\0b"           ;; end
)

(assert_return (invoke "test") (i32.const 42))