mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 08:42:13 +00:00
Bug 1471500 - Complete initial implementation of the bulk-memory proposal. Part 10 of 10. r=bbouvier.
Test cases for new instructions {mem,table}.{init,drop} and table.copy. * New file passive-segs-nonboundary.js: This tests the new instructions for "normal" (non-boundary) cases enough to be reasonably convinced that the implementation works. It does not test any exceptional/boundary cases, except for the case where we attempt to call through an empty table slot. This file also incorporates tests for memory.copy and memory.fill that were previously in memory-bulk.js, which, in turn, has been removed. * New file passive-segs-boundary.js: This tests boundary / validation cases for the new instructions. Almost all tests wind up throwing some kind of exception. --HG-- extra : rebase_source : ec91c595c2f9af92ac440134cf965c736b90d03a
This commit is contained in:
parent
fe52931272
commit
4d15d8f78a
345
js/src/jit-test/tests/wasm/passive-segs-boundary.js
Normal file
345
js/src/jit-test/tests/wasm/passive-segs-boundary.js
Normal file
@ -0,0 +1,345 @@
|
||||
|
||||
if (!wasmBulkMemSupported())
|
||||
quit(0);
|
||||
|
||||
// Perform a test which,
|
||||
//
|
||||
// * if errKind is defined, is expected to fail with an exception
|
||||
// characterised by errKind and errText.
|
||||
//
|
||||
// * if errKind is undefined, is expected to succeed, in which case errKind
|
||||
// and errText are ignored.
|
||||
//
|
||||
// The function body will be [insn1, insn2]. isMem controls whether the
|
||||
// module is constructed with memory or table initializers. haveMemOrTable
|
||||
// determines whether there is actually a memory or table to work with.
|
||||
|
||||
function do_test(insn1, insn2, errKind, errText, isMem, haveMemOrTable)
|
||||
{
|
||||
let preamble;
|
||||
if (isMem) {
|
||||
let mem_def = haveMemOrTable ? "(memory 1 1)" : "";
|
||||
let mem_init = haveMemOrTable
|
||||
? `(data (i32.const 2) "\\03\\01\\04\\01")
|
||||
(data passive "\\02\\07\\01\\08")
|
||||
(data (i32.const 12) "\\07\\05\\02\\03\\06")
|
||||
(data passive "\\05\\09\\02\\07\\06")`
|
||||
: "";
|
||||
preamble
|
||||
= `;; -------- Memories --------
|
||||
${mem_def}
|
||||
;; -------- Memory initialisers --------
|
||||
${mem_init}
|
||||
`;
|
||||
} else {
|
||||
let tab_def = haveMemOrTable ? "(table 30 30 anyfunc)" : "";
|
||||
let tab_init = haveMemOrTable
|
||||
? `(elem (i32.const 2) 3 1 4 1)
|
||||
(elem passive 2 7 1 8)
|
||||
(elem (i32.const 12) 7 5 2 3 6)
|
||||
(elem passive 5 9 2 7 6)`
|
||||
: "";
|
||||
preamble
|
||||
= `;; -------- Tables --------
|
||||
${tab_def}
|
||||
;; -------- Table initialisers --------
|
||||
${tab_init}
|
||||
;; ------ Functions (0..9) referred by the table/esegs ------
|
||||
(func (result i32) (i32.const 0))
|
||||
(func (result i32) (i32.const 1))
|
||||
(func (result i32) (i32.const 2))
|
||||
(func (result i32) (i32.const 3))
|
||||
(func (result i32) (i32.const 4))
|
||||
(func (result i32) (i32.const 5))
|
||||
(func (result i32) (i32.const 6))
|
||||
(func (result i32) (i32.const 7))
|
||||
(func (result i32) (i32.const 8))
|
||||
(func (result i32) (i32.const 9))
|
||||
`;
|
||||
}
|
||||
|
||||
let txt = "(module\n" + preamble +
|
||||
`;; -------- testfn --------
|
||||
(func (export "testfn")
|
||||
${insn1}
|
||||
${insn2}
|
||||
)
|
||||
)`;
|
||||
|
||||
if (!!errKind) {
|
||||
assertErrorMessage(
|
||||
() => {
|
||||
let inst = wasmEvalText(txt);
|
||||
inst.exports.testfn();
|
||||
},
|
||||
errKind,
|
||||
errText
|
||||
);
|
||||
} else {
|
||||
let inst = wasmEvalText(txt);
|
||||
assertEq(undefined, inst.exports.testfn());
|
||||
}
|
||||
}
|
||||
|
||||
function mem_test(insn1, insn2, errKind, errText, haveMem=true) {
|
||||
do_test(insn1, insn2, errKind, errText,
|
||||
/*isMem=*/true, haveMem);
|
||||
}
|
||||
|
||||
function mem_test_nofail(insn1, insn2) {
|
||||
do_test(insn1, insn2, undefined, undefined,
|
||||
/*isMem=*/true, /*haveMemOrTable=*/true);
|
||||
}
|
||||
|
||||
function tab_test(insn1, insn2, errKind, errText, haveTab=true) {
|
||||
do_test(insn1, insn2, errKind, errText,
|
||||
/*isMem=*/false, haveTab);
|
||||
}
|
||||
|
||||
function tab_test_nofail(insn1, insn2) {
|
||||
do_test(insn1, insn2, undefined, undefined,
|
||||
/*isMem=*/false, /*haveMemOrTable=*/true);
|
||||
}
|
||||
|
||||
|
||||
//---- memory.{drop,init} -------------------------------------------------
|
||||
|
||||
// drop with no memory
|
||||
mem_test("memory.drop 3", "",
|
||||
WebAssembly.CompileError, /can't touch memory without memory/,
|
||||
false);
|
||||
|
||||
// init with no memory
|
||||
mem_test("(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /can't touch memory without memory/,
|
||||
false);
|
||||
|
||||
// drop with data seg ix out of range
|
||||
mem_test("memory.drop 4", "",
|
||||
WebAssembly.CompileError, /memory.{drop,init} index out of range/);
|
||||
|
||||
// init with data seg ix out of range
|
||||
mem_test("(memory.init 4 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /memory.{drop,init} index out of range/);
|
||||
|
||||
// drop with data seg ix indicating an active segment
|
||||
mem_test("memory.drop 2", "",
|
||||
WebAssembly.RuntimeError, /use of invalid passive data segment/);
|
||||
|
||||
// init with data seg ix indicating an active segment
|
||||
mem_test("(memory.init 2 (i32.const 1234) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.RuntimeError, /use of invalid passive data segment/);
|
||||
|
||||
// init, using a data seg ix more than once is OK
|
||||
mem_test_nofail(
|
||||
"(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))",
|
||||
"(memory.init 1 (i32.const 4321) (i32.const 1) (i32.const 1))");
|
||||
|
||||
// drop, then drop
|
||||
mem_test("memory.drop 1",
|
||||
"memory.drop 1",
|
||||
WebAssembly.RuntimeError, /use of invalid passive data segment/);
|
||||
|
||||
// drop, then init
|
||||
mem_test("memory.drop 1",
|
||||
"(memory.init 1 (i32.const 1234) (i32.const 1) (i32.const 1))",
|
||||
WebAssembly.RuntimeError, /use of invalid passive data segment/);
|
||||
|
||||
// init: seg ix is valid passive, but length to copy > len of seg
|
||||
mem_test("",
|
||||
"(memory.init 1 (i32.const 1234) (i32.const 0) (i32.const 5))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, but implies copying beyond end of seg
|
||||
mem_test("",
|
||||
"(memory.init 1 (i32.const 1234) (i32.const 2) (i32.const 3))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, but implies copying beyond end of dst
|
||||
mem_test("",
|
||||
"(memory.init 1 (i32.const 0xFFFE) (i32.const 1) (i32.const 3))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, zero len, but src offset out of bounds
|
||||
mem_test("",
|
||||
"(memory.init 1 (i32.const 1234) (i32.const 4) (i32.const 0))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, zero len, but dst offset out of bounds
|
||||
mem_test("",
|
||||
"(memory.init 1 (i32.const 0x10000) (i32.const 2) (i32.const 0))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// drop: too many args
|
||||
mem_test("memory.drop 1 (i32.const 42)", "",
|
||||
WebAssembly.CompileError,
|
||||
/unused values not explicitly dropped by end of block/);
|
||||
|
||||
// init: too many args
|
||||
mem_test("(memory.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
|
||||
"",
|
||||
SyntaxError, /parsing wasm text at/);
|
||||
|
||||
// init: too few args
|
||||
mem_test("(memory.init 1 (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
|
||||
// invalid argument types
|
||||
{
|
||||
const tys = ['i32', 'f32', 'i64', 'f64'];
|
||||
|
||||
for (let ty1 of tys) {
|
||||
for (let ty2 of tys) {
|
||||
for (let ty3 of tys) {
|
||||
if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
|
||||
continue; // this is the only valid case
|
||||
let i1 = `(memory.init 1 (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`;
|
||||
mem_test(i1, "", WebAssembly.CompileError, /type mismatch/);
|
||||
}}}
|
||||
}
|
||||
|
||||
|
||||
//---- table.{drop,init} --------------------------------------------------
|
||||
|
||||
// drop with no table
|
||||
tab_test("table.drop 3", "",
|
||||
WebAssembly.CompileError, /can't table.drop without a table/,
|
||||
false);
|
||||
|
||||
// init with no table
|
||||
tab_test("(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /can't table.init without a table/,
|
||||
false);
|
||||
|
||||
// drop with elem seg ix out of range
|
||||
tab_test("table.drop 4", "",
|
||||
WebAssembly.CompileError, /table.drop index out of range/);
|
||||
|
||||
// init with elem seg ix out of range
|
||||
tab_test("(table.init 4 (i32.const 12) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError, /table.init index out of range/);
|
||||
|
||||
// drop with elem seg ix indicating an active segment
|
||||
tab_test("table.drop 2", "",
|
||||
WebAssembly.RuntimeError, /use of invalid passive element segment/);
|
||||
|
||||
// init with elem seg ix indicating an active segment
|
||||
tab_test("(table.init 2 (i32.const 12) (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.RuntimeError, /use of invalid passive element segment/);
|
||||
|
||||
// init, using an elem seg ix more than once is OK
|
||||
tab_test_nofail(
|
||||
"(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))",
|
||||
"(table.init 1 (i32.const 21) (i32.const 1) (i32.const 1))");
|
||||
|
||||
// drop, then drop
|
||||
tab_test("table.drop 1",
|
||||
"table.drop 1",
|
||||
WebAssembly.RuntimeError, /use of invalid passive element segment/);
|
||||
|
||||
// drop, then init
|
||||
tab_test("table.drop 1",
|
||||
"(table.init 1 (i32.const 12) (i32.const 1) (i32.const 1))",
|
||||
WebAssembly.RuntimeError, /use of invalid passive element segment/);
|
||||
|
||||
// init: seg ix is valid passive, but length to copy > len of seg
|
||||
tab_test("",
|
||||
"(table.init 1 (i32.const 12) (i32.const 0) (i32.const 5))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, but implies copying beyond end of seg
|
||||
tab_test("",
|
||||
"(table.init 1 (i32.const 12) (i32.const 2) (i32.const 3))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, but implies copying beyond end of dst
|
||||
tab_test("",
|
||||
"(table.init 1 (i32.const 28) (i32.const 1) (i32.const 3))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, zero len, but src offset out of bounds
|
||||
tab_test("",
|
||||
"(table.init 1 (i32.const 12) (i32.const 4) (i32.const 0))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// init: seg ix is valid passive, zero len, but dst offset out of bounds
|
||||
tab_test("",
|
||||
"(table.init 1 (i32.const 30) (i32.const 2) (i32.const 0))",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// drop: too many args
|
||||
tab_test("table.drop 1 (i32.const 42)", "",
|
||||
WebAssembly.CompileError,
|
||||
/unused values not explicitly dropped by end of block/);
|
||||
|
||||
// init: too many args
|
||||
tab_test("(table.init 1 (i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1))",
|
||||
"",
|
||||
SyntaxError, /parsing wasm text at/);
|
||||
|
||||
// init: too few args
|
||||
tab_test("(table.init 1 (i32.const 1) (i32.const 1))", "",
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
|
||||
// invalid argument types
|
||||
{
|
||||
const tys = ['i32', 'f32', 'i64', 'f64'];
|
||||
|
||||
const ops = ['table.init 1', 'table.copy'];
|
||||
for (let ty1 of tys) {
|
||||
for (let ty2 of tys) {
|
||||
for (let ty3 of tys) {
|
||||
for (let op of ops) {
|
||||
if (ty1 == 'i32' && ty2 == 'i32' && ty3 == 'i32')
|
||||
continue; // this is the only valid case
|
||||
let i1 = `(${op} (${ty1}.const 1) (${ty2}.const 1) (${ty3}.const 1))`;
|
||||
tab_test(i1, "", WebAssembly.CompileError, /type mismatch/);
|
||||
}}}}
|
||||
}
|
||||
|
||||
|
||||
//---- table.copy ---------------------------------------------------------
|
||||
|
||||
// There are no immediates here, only 3 dynamic args. So we're limited to
|
||||
// runtime boundary checks.
|
||||
|
||||
// passive-segs-smoketest.js tests the normal, non-exception cases of
|
||||
// table.copy. Here we just test the boundary-failure cases. The
|
||||
// table's valid indices are 0 .. 29 inclusive.
|
||||
|
||||
// copy: dst range invalid
|
||||
tab_test("(table.copy (i32.const 28) (i32.const 1) (i32.const 3))",
|
||||
"",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// copy: dst wraparound end of 32 bit offset space
|
||||
tab_test("(table.copy (i32.const 0xFFFFFFFE) (i32.const 1) (i32.const 2))",
|
||||
"",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// copy: src range invalid
|
||||
tab_test("(table.copy (i32.const 15) (i32.const 25) (i32.const 6))",
|
||||
"",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// copy: src wraparound end of 32 bit offset space
|
||||
tab_test("(table.copy (i32.const 15) (i32.const 0xFFFFFFFE) (i32.const 2))",
|
||||
"",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// copy: zero length with both offsets in-bounds is OK
|
||||
tab_test_nofail(
|
||||
"(table.copy (i32.const 15) (i32.const 25) (i32.const 0))",
|
||||
"");
|
||||
|
||||
// copy: zero length with dst offset out of bounds
|
||||
tab_test("(table.copy (i32.const 30) (i32.const 15) (i32.const 0))",
|
||||
"",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
||||
|
||||
// copy: zero length with src offset out of bounds
|
||||
tab_test("(table.copy (i32.const 15) (i32.const 30) (i32.const 0))",
|
||||
"",
|
||||
WebAssembly.RuntimeError, /index out of bounds/);
|
@ -2,9 +2,264 @@
|
||||
if (!wasmBulkMemSupported())
|
||||
quit(0);
|
||||
|
||||
const Module = WebAssembly.Module;
|
||||
const Instance = WebAssembly.Instance;
|
||||
|
||||
// Some non-boundary tests for {table,memory}.{init,drop,copy}. The table
|
||||
// case is more complex and appears first. The memory case is a simplified
|
||||
// version of it.
|
||||
|
||||
// This module exports 5 functions ..
|
||||
let tab_expmod_t =
|
||||
`(module
|
||||
(func (export "ef0") (result i32) (i32.const 0))
|
||||
(func (export "ef1") (result i32) (i32.const 1))
|
||||
(func (export "ef2") (result i32) (i32.const 2))
|
||||
(func (export "ef3") (result i32) (i32.const 3))
|
||||
(func (export "ef4") (result i32) (i32.const 4))
|
||||
)`;
|
||||
|
||||
// .. and this one imports those 5 functions. It adds 5 of its own, creates a
|
||||
// 30 element table using both active and passive initialisers, with a mixture
|
||||
// of the imported and local functions. |testfn| is exported. It uses the
|
||||
// supplied |insn| to modify the table somehow, and then will indirect-call
|
||||
// the table entry number specified as a parameter. That will either return a
|
||||
// value 0 to 9 indicating the function called, or will throw an exception if
|
||||
// the table entry is empty.
|
||||
function gen_tab_impmod_t(insn)
|
||||
{
|
||||
let t =
|
||||
`(module
|
||||
;; -------- Types --------
|
||||
(type (func (result i32))) ;; type #0
|
||||
;; -------- Tables --------
|
||||
(table 30 30 anyfunc)
|
||||
;; -------- Table initialisers --------
|
||||
(elem (i32.const 2) 3 1 4 1)
|
||||
(elem passive 2 7 1 8)
|
||||
(elem (i32.const 12) 7 5 2 3 6)
|
||||
(elem passive 5 9 2 7 6)
|
||||
;; -------- Imports --------
|
||||
(import "a" "if0" (result i32)) ;; index 0
|
||||
(import "a" "if1" (result i32))
|
||||
(import "a" "if2" (result i32))
|
||||
(import "a" "if3" (result i32))
|
||||
(import "a" "if4" (result i32)) ;; index 4
|
||||
;; -------- Functions --------
|
||||
(func (result i32) (i32.const 5)) ;; index 5
|
||||
(func (result i32) (i32.const 6))
|
||||
(func (result i32) (i32.const 7))
|
||||
(func (result i32) (i32.const 8))
|
||||
(func (result i32) (i32.const 9)) ;; index 9
|
||||
|
||||
(func (export "testfn") (param i32) (result i32)
|
||||
${insn}
|
||||
;; call the selected table entry, which will either return a value,
|
||||
;; or will cause an exception.
|
||||
get_local 0 ;; callIx
|
||||
call_indirect 0 ;; and its return value is our return value.
|
||||
)
|
||||
)`;
|
||||
return t;
|
||||
};
|
||||
|
||||
// This is the test driver. It constructs the abovementioned module, using
|
||||
// the given |instruction| to modify the table, and then probes the table
|
||||
// by making indirect calls, one for each element of |expected_result_vector|.
|
||||
// The results are compared to those in the vector.
|
||||
|
||||
function tab_test(instruction, expected_result_vector)
|
||||
{
|
||||
let tab_expmod_b = wasmTextToBinary(tab_expmod_t);
|
||||
let tab_expmod_i = new Instance(new Module(tab_expmod_b));
|
||||
|
||||
let tab_impmod_t = gen_tab_impmod_t(instruction);
|
||||
let tab_impmod_b = wasmTextToBinary(tab_impmod_t);
|
||||
|
||||
for (let i = 0; i < expected_result_vector.length; i++) {
|
||||
let inst = new Instance(new Module(tab_impmod_b),
|
||||
{a:{if0:tab_expmod_i.exports.ef0,
|
||||
if1:tab_expmod_i.exports.ef1,
|
||||
if2:tab_expmod_i.exports.ef2,
|
||||
if3:tab_expmod_i.exports.ef3,
|
||||
if4:tab_expmod_i.exports.ef4
|
||||
}});
|
||||
let expected = expected_result_vector[i];
|
||||
let actual = undefined;
|
||||
try {
|
||||
actual = inst.exports.testfn(i);
|
||||
assertEq(actual !== null, true);
|
||||
} catch (e) {
|
||||
if (!(e instanceof Error &&
|
||||
e.message.match(/indirect call to null/)))
|
||||
throw e;
|
||||
// actual remains undefined
|
||||
}
|
||||
assertEq(actual, expected,
|
||||
"tab_test fail: insn = '" + instruction + "', index = " +
|
||||
i + ", expected = " + expected + ", actual = " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
// Using 'e' for empty (undefined) spaces in the table, to make it easier
|
||||
// to count through the vector entries when debugging.
|
||||
let e = undefined;
|
||||
|
||||
// This just gives the initial state of the table, with its active
|
||||
// initialisers applied
|
||||
tab_test("nop",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy non-null over non-null
|
||||
tab_test("(table.copy (i32.const 13) (i32.const 2) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,3,1, 4,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy non-null over null
|
||||
tab_test("(table.copy (i32.const 25) (i32.const 15) (i32.const 2))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, 3,6,e,e,e]);
|
||||
|
||||
// Copy null over non-null
|
||||
tab_test("(table.copy (i32.const 13) (i32.const 25) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,e,e, e,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy null over null
|
||||
tab_test("(table.copy (i32.const 20) (i32.const 22) (i32.const 4))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy null and non-null entries, non overlapping
|
||||
tab_test("(table.copy (i32.const 25) (i32.const 1) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,3,1,e,e]);
|
||||
|
||||
// Copy null and non-null entries, overlapping, backwards
|
||||
tab_test("(table.copy (i32.const 10) (i32.const 12) (i32.const 7))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, 7,5,2,3,6, e,e,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy null and non-null entries, overlapping, forwards
|
||||
tab_test("(table.copy (i32.const 12) (i32.const 10) (i32.const 7))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,e,e,7, 5,2,3,6,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Passive init that overwrites all-null entries
|
||||
tab_test("(table.init 1 (i32.const 7) (i32.const 0) (i32.const 4))",
|
||||
[e,e,3,1,4, 1,e,2,7,1, 8,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Passive init that overwrites existing active-init-created entries
|
||||
tab_test("(table.init 3 (i32.const 15) (i32.const 1) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 9,2,7,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Perform active and passive initialisation and then multiple copies
|
||||
tab_test("(table.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) \n" +
|
||||
"table.drop 1 \n" +
|
||||
"(table.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) \n" +
|
||||
"table.drop 3 \n" +
|
||||
"(table.copy (i32.const 20) (i32.const 15) (i32.const 5)) \n" +
|
||||
"(table.copy (i32.const 21) (i32.const 29) (i32.const 1)) \n" +
|
||||
"(table.copy (i32.const 24) (i32.const 10) (i32.const 1)) \n" +
|
||||
"(table.copy (i32.const 13) (i32.const 11) (i32.const 4)) \n" +
|
||||
"(table.copy (i32.const 19) (i32.const 20) (i32.const 5))",
|
||||
[e,e,3,1,4, 1,e,2,7,1, 8,e,7,e,7, 5,2,7,e,9, e,7,e,8,8, e,e,e,e,e]);
|
||||
|
||||
|
||||
// And now a simplified version of the above, for memory.{init,drop,copy}.
|
||||
|
||||
function gen_mem_mod_t(insn)
|
||||
{
|
||||
let t =
|
||||
`(module
|
||||
;; -------- Memories --------
|
||||
(memory (export "memory0") 1 1)
|
||||
;; -------- Memory initialisers --------
|
||||
(data (i32.const 2) "\\03\\01\\04\\01")
|
||||
(data passive "\\02\\07\\01\\08")
|
||||
(data (i32.const 12) "\\07\\05\\02\\03\\06")
|
||||
(data passive "\\05\\09\\02\\07\\06")
|
||||
|
||||
(func (export "testfn")
|
||||
${insn}
|
||||
;; There's no return value. The JS driver can just pull out the
|
||||
;; final memory and examine it.
|
||||
)
|
||||
)`;
|
||||
return t;
|
||||
};
|
||||
|
||||
function mem_test(instruction, expected_result_vector)
|
||||
{
|
||||
let mem_mod_t = gen_mem_mod_t(instruction);
|
||||
let mem_mod_b = wasmTextToBinary(mem_mod_t);
|
||||
|
||||
let inst = new Instance(new Module(mem_mod_b));
|
||||
inst.exports.testfn();
|
||||
let buf = new Uint8Array(inst.exports.memory0.buffer);
|
||||
|
||||
for (let i = 0; i < expected_result_vector.length; i++) {
|
||||
let expected = expected_result_vector[i];
|
||||
let actual = buf[i];
|
||||
assertEq(actual, expected,
|
||||
"mem_test fail: insn = '" + instruction + "', index = " +
|
||||
i + ", expected = " + expected + ", actual = " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
e = 0;
|
||||
|
||||
// This just gives the initial state of the memory, with its active
|
||||
// initialisers applied.
|
||||
mem_test("nop",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy non-zero over non-zero
|
||||
mem_test("(memory.copy (i32.const 13) (i32.const 2) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,3,1, 4,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy non-zero over zero
|
||||
mem_test("(memory.copy (i32.const 25) (i32.const 15) (i32.const 2))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, 3,6,e,e,e]);
|
||||
|
||||
// Copy zero over non-zero
|
||||
mem_test("(memory.copy (i32.const 13) (i32.const 25) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,e,e, e,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy zero over zero
|
||||
mem_test("(memory.copy (i32.const 20) (i32.const 22) (i32.const 4))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy zero and non-zero entries, non overlapping
|
||||
mem_test("(memory.copy (i32.const 25) (i32.const 1) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,3,1,e,e]);
|
||||
|
||||
// Copy zero and non-zero entries, overlapping, backwards
|
||||
mem_test("(memory.copy (i32.const 10) (i32.const 12) (i32.const 7))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, 7,5,2,3,6, e,e,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Copy zero and non-zero entries, overlapping, forwards
|
||||
mem_test("(memory.copy (i32.const 12) (i32.const 10) (i32.const 7))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,e,e,7, 5,2,3,6,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Passive init that overwrites all-zero entries
|
||||
mem_test("(memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4))",
|
||||
[e,e,3,1,4, 1,e,2,7,1, 8,e,7,5,2, 3,6,e,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Passive init that overwrites existing active-init-created entries
|
||||
mem_test("(memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3))",
|
||||
[e,e,3,1,4, 1,e,e,e,e, e,e,7,5,2, 9,2,7,e,e, e,e,e,e,e, e,e,e,e,e]);
|
||||
|
||||
// Perform active and passive initialisation and then multiple copies
|
||||
mem_test("(memory.init 1 (i32.const 7) (i32.const 0) (i32.const 4)) \n" +
|
||||
"memory.drop 1 \n" +
|
||||
"(memory.init 3 (i32.const 15) (i32.const 1) (i32.const 3)) \n" +
|
||||
"memory.drop 3 \n" +
|
||||
"(memory.copy (i32.const 20) (i32.const 15) (i32.const 5)) \n" +
|
||||
"(memory.copy (i32.const 21) (i32.const 29) (i32.const 1)) \n" +
|
||||
"(memory.copy (i32.const 24) (i32.const 10) (i32.const 1)) \n" +
|
||||
"(memory.copy (i32.const 13) (i32.const 11) (i32.const 4)) \n" +
|
||||
"(memory.copy (i32.const 19) (i32.const 20) (i32.const 5))",
|
||||
[e,e,3,1,4, 1,e,2,7,1, 8,e,7,e,7, 5,2,7,e,9, e,7,e,8,8, e,e,e,e,e]);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------//
|
||||
//---------------------------------------------------------------------//
|
||||
// Validation tests
|
||||
// Some further tests for memory.copy and memory.fill. First, validation
|
||||
// tests.
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Test helpers. Copied and simplified from binary.js.
|
Loading…
Reference in New Issue
Block a user