v8/test/mjsunit/wasm/indirect-tables.js
Clemens Hammacher f29cae45ce [wasm] Fix emitting element section in wasm-module-builder.js
We were emitting the table index once in the element section instead of
once per element segment. This did not cause failures because we never
had more than one element segment.
This CL fixes this, and adds a test for more than one segment.

R=rossberg@chromium.org

Change-Id: I13923baf3dae383c72760286e24242d0ad55c4db
Reviewed-on: https://chromium-review.googlesource.com/527155
Reviewed-by: Andreas Rossberg <rossberg@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45857}
2017-06-12 12:45:36 +00:00

571 lines
18 KiB
JavaScript

// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function AddFunctions(builder) {
let sig_index = builder.addType(kSig_i_ii);
let mul = builder.addFunction("mul", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Mul // --
]);
let add = builder.addFunction("add", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add // --
]);
let sub = builder.addFunction("sub", sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Sub // --
]);
return {mul: mul, add: add, sub: sub};
}
function js_div(a, b) { return (a / b) | 0; }
(function ExportedTableTest() {
print("ExportedTableTest...");
let builder = new WasmModuleBuilder();
let d = builder.addImport("q", "js_div", kSig_i_ii);
let f = AddFunctions(builder);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
f.add.exportAs("blarg");
builder.setFunctionTableLength(10);
let g = builder.addImportedGlobal("q", "base", kWasmI32);
builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index,
f.sub.index,
d]);
builder.addExportOfKind("table", kExternalTable, 0);
let module = new WebAssembly.Module(builder.toBuffer());
for (let i = 0; i < 5; i++) {
print(" base = " + i);
let instance = new WebAssembly.Instance(module, {q: {base: i, js_div: js_div}});
main = instance.exports.main;
let table = instance.exports.table;
assertTrue(table instanceof WebAssembly.Table);
assertEquals(10, table.length);
for (let j = 0; j < i; j++) {
assertSame(null, table.get(j));
}
let mul = table.get(i+0);
let add = table.get(i+1);
let sub = table.get(i+2);
print(" mul=" + mul);
print(" add=" + add);
print(" sub=" + sub);
assertEquals("function", typeof mul);
assertEquals("function", typeof add);
assertEquals("function", typeof sub);
assertEquals(2, mul.length);
assertEquals(2, add.length);
assertEquals(2, sub.length);
assertEquals(String(f.add.index), add.name);
let exp_div = table.get(i+3);
assertEquals("function", typeof exp_div);
print(" js_div=" + exp_div);
// Should have a new, wrapped version of the import.
assertFalse(js_div == exp_div);
for (let j = i + 4; j < 10; j++) {
assertSame(null, table.get(j));
}
assertEquals(-33, mul(-11, 3));
assertEquals(4444444, add(3333333, 1111111));
assertEquals(-9999, sub(1, 10000));
assertEquals(-44, exp_div(-88.1, 2));
}
})();
(function ImportedTableTest() {
let kTableSize = 10;
print("ImportedTableTest...");
var builder = new WasmModuleBuilder();
let d = builder.addImport("q", "js_div", kSig_i_ii);
let f = AddFunctions(builder);
builder.setFunctionTableLength(kTableSize);
let g = builder.addImportedGlobal("q", "base", kWasmI32);
builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index,
f.sub.index,
d]);
builder.addExportOfKind("table", kExternalTable, 0);
let m1 = new WebAssembly.Module(builder.toBuffer());
var builder = new WasmModuleBuilder();
builder.addImportedTable("r", "table", kTableSize, kTableSize);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 33, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
let m2 = new WebAssembly.Module(builder.toBuffer());
// Run 5 trials at different table bases.
for (let i = 0; i < 5; i++) {
print(" base = " + i);
let i1 = new WebAssembly.Instance(m1, {q: {base: i, js_div: js_div}});
let table = i1.exports.table;
assertEquals(10, table.length);
let i2 = new WebAssembly.Instance(m2, {r: {table: table}});
let main = i2.exports.main;
for (var j = 0; j < i; j++) {
assertThrows(() => main(0, j));
assertSame(null, table.get(j));
}
// mul
assertEquals("function", typeof table.get(i+0));
assertEquals(0, main(0, i+0));
assertEquals(66, main(2, i+0));
// add
assertEquals("function", typeof table.get(i+1));
assertEquals(33, main(0, i+1));
assertEquals(38, main(5, i+1));
// sub
assertEquals("function", typeof table.get(i+2));
assertEquals(32, main(1, i+2));
assertEquals(28, main(5, i+2));
// div
assertEquals("function", typeof table.get(i+3));
assertEquals(8, main(4, i+3));
assertEquals(3, main(11, i+3));
for (var j = i + 4; j < (kTableSize + 5); j++) {
assertThrows(x => main(0, j));
if (j < kTableSize) assertSame(null, table.get(j));
}
}
})();
(function ImportedTableTest() {
let kTableSize = 10;
print("ManualTableTest...");
var builder = new WasmModuleBuilder();
let d = builder.addImport("q", "js_div", kSig_i_ii);
builder.addImportedTable("q", "table", kTableSize, kTableSize);
let g = builder.addImportedGlobal("q", "base", kWasmI32);
let f = AddFunctions(builder);
builder.addFunctionTableInit(g, true, [f.mul.index, f.add.index,
f.sub.index,
d]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 55, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, kTableZero]) // --
.exportAs("main");
let m2 = new WebAssembly.Module(builder.toBuffer());
// Run 5 trials at different table bases.
for (let i = 0; i < 5; i++) {
print(" base = " + i);
let table = new WebAssembly.Table({element: "anyfunc",
initial: kTableSize,
maximum: kTableSize});
assertEquals(10, table.length);
let i2 = new WebAssembly.Instance(m2, {q: {base: i, table: table,
js_div: js_div}});
let main = i2.exports.main;
for (var j = 0; j < i; j++) {
assertThrows(() => main(0, j));
assertSame(null, table.get(j));
}
// mul
assertEquals("function", typeof table.get(i+0));
assertEquals(0, main(0, i+0));
assertEquals(110, main(2, i+0));
// add
assertEquals("function", typeof table.get(i+1));
assertEquals(55, main(0, i+1));
assertEquals(60, main(5, i+1));
// sub
assertEquals("function", typeof table.get(i+2));
assertEquals(54, main(1, i+2));
assertEquals(50, main(5, i+2));
// div
assertEquals("function", typeof table.get(i+3));
assertEquals(13, main(4, i+3));
assertEquals(5, main(11, i+3));
for (var j = i + 4; j < (kTableSize + 5); j++) {
assertThrows(x => main(0, j));
if (j < kTableSize) assertSame(null, table.get(j));
}
}
})();
(function CumulativeTest() {
print("CumulativeTest...");
let kTableSize = 10;
let table = new WebAssembly.Table(
{element: "anyfunc", initial: kTableSize, maximum: kTableSize});
var builder = new WasmModuleBuilder();
builder.addImportedTable("x", "table", kTableSize, kTableSize);
let g = builder.addImportedGlobal("x", "base", kWasmI32);
let sig_index = builder.addType(kSig_i_v);
builder.addFunction("g", sig_index)
.addBody([
kExprGetGlobal, g
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprCallIndirect, sig_index, kTableZero]) // --
.exportAs("main");
builder.addFunctionTableInit(g, true, [g]);
let module = new WebAssembly.Module(builder.toBuffer());
for (var i = 0; i < kTableSize; i++) {
print(" base = " + i);
let instance = new WebAssembly.Instance(module, {x: {base: i, table: table}});
for (var j = 0; j < kTableSize; j++) {
let func = table.get(j);
if (j > i) {
assertSame(null, func);
assertTraps(kTrapFuncSigMismatch, () => instance.exports.main(j));
} else {
assertEquals("function", typeof func);
assertEquals(j, func());
assertEquals(j, instance.exports.main(j));
}
}
}
})();
(function TwoWayTest() {
print("TwoWayTest...");
let kTableSize = 3;
// Module {m1} defines the table and exports it.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_i);
builder.addType(kSig_i_ii);
var sig_index1 = builder.addType(kSig_i_v);
var f1 = builder.addFunction("f1", sig_index1)
.addBody([kExprI32Const, 11]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprCallIndirect, sig_index1, kTableZero]) // --
.exportAs("main");
builder.setFunctionTableLength(kTableSize);
builder.addFunctionTableInit(0, false, [f1.index]);
builder.addExportOfKind("table", kExternalTable, 0);
var m1 = new WebAssembly.Module(builder.toBuffer());
// Module {m2} imports the table and adds {f2}.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_ii);
var sig_index2 = builder.addType(kSig_i_v);
var f2 = builder.addFunction("f2", sig_index2)
.addBody([kExprI32Const, 22]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprCallIndirect, sig_index2, kTableZero]) // --
.exportAs("main");
builder.addImportedTable("z", "table", kTableSize, kTableSize);
builder.addFunctionTableInit(1, false, [f2.index], true);
var m2 = new WebAssembly.Module(builder.toBuffer());
assertFalse(sig_index1 == sig_index2);
var i1 = new WebAssembly.Instance(m1);
var i2 = new WebAssembly.Instance(m2, {z: {table: i1.exports.table}});
assertEquals(11, i1.exports.main(0));
assertEquals(11, i2.exports.main(0));
assertEquals(22, i1.exports.main(1));
assertEquals(22, i2.exports.main(1));
assertThrows(() => i1.exports.main(2));
assertThrows(() => i2.exports.main(2));
assertThrows(() => i1.exports.main(3));
assertThrows(() => i2.exports.main(3));
})();
(function MismatchedTableSize() {
print("MismatchedTableSize...");
let kTableSize = 5;
for (var expsize = 1; expsize < 4; expsize++) {
for (var impsize = 1; impsize < 4; impsize++) {
print(" expsize = " + expsize + ", impsize = " + impsize);
var builder = new WasmModuleBuilder();
builder.setFunctionTableLength(expsize);
builder.addExportOfKind("expfoo", kExternalTable, 0);
let m1 = new WebAssembly.Module(builder.toBuffer());
var builder = new WasmModuleBuilder();
builder.addImportedTable("y", "impfoo", impsize, impsize);
let m2 = new WebAssembly.Module(builder.toBuffer());
var i1 = new WebAssembly.Instance(m1);
// TODO(titzer): v8 currently requires import table size to match
// export table size.
var ffi = {y: {impfoo: i1.exports.expfoo}};
if (expsize == impsize) {
var i2 = new WebAssembly.Instance(m2, ffi);
} else {
assertThrows(() => new WebAssembly.Instance(m2, ffi));
}
}
}
})();
(function TableGrowBoundsCheck() {
print("TableGrowBoundsCheck");
var kMaxSize = 30, kInitSize = 5;
let table = new WebAssembly.Table({element: "anyfunc",
initial: kInitSize, maximum: kMaxSize});
var builder = new WasmModuleBuilder();
builder.addImportedTable("x", "table", kInitSize, kMaxSize);
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {x: {base: 1, table: table}});
for(var i = kInitSize; i < kMaxSize; i+=5) {
assertEquals(i, table.length);
for (var j = 0; j < i; j++) table.set(j, null);
for (var j = 0; j < i; j++) assertEquals(null, table.get(j));
assertThrows(() => table.set(i, null));
assertThrows(() => table.get(i));
assertEquals(i, table.grow(5));
}
assertEquals(30, table.length);
assertThrows(() => table.grow(1));
assertThrows(() => table.set(kMaxSize, null));
assertThrows(() => table.get(kMaxSize));
})();
(function CumulativeGrowTest() {
print("CumulativeGrowTest...");
let table = new WebAssembly.Table({
element: "anyfunc", initial: 10, maximum: 30});
var builder = new WasmModuleBuilder();
builder.addImportedTable("x", "table", 10, 30);
let g = builder.addImportedGlobal("x", "base", kWasmI32);
let sig_index = builder.addType(kSig_i_v);
builder.addFunction("g", sig_index)
.addBody([
kExprGetGlobal, g
]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprCallIndirect, sig_index, kTableZero]) // --
.exportAs("main");
builder.addFunctionTableInit(g, true, [g]);
let module = new WebAssembly.Module(builder.toBuffer());
var instances = [];
for (var i = 0; i < 10; i++) {
print(" base = " + i);
instances.push(new WebAssembly.Instance(
module, {x: {base: i, table: table}}));
}
for (var j = 0; j < 10; j++) {
let func = table.get(j);
assertEquals("function", typeof func);
assertEquals(j, func());
assertEquals(j, instances[j].exports.main(j));
}
assertEquals(10, table.grow(10));
// Verify that grow does not alter function behaviors
for (var j = 0; j < 10; j++) {
let func = table.get(j);
assertEquals("function", typeof func);
assertEquals(j, func());
assertEquals(j, instances[j].exports.main(j));
}
let new_builder = new WasmModuleBuilder();
new_builder.addExport("wasm", new_builder.addFunction("", kSig_v_v).addBody([]));
new_builder.addImportedTable("x", "table", 20, 30);
let new_module = new WebAssembly.Module(new_builder.toBuffer());
let instance = new WebAssembly.Instance(new_module, {x: {table: table}});
let new_func = instance.exports.wasm;
for (var j = 10; j < 20; j++) {
table.set(j, new_func);
let func = table.get(j);
assertEquals("function", typeof func);
assertSame(new_func, table.get(j));
}
assertThrows(() => table.grow(11));
})();
(function TestImportTooLarge() {
print("TestImportTooLarge...");
let builder = new WasmModuleBuilder();
builder.addImportedTable("t", "t", 1, 2);
// initial size is too large
assertThrows(() => builder.instantiate({t: {t: new WebAssembly.Table(
{element: "anyfunc", initial: 3, maximum: 3})}}));
// maximum size is too large
assertThrows(() => builder.instantiate({t: {t: new WebAssembly.Table(
{element: "anyfunc", initial: 1, maximum: 4})}}));
// no maximum
assertThrows(() => builder.instantiate({t: {t: new WebAssembly.Table(
{element: "anyfunc", initial: 1})}}));
})();
(function TableImportLargerThanCompiled() {
print("TableImportLargerThanCompiled...");
var kMaxSize = 30, kInitSize = 5;
var builder = new WasmModuleBuilder();
builder.addImportedTable("x", "table", 1, 35);
let table = new WebAssembly.Table({element: "anyfunc",
initial: kInitSize, maximum: kMaxSize});
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {x: {base: 1, table: table}});
for (var i = 0; i < kInitSize; ++i) table.set(i, null);
for (var i = 0; i < kInitSize; ++i) assertEquals(null, table.get(i));
assertThrows(() => table.set(kInitSize, null));
})();
(function ModulesShareTableAndGrow() {
print("ModulesShareTableAndGrow...");
let module1 = (() => {
let builder = new WasmModuleBuilder();
builder.addImportedTable("x", "table", 1, 35);
return new WebAssembly.Module(builder.toBuffer());
})();
let module2 = (() => {
let builder = new WasmModuleBuilder();
builder.addImportedTable("x", "table", 2, 40);
return new WebAssembly.Module(builder.toBuffer());
})();
var kMaxSize = 30, kInitSize = 5;
let table = new WebAssembly.Table({element: "anyfunc",
initial: kInitSize, maximum: kMaxSize});
let instance1 = new WebAssembly.Instance(
module1, {x: {base: 1, table: table}});
let instance2 = new WebAssembly.Instance(
module2, {x: {base: 1, table: table}});
for (var i = 0; i < kInitSize; ++i) table.set(i, null);
for (var i = 0; i < kInitSize; ++i) assertEquals(null, table.get(i));
assertThrows(() => table.set(kInitSize, null));
assertEquals(kInitSize, table.grow(5));
for (var i = 0; i < 2*kInitSize; ++i) table.set(i, null);
for (var i = 0; i < 2*kInitSize; ++i) assertEquals(null, table.get(i));
assertThrows(() => table.set(2*kInitSize, null));
// Try to grow past imported maximum
assertThrows(() => table.grow(21));
})();
(function MultipleElementSegments() {
let kTableSize = 10;
print("MultipleElementSegments...");
let mul = (a, b) => a * b;
let add = (a, b) => a + b;
let sub = (a, b) => a - b;
// Test 1 to 3 segments in the elements section.
// segment 1 sets [1, 2] to mul,
// segment 2 sets [2, 3, 4] to add,
// segment 3 sets [3, 4, 5, 6] to sub.
for (let num_segments = 1; num_segments < 4; ++num_segments) {
var builder = new WasmModuleBuilder();
builder.setFunctionTableLength(kTableSize);
builder.addExportOfKind("table", kExternalTable, 0);
let f = AddFunctions(builder);
let indexes = [f.mul.index, f.add.index, f.sub.index];
for (let i = 0; i < num_segments; ++i) {
let offset = i + 1;
let len = i + 2;
let index = indexes[i];
builder.addFunctionTableInit(offset, false, new Array(len).fill(index));
}
let instance = builder.instantiate();
let table = instance.exports.table;
assertEquals(kTableSize, table.length);
for (let i = 0; i < num_segments; ++i) {
let exp = i < 1 || i > 2 ? null : mul;
if (num_segments > 1 && i >= 2 && i <= 4) exp = add;
if (num_segments > 2 && i >= 3 && i <= 6) exp = sub;
if (!exp) {
assertSame(null, table.get(i));
} else {
assertEquals("function", typeof table.get(i));
assertEquals(exp(6, 3), table.get(i)(6, 3));
}
}
}
})();