diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/gc/structs2.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/gc/structs2.js | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/gc/structs2.js b/js/src/jit-test/tests/wasm/gc/structs2.js new file mode 100644 index 0000000000..8c5b94db11 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/structs2.js @@ -0,0 +1,243 @@ +// |jit-test| skip-if: !wasmGcEnabled() + +// This tests 8- and 16-bit field accesses for structs. + +// Check that struct.new writes for 8-bit fields do not overwrite subsequent +// data. Because the writes happen forwards in the address space, the only +// way I could think to do this is to force an 8-bit field to occupy the last +// byte of the OOL malloc'd block, and then hope that ASan runs in automation +// will pick up any overrun. I think it's impossible to test this from inside +// the JS+wasm universe. Hence the test is pretty pointless from a purely +// JS+wasm interpretation. +{ + let txt = + `(module + (type $hasOOL (struct + ;; In-line storage; 16 fields that preserve 16-alignment + (field i64) (field i64) (field i64) (field i64) ;; 32 + (field i64) (field i64) (field i64) (field i64) ;; 64 + (field i64) (field i64) (field i64) (field i64) ;; 96 + (field i64) (field i64) (field i64) (field i64) ;; 128 + ;; Out-of-line storage (or maybe it starts earlier, but + ;; definitely not after this point). 16 bytes on the + ;; basis that StructLayout::close will round the requested + ;; block size up to at max the next 16 byte boundary. + ;; The goal is that the last (field i8) is right at the + ;; end of the resulting malloc'd block, so that, if the + ;; struct.new initialisation code mistakenly initialises + ;; that field with a write larger than 8 bits, then we'll + ;; have a write off the end of the malloc'd block, which + ;; ASan automation runs should detect. + (field i8) (field i8) (field i8) (field i8) + (field i8) (field i8) (field i8) (field i8) + (field i8) (field i8) (field i8) (field i8) + (field i8) (field i8) (field i8) (field i8)) + ) + (func (export "build8") + (param $filler i64) (param $interesting i32) (result eqref) + (struct.new $hasOOL + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + ) + ) + )`; + let exports = wasmEvalText(txt).exports; + let obj8 = exports.build8(0x1234n, 0x5678); + // The above call should trigger OOB writes if the struct.new field + // writes are too large, but those will only be visible if we're running + // on ASan or Valgrind. In any case, add a fake data dependency below, so + // that the construction of the object can't (so easily) be optimised away. + assertEq(obj8[0] + BigInt(obj8[31]), 0x12ACn); // == 0x1234 + 0x78 +} + +// And exactly the same, except for 16 bit fields. +{ + let txt = + `(module + (type $hasOOL (struct + ;; in-line storage + (field i64) (field i64) (field i64) (field i64) ;; 32 + (field i64) (field i64) (field i64) (field i64) ;; 64 + (field i64) (field i64) (field i64) (field i64) ;; 96 + (field i64) (field i64) (field i64) (field i64) ;; 128 + (field i16) (field i16) (field i16) (field i16) + (field i16) (field i16) (field i16) (field i16)) + ) + (func (export "build16") + (param $filler i64) (param $interesting i32) (result eqref) + (struct.new $hasOOL + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $filler) (local.get $filler) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + (local.get $interesting) (local.get $interesting) + ) + ) + )`; + let exports = wasmEvalText(txt).exports; + let obj16 = exports.build16(0x4321n, 0x7865); + assertEq(obj16[0] + BigInt(obj16[23]), 0xBB86n); // == 0x4321 + 0x7865 +} + +// Test that 8-bit field writes do not overwrite adjacent fields. +{ + let txt = + `(module + (type $struct8x8 + (struct (field i8) (field i8) (field i8) (field (mut i8)) + (field i8) (field i8) (field i8) (field i8) + )) + (func (export "create") (result eqref) + (struct.new $struct8x8 (i32.const 0x55) (i32.const 0x55) + (i32.const 0x55) (i32.const 0x55) + (i32.const 0x55) (i32.const 0x55) + (i32.const 0x55) (i32.const 0x55) + )) + (func (export "writeField8x8_3") (param $p eqref) (param $v i32) + (struct.set $struct8x8 3 (ref.cast (ref null $struct8x8) (local.get $p)) + (local.get $v)) + ) + )`; + let exports = wasmEvalText(txt).exports; + let theObject = exports.create(); + exports.writeField8x8_3(theObject, 0x77); + assertEq(theObject[0], 0x55); + assertEq(theObject[1], 0x55); + assertEq(theObject[2], 0x55); + assertEq(theObject[3], 0x77); + assertEq(theObject[4], 0x55); + assertEq(theObject[5], 0x55); + assertEq(theObject[6], 0x55); + assertEq(theObject[7], 0x55); +} + +// Test that 16-bit field writes do not overwrite adjacent fields. +{ + let txt = + `(module + (type $struct16x8 + (struct (field i16) (field i16) (field i16) (field (mut i16)) + (field i16) (field i16) (field i16) (field i16) + )) + (func (export "create") (result eqref) + (struct.new $struct16x8 (i32.const 0x5555) (i32.const 0x5555) + (i32.const 0x5555) (i32.const 0x5555) + (i32.const 0x5555) (i32.const 0x5555) + (i32.const 0x5555) (i32.const 0x5555) + )) + (func (export "writeField16x8_3") (param $p eqref) (param $v i32) + (struct.set $struct16x8 3 (ref.cast (ref null $struct16x8) (local.get $p)) + (local.get $v)) + ) + )`; + let exports = wasmEvalText(txt).exports; + let theObject = exports.create(); + exports.writeField16x8_3(theObject, 0x7766); + assertEq(theObject[0], 0x5555); + assertEq(theObject[1], 0x5555); + assertEq(theObject[2], 0x5555); + assertEq(theObject[3], 0x7766); + assertEq(theObject[4], 0x5555); + assertEq(theObject[5], 0x5555); + assertEq(theObject[6], 0x5555); + assertEq(theObject[7], 0x5555); +} + +// Test that 8-bit field reads sign/zero extend correctly. +{ + let txt = + `(module + (type $struct8x8 + (struct (field i8) (field i8) (field i8) (field i8) + (field i8) (field i8) (field i8) (field i8) + )) + (func (export "create") (result eqref) + (struct.new $struct8x8 (i32.const 0x11) (i32.const 0x82) + (i32.const 0x23) (i32.const 0x94) + (i32.const 0x35) (i32.const 0xA6) + (i32.const 0x47) (i32.const 0xB8) + )) + ;; read i8 from a field, unsigned extend, read value has top bit 0 + (func (export "readU8hi0") (param $p eqref) (result i32) + (struct.get_u $struct8x8 2 (ref.cast (ref null $struct8x8) (local.get $p))) + ) + ;; read i8 from a field, unsigned extend, read value has top bit 1 + (func (export "readU8hi1") (param $p eqref) (result i32) + (struct.get_u $struct8x8 3 (ref.cast (ref null $struct8x8) (local.get $p))) + ) + ;; read i8 from a field, signed extend, read value has top bit 0 + (func (export "readS8hi0") (param $p eqref) (result i32) + (struct.get_s $struct8x8 4 (ref.cast (ref null $struct8x8) (local.get $p))) + ) + ;; read i8 from a field, signed extend, read value has top bit 1 + (func (export "readS8hi1") (param $p eqref) (result i32) + (struct.get_s $struct8x8 5 (ref.cast (ref null $struct8x8) (local.get $p))) + ) + )`; + let exports = wasmEvalText(txt).exports; + let theObject = exports.create(); + assertEq(exports.readU8hi0(theObject), 0x23); // zx of 0x23 + assertEq(exports.readU8hi1(theObject), 0x94); // zx of 0x94 + assertEq(exports.readS8hi0(theObject), 0x35); // sx of 0x35 + assertEq(exports.readS8hi1(theObject), -0x5A); // sx of 0xA6 +} + +// Test that 16-bit field reads sign/zero extend correctly. +{ + let txt = + `(module + (type $struct16x8 + (struct (field i16) (field i16) (field i16) (field i16) + (field i16) (field i16) (field i16) (field i16) + )) + (func (export "create") (result eqref) + (struct.new $struct16x8 (i32.const 0x11FF) (i32.const 0x82FE) + (i32.const 0x23FD) (i32.const 0x94FC) + (i32.const 0x35FB) (i32.const 0xA6FA) + (i32.const 0x47F9) (i32.const 0xB8F8) + )) + ;; read i16 from a field, unsigned extend, read value has top bit 0 + (func (export "readU16hi0") (param $p eqref) (result i32) + (struct.get_u $struct16x8 2 (ref.cast (ref null $struct16x8) (local.get $p))) + ) + ;; read i16 from a field, unsigned extend, read value has top bit 1 + (func (export "readU16hi1") (param $p eqref) (result i32) + (struct.get_u $struct16x8 3 (ref.cast (ref null $struct16x8) (local.get $p))) + ) + ;; read i16 from a field, signed extend, read value has top bit 0 + (func (export "readS16hi0") (param $p eqref) (result i32) + (struct.get_s $struct16x8 4 (ref.cast (ref null $struct16x8) (local.get $p))) + ) + ;; read i16 from a field, signed extend, read value has top bit 1 + (func (export "readS16hi1") (param $p eqref) (result i32) + (struct.get_s $struct16x8 5 (ref.cast (ref null $struct16x8) (local.get $p))) + ) + )`; + let exports = wasmEvalText(txt).exports; + let theObject = exports.create(); + assertEq(exports.readU16hi0(theObject), 0x23FD); // zx of 0x23FD + assertEq(exports.readU16hi1(theObject), 0x94FC); // zx of 0x94FC + assertEq(exports.readS16hi0(theObject), 0x35FB); // sx of 0x35FB + assertEq(exports.readS16hi1(theObject), -0x5906); // sx of 0xA6FC +} |