summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/memory-control
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/wasm/memory-control
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/wasm/memory-control')
-rw-r--r--js/src/jit-test/tests/wasm/memory-control/directives.txt1
-rw-r--r--js/src/jit-test/tests/wasm/memory-control/disabled.js18
-rw-r--r--js/src/jit-test/tests/wasm/memory-control/memory-discard.js227
3 files changed, 246 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/memory-control/directives.txt b/js/src/jit-test/tests/wasm/memory-control/directives.txt
new file mode 100644
index 0000000000..1092e20d8a
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/memory-control/directives.txt
@@ -0,0 +1 @@
+|jit-test| include:wasm.js; test-also=--wasm-compiler=optimizing --wasm-memory-control; test-also=--wasm-compiler=baseline --wasm-memory-control; test-also=--wasm-compiler=optimizing --no-wasm-memory64 --wasm-memory-control; test-also=--wasm-compiler=baseline --no-wasm-memory64 --wasm-memory-control
diff --git a/js/src/jit-test/tests/wasm/memory-control/disabled.js b/js/src/jit-test/tests/wasm/memory-control/disabled.js
new file mode 100644
index 0000000000..48903521db
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/memory-control/disabled.js
@@ -0,0 +1,18 @@
+// |jit-test| skip-if: wasmMemoryControlEnabled()
+
+const { validate } = WebAssembly;
+
+const UNRECOGNIZED_OPCODE = /unrecognized opcode/;
+
+let simpleTests = [
+ "(module (func (memory.discard (i32.const 0) (i32.const 65536))))",
+];
+
+for (let src of simpleTests) {
+ let bin = wasmTextToBinary(src);
+ assertEq(validate(bin), false);
+ wasmCompilationShouldFail(bin, UNRECOGNIZED_OPCODE);
+}
+
+const mem = new WebAssembly.Memory({ initial: 1 });
+assertEq(mem.discard, undefined);
diff --git a/js/src/jit-test/tests/wasm/memory-control/memory-discard.js b/js/src/jit-test/tests/wasm/memory-control/memory-discard.js
new file mode 100644
index 0000000000..87f2ae625a
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/memory-control/memory-discard.js
@@ -0,0 +1,227 @@
+// |jit-test| skip-if: !wasmMemoryControlEnabled(); test-also=--wasm-memory64; test-also=--no-wasm-memory64
+
+// This tests memory.discard and WebAssembly.Memory.discard() by placing data
+// (the alphabet) halfway across a page boundary, then discarding the first
+// page. The first half of the alphabet should be zeroed; the second should
+// not. The memory should remain readable and writable.
+//
+// The ultimate goal is to release physical pages of memory back to the
+// operating system, but we can't really observe memory metrics here. Oh well.
+
+function initModule(discardOffset, discardLen, discardViaJS, shared, memType = 'i32') {
+ const memProps = shared ? '4 4 shared' : '4'; // 4 pages
+ const text = `(module
+ (memory (export "memory") ${memType} ${memProps})
+ (data "abcdefghijklmnopqrstuvwxyz")
+ (func (export "init")
+ ;; splat alphabet halfway across the 3/4 page boundary.
+ ;; 196595 = (65536 * 3) - (26 / 2)
+ (memory.init 0 (${memType}.const 196595) (i32.const 0) (i32.const 26))
+ )
+ (func (export "letter") (param i32) (result i32)
+ (i32.load8_u (${memType}.add (${memType}.const 196595) ${memType == 'i64' ? '(i64.extend_i32_u (local.get 0))' : '(local.get 0)'}))
+ )
+ (func (export "discard")
+ (memory.discard (${memType}.const ${discardOffset}) (${memType}.const ${discardLen}))
+ )
+ )`;
+ const exp = wasmEvalText(text).exports;
+
+ return [exp, discardViaJS ? () => exp.memory.discard(discardOffset, discardLen) : exp.discard];
+}
+
+function checkRegion(exp, min, max, expectLetters) {
+ for (let i = min; i < max; i++) {
+ const c = "a".charCodeAt(0) + i;
+ const expected = expectLetters ? c : 0;
+ assertEq(exp.letter(i), expected, `checking position of letter ${String.fromCharCode(c)}`);
+ }
+}
+function checkFirstHalf(exp, expectLetters) { return checkRegion(exp, 0, 13, expectLetters) }
+function checkSecondHalf(exp, expectLetters) { return checkRegion(exp, 13, 26, expectLetters) }
+function checkWholeAlphabet(exp, expectLetters) { return checkRegion(exp, 0, 26, expectLetters) }
+
+function testAll(func) {
+ func(false, false, 'i32');
+ func(false, true, 'i32');
+ func(true, false, 'i32');
+ func(true, true, 'i32');
+ if (wasmMemory64Enabled()) {
+ func(false, false, 'i64');
+ func(false, true, 'i64');
+ func(true, false, 'i64');
+ func(true, true, 'i64');
+ }
+}
+
+testAll(function testHappyPath(discardViaJS, shared, memType) {
+ // Only page 3 of memory, half the alphabet
+ const [exp, discard] = initModule(65536 * 2, 65536, discardViaJS, shared, memType);
+
+ // All zero to start
+ checkWholeAlphabet(exp, false);
+
+ // Then all alphabet
+ exp.init();
+ checkWholeAlphabet(exp, true);
+
+ // Discarding the first page clears the first half of the alphabet
+ discard();
+ checkFirstHalf(exp, false);
+ checkSecondHalf(exp, true);
+
+ // We should be able to write back to a discarded region of memory
+ exp.init();
+ checkWholeAlphabet(exp, true);
+
+ // ...and then discard again
+ discard();
+ checkFirstHalf(exp, false);
+ checkSecondHalf(exp, true);
+});
+
+testAll(function testZeroLen(discardViaJS, shared) {
+ // Discard zero bytes
+ const [exp, discard] = initModule(PageSizeInBytes * 2, 0, discardViaJS, shared);
+
+ // Init the stuff
+ exp.init();
+ checkWholeAlphabet(exp, true);
+
+ // Discarding zero bytes should be valid...
+ discard();
+
+ // ...but should have no effect.
+ checkWholeAlphabet(exp, true);
+});
+
+testAll(function testWithGrow(discardViaJS, shared, memType) {
+ if (shared) {
+ return; // shared memories cannot grow
+ }
+
+ // Only page 3 of memory, half the alphabet. There is no max size on the
+ // memory, so it will be subject to moving grows in 32-bit mode.
+ const [exp, discard] = initModule(65536 * 2, 65536, discardViaJS, false, memType);
+
+ // Start with the whole alphabet
+ exp.init();
+ checkWholeAlphabet(exp, true);
+
+ // Discarding the first page clears the first half of the alphabet
+ discard();
+ checkFirstHalf(exp, false);
+ checkSecondHalf(exp, true);
+
+ // Oops we just grew by a preposterous amount, time to move
+ exp.memory.grow(200)
+
+ // The memory should still be readable
+ checkFirstHalf(exp, false);
+ checkSecondHalf(exp, true);
+
+ // We should be able to write back to a discarded region of memory
+ exp.init();
+ checkWholeAlphabet(exp, true);
+
+ // ...and then discard again
+ discard();
+ checkFirstHalf(exp, false);
+ checkSecondHalf(exp, true);
+});
+
+testAll(function testOOB(discardViaJS, shared) {
+ // Discard two pages where there is only one
+ const [exp, discard] = initModule(PageSizeInBytes * 3, PageSizeInBytes * 2, discardViaJS, shared);
+
+ exp.init();
+ checkWholeAlphabet(exp, true);
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /out of bounds/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+});
+
+testAll(function testOOB2(discardViaJS, shared) {
+ // Discard two pages starting near the end of 32-bit address space
+ // (would trigger an overflow in 32-bit world)
+ const [exp, discard] = initModule(2 ** 32 - PageSizeInBytes, PageSizeInBytes * 2, discardViaJS, shared);
+
+ exp.init();
+ checkWholeAlphabet(exp, true);
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /out of bounds/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+});
+
+testAll(function testOOB3(discardViaJS, shared) {
+ // Discard nearly an entire 32-bit address space's worth of pages. Very exciting!
+ const [exp, discard] = initModule(0, 2 ** 32 - PageSizeInBytes, discardViaJS, shared);
+
+ exp.init();
+ checkWholeAlphabet(exp, true);
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /out of bounds/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+});
+
+(function testOOB4() {
+ // Discard an entire 32-bit address space's worth of pages. JS can do this
+ // even to 32-bit memories because it can handle numbers bigger
+ // than 2^32 - 1.
+ const [exp, _] = initModule(0, 0, false); // pass zero to allow the wasm module to validate
+ const discard = () => exp.memory.discard(0, 2 ** 32);
+
+ exp.init();
+ checkWholeAlphabet(exp, true);
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /out of bounds/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+})();
+
+if (wasmMemory64Enabled()) {
+ (function testOverflow() {
+ // Discard UINT64_MAX - (2 pages), starting from page 4. This overflows but puts both start and end in bounds.
+ // This cannot be done with a JS discard because JS can't actually represent big enough integers.
+
+ // The big ol' number here is 2^64 - (65536 * 2)
+ const [exp, discard] = initModule(65536 * 3, `18_446_744_073_709_420_544`, false, false, 'i64');
+
+ // Init the stuff
+ exp.init();
+ checkWholeAlphabet(exp, true);
+
+ // Discarding should not be valid when it goes out of bounds
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /out of bounds/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+ })();
+}
+
+testAll(function testMisalignedStart(discardViaJS, shared) {
+ // Discard only the first half of the alphabet (this misaligns the start)
+ const [exp, discard] = initModule(PageSizeInBytes * 3 - 13, 13, discardViaJS, shared);
+
+ exp.init();
+ checkWholeAlphabet(exp, true);
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /unaligned/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+});
+
+testAll(function testMisalignedEnd(discardViaJS, shared) {
+ // Discard only the second half of the alphabet (this misaligns the end)
+ const [exp, discard] = initModule(PageSizeInBytes * 3, 13, discardViaJS, shared);
+
+ exp.init();
+ checkWholeAlphabet(exp, true);
+ assertErrorMessage(() => discard(), WebAssembly.RuntimeError, /unaligned/);
+
+ // Ensure nothing was discarded
+ checkWholeAlphabet(exp, true);
+});