diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/wasm/memory-control | |
parent | Initial commit. (diff) | |
download | firefox-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')
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); +}); |