summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/gc/structs2.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/gc/structs2.js')
-rw-r--r--js/src/jit-test/tests/wasm/gc/structs2.js243
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
+}