| ;; 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)) |