v8/test/mjsunit/wasm/indirect-call-non-zero-table.js
Andreas Haas 2d9ec0a420 Reland: [wasm][anyref] Add support of call-indirect for multiple tables
The reason for the revert was that Liftoff did not bail out on indirect
calls to tables other than table 0. Whenever the Liftoff code got
executed, the test would fail.

Original message:
With this CL it is possible to use any anyfunc table in call-indirect,
not just the first table.

The current implementation is based on runtime calls. This is just an
initial implementation which should be replaced by a
dispatch-table-based eventually. However, this implementation allows
us to move forward with the anyref proposal implementation.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: Iedd56ee7acb281441bca32ffd3dc7157203ee1ac
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1532072
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Auto-Submit: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60382}
2019-03-21 08:42:48 +00:00

203 lines
7.0 KiB
JavaScript

// Copyright 2019 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 --experimental-wasm-anyref --experimental-wasm-return-call
load("test/mjsunit/wasm/wasm-module-builder.js");
(function IndirectCallToNonZeroTable() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const placeholder = builder.addTable(kWasmAnyFunc, 3).index;
const table1 = builder.addTable(kWasmAnyFunc, 3).index;
const table2 = builder.addTable(kWasmAnyFunc, 5).index;
const sig_index = builder.addType(kSig_i_v);
const other_sig = builder.addType(kSig_i_i);
const v1 = 16;
const v2 = 26;
const v3 = 36;
const v4 = 46;
const v5 = 56;
const f_unreachable = builder.addFunction('unreachable', sig_index)
.addBody([kExprUnreachable]).index;
const f1 = builder.addFunction('f1', sig_index)
.addBody([kExprI32Const, v1])
.index;
const f2 = builder.addFunction('f2', sig_index)
.addBody([kExprI32Const, v2])
.index;
const f3 = builder.addFunction('f3', sig_index)
.addBody([kExprI32Const, v3])
.index;
const f4 = builder.addFunction('f4', sig_index)
.addBody([kExprI32Const, v4])
.index;
const f5 = builder.addFunction('f5', sig_index)
.addBody([kExprI32Const, v5])
.index;
builder.addFunction('call1', kSig_i_i)
.addBody([kExprGetLocal, 0, // function index
kExprCallIndirect, sig_index, table1])
.exportAs('call1');
builder.addFunction('return_call1', kSig_i_i)
.addBody([kExprGetLocal, 0, // function index
kExprReturnCallIndirect, sig_index, table1])
.exportAs('return_call1');
builder.addFunction('call2', kSig_i_i)
.addBody([kExprGetLocal, 0, // function index
kExprCallIndirect, sig_index, table2])
.exportAs('call2');
builder.addFunction('return_call2', kSig_i_i)
.addBody([kExprGetLocal, 0, // function index
kExprReturnCallIndirect, sig_index, table2])
.exportAs('return_call2');
builder.addFunction('call_invalid_sig', kSig_i_i)
.addBody([kExprGetLocal, 0, kExprGetLocal, 0, // function index + param
kExprCallIndirect, other_sig, table2])
.exportAs('call_invalid_sig');
builder.addFunction('return_call_invalid_sig', kSig_i_i)
.addBody([kExprGetLocal, 0, kExprGetLocal, 0, // function index + param
kExprReturnCallIndirect, other_sig, table2])
.exportAs('return_call_invalid_sig');
// We want to crash if we call through the table with index 0.
builder.addElementSegment(placeholder, 0, false,
[f_unreachable, f_unreachable, f_unreachable], false);
builder.addElementSegment(table1, 0, false, [f1, f2, f3], false);
// Keep one slot in table2 uninitialized. We should trap if we call it.
builder.addElementSegment(table2, 1, false,
[f_unreachable, f_unreachable, f4, f5], false);
const instance = builder.instantiate();
assertEquals(v1, instance.exports.call1(0));
assertEquals(v2, instance.exports.call1(1));
assertEquals(v3, instance.exports.call1(2));
assertTraps(kTrapFuncInvalid, () => instance.exports.call1(3));
assertEquals(v1, instance.exports.return_call1(0));
assertEquals(v2, instance.exports.return_call1(1));
assertEquals(v3, instance.exports.return_call1(2));
assertTraps(kTrapFuncInvalid, () => instance.exports.return_call1(3));
// Try to call through the uninitialized table entry.
assertTraps(kTrapFuncSigMismatch, () => instance.exports.call2(0));
assertEquals(v4, instance.exports.call2(3));
assertEquals(v5, instance.exports.call2(4));
assertTraps(kTrapFuncSigMismatch,
() => instance.exports.call_invalid_sig(4));
assertTraps(kTrapFuncSigMismatch, () => instance.exports.return_call2(0));
assertEquals(v4, instance.exports.return_call2(3));
assertEquals(v5, instance.exports.return_call2(4));
assertTraps(kTrapFuncSigMismatch,
() => instance.exports.return_call_invalid_sig(4));
})();
(function IndirectCallToImportedNonZeroTable() {
print(arguments.callee.name);
const table_size = 10;
const placeholder = new WebAssembly.Table(
{ initial: table_size, maximum: table_size, element: "anyfunc" });
const table = new WebAssembly.Table(
{ initial: table_size, maximum: table_size, element: "anyfunc" });
const builder = new WasmModuleBuilder();
builder.addImportedTable("m", "placeholder", table_size, table_size);
const t1 = builder.addImportedTable("m", "table", table_size, table_size);
// We initialize the module twice and put the function f1 in the table at
// the index defined by {g}. Thereby we can initialize the table at different
// slots for different instances. The function f1 also returns {g} so that we
// can see that actually different functions get called.
const g = builder.addImportedGlobal("m", "base", kWasmI32);
const sig_index = builder.addType(kSig_i_v);
const f1 = builder.addFunction("foo", sig_index)
.addBody([kExprGetGlobal, g, kExprI32Const, 12, kExprI32Add]);
builder.addFunction('call', kSig_i_i)
.addBody([kExprGetLocal, 0, // function index
kExprCallIndirect, sig_index, t1])
.exportAs('call');
builder.addElementSegment(t1, g, true, [f1.index], true);
const base1 = 3;
const base2 = 5;
const instance1 = builder.instantiate({
m: {
placeholder: placeholder,
table: table,
base: base1
}
});
const instance2 = builder.instantiate({
m: {
placeholder: placeholder,
table: table,
base: base2
}
});
assertEquals(base1 + 12, instance1.exports.call(base1));
assertEquals(base2 + 12, instance1.exports.call(base2));
assertEquals(base1 + 12, instance2.exports.call(base1));
assertEquals(base2 + 12, instance2.exports.call(base2));
})();
function js_div(a, b) { return (a / b) | 0; }
(function CallImportedFunction() {
let kTableSize = 10;
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let div = builder.addImport("q", "js_div", kSig_i_ii);
builder.addImportedTable("q", "placeholder", kTableSize, kTableSize);
let table_index = builder.addImportedTable("q", "table", kTableSize, kTableSize);
let g = builder.addImportedGlobal("q", "base", kWasmI32);
let sig_index = builder.addType(kSig_i_ii);
builder.addFunction("placeholder", sig_index)
.addBody([kExprGetLocal, 0]);
builder.addElementSegment(table_index, g, true, [div]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprI32Const, 55, // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallIndirect, 0, table_index]) // --
.exportAs("main");
let m = new WebAssembly.Module(builder.toBuffer());
let table = new WebAssembly.Table({
element: "anyfunc",
initial: kTableSize,
maximum: kTableSize
});
let placeholder = new WebAssembly.Table({
element: "anyfunc",
initial: kTableSize,
maximum: kTableSize
});
let instance = new WebAssembly.Instance(m, {
q: {
base: 0, table: table, placeholder: placeholder,
js_div: js_div
}
});
assertEquals(13, instance.exports.main(4, 0));
})();