Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

1
deps/v8/test/mjsunit/wasm/OWNERS vendored Normal file
View File

@ -0,0 +1 @@
file:../../../src/wasm/OWNERS

View File

@ -0,0 +1,317 @@
// Copyright 2015 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const JS = false; // for testing the tests.
const WRONG1 = 0x0DEDFACE;
const WRONG2 = 0x0DEDBABE;
const WRONG3 = 0x0DEDD011
function makeSelect(type, args, which) {
if (JS) {
// For testing the tests.
return function() {
var val = +arguments[which];
print(" " + val);
if (type == kWasmI32) return val | 0;
if (type == kWasmF32) return Math.fround(val);
if (type == kWasmF64) return val;
return undefined;
}
}
var builder = new WasmModuleBuilder();
var params = [];
for (var i = 0; i < args; i++) params.push(type);
builder.addFunction("select", makeSig(params, [type]))
.addBody([kExprLocalGet, which])
.exportFunc();
return builder.instantiate().exports.select;
}
const inputs = [
-1, 0, 2.2, 3.3, 3000.11, Infinity, -Infinity, NaN
];
(function TestInt1() {
print("i32 1(0)...");
var C = function(v) { return v | 0; }
var select1 = makeSelect(kWasmI32, 1, 0);
for (val of inputs) {
assertEquals(C(val), select1(val));
// under args
assertEquals(C(undefined), select1());
// over args
assertEquals(C(val), select1(val, WRONG1));
assertEquals(C(val), select1(val, WRONG1, WRONG2));
}
})();
(function TestInt2() {
print("i32 2(0)...");
var C = function(v) { return v | 0; }
var select = makeSelect(kWasmI32, 2, 0);
for (val of inputs) {
assertEquals(C(val), select(val, WRONG1));
// under args
assertEquals(C(undefined), select());
assertEquals(C(val), select(val));
// over args
assertEquals(C(val), select(val, WRONG1, WRONG2));
assertEquals(C(val), select(val, WRONG1, WRONG2, WRONG3));
}
print("i32 2(1)...");
var select = makeSelect(kWasmI32, 2, 1);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, val));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(val));
// over args
assertEquals(C(val), select(WRONG1, val));
assertEquals(C(val), select(WRONG1, val, WRONG2));
assertEquals(C(val), select(WRONG1, val, WRONG2, WRONG3));
}
})();
(function TestInt3() {
print("i32 3(0)...");
var C = function(v) { return v | 0; }
var select = makeSelect(kWasmI32, 3, 0);
for (val of inputs) {
assertEquals(C(val), select(val, WRONG1, WRONG2));
// under args
assertEquals(C(undefined), select());
assertEquals(C(val), select(val));
assertEquals(C(val), select(val, WRONG1));
// over args
assertEquals(C(val), select(val, WRONG1, WRONG2, WRONG3));
}
print("i32 3(1)...");
var select = makeSelect(kWasmI32, 3, 1);
for (val of inputs) {
assertEquals(val | 0, select(WRONG1, val, WRONG2));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(0xDEDFACE));
assertEquals(C(val), select(WRONG1, val));
// over args
assertEquals(C(val), select(WRONG1, val, WRONG2, WRONG3));
}
print("i32 3(2)...");
var select = makeSelect(kWasmI32, 3, 2);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, WRONG2, val));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(0xDEDFACE));
assertEquals(C(undefined), select(WRONG1, WRONG2));
// over args
assertEquals(C(val), select(WRONG1, WRONG2, val, WRONG3));
}
})();
(function TestFloat32_1() {
print("f32 1(0)...");
var C = function(v) { return Math.fround(v); }
var select1 = makeSelect(kWasmF32, 1, 0);
for (val of inputs) {
assertEquals(C(val), select1(val));
// under args
assertEquals(C(undefined), select1());
// over args
assertEquals(C(val), select1(val, WRONG1));
assertEquals(C(val), select1(val, WRONG1, WRONG2));
}
})();
(function TestFloat32_2() {
print("f32 2(0)...");
var C = function(v) { return Math.fround(v); }
var select = makeSelect(kWasmF32, 2, 0);
for (val of inputs) {
assertEquals(C(val), select(val, WRONG1));
// under args
assertEquals(C(undefined), select());
assertEquals(C(val), select(val));
// over args
assertEquals(C(val), select(val, WRONG1, WRONG2));
assertEquals(C(val), select(val, WRONG1, WRONG2, WRONG3));
}
print("f32 2(1)...");
var select = makeSelect(kWasmF32, 2, 1);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, val));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(val));
// over args
assertEquals(C(val), select(WRONG1, val));
assertEquals(C(val), select(WRONG1, val, WRONG2));
assertEquals(C(val), select(WRONG1, val, WRONG2, WRONG3));
}
})();
(function TestFloat32_2() {
print("f32 3(0)...");
var C = function(v) { return Math.fround(v); }
var select = makeSelect(kWasmF32, 3, 0);
for (val of inputs) {
assertEquals(C(val), select(val, WRONG1, WRONG2));
// under args
assertEquals(C(undefined), select());
assertEquals(C(val), select(val));
assertEquals(C(val), select(val, WRONG1));
// over args
assertEquals(C(val), select(val, WRONG1, WRONG2, WRONG3));
}
print("f32 3(1)...");
var select = makeSelect(kWasmF32, 3, 1);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, val, WRONG2));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(0xDEDFACE));
assertEquals(C(val), select(WRONG1, val));
// over args
assertEquals(C(val), select(WRONG1, val, WRONG2, WRONG3));
}
print("f32 3(2)...");
var select = makeSelect(kWasmF32, 3, 2);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, WRONG2, val));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(0xDEDFACE));
assertEquals(C(undefined), select(WRONG1, WRONG2));
// over args
assertEquals(C(val), select(WRONG1, WRONG2, val, WRONG3));
}
})();
(function TestFloat64_1() {
print("f64 1(0)...");
var C = function(v) { return +v; }
var select1 = makeSelect(kWasmF64, 1, 0);
for (val of inputs) {
assertEquals(C(val), select1(val));
// under args
assertEquals(C(undefined), select1());
// over args
assertEquals(C(val), select1(val, WRONG1));
assertEquals(C(val), select1(val, WRONG1, WRONG2));
}
})();
(function TestFloat64_2() {
print("f64 2(0)...");
var C = function(v) { return +v; }
var select = makeSelect(kWasmF64, 2, 0);
for (val of inputs) {
assertEquals(C(val), select(val, WRONG1));
// under args
assertEquals(C(undefined), select());
assertEquals(C(val), select(val));
// over args
assertEquals(C(val), select(val, WRONG1, WRONG2));
assertEquals(C(val), select(val, WRONG1, WRONG2, WRONG3));
}
print("f64 2(1)...");
var select = makeSelect(kWasmF64, 2, 1);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, val));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(val));
// over args
assertEquals(C(val), select(WRONG1, val));
assertEquals(C(val), select(WRONG1, val, WRONG2));
assertEquals(C(val), select(WRONG1, val, WRONG2, WRONG3));
}
})();
(function TestFloat64_2() {
print("f64 3(0)...");
var C = function(v) { return +v; }
var select = makeSelect(kWasmF64, 3, 0);
for (val of inputs) {
assertEquals(C(val), select(val, WRONG1, WRONG2));
// under args
assertEquals(C(undefined), select());
assertEquals(C(val), select(val));
assertEquals(C(val), select(val, WRONG1));
// over args
assertEquals(C(val), select(val, WRONG1, WRONG2, WRONG3));
}
print("f64 3(1)...");
var select = makeSelect(kWasmF64, 3, 1);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, val, WRONG2));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(0xDEDFACE));
assertEquals(C(val), select(WRONG1, val));
// over args
assertEquals(C(val), select(WRONG1, val, WRONG2, WRONG3));
}
print("f64 3(2)...");
var select = makeSelect(kWasmF64, 3, 2);
for (val of inputs) {
assertEquals(C(val), select(WRONG1, WRONG2, val));
// under args
assertEquals(C(undefined), select());
assertEquals(C(undefined), select(0xDEDFACE));
assertEquals(C(undefined), select(WRONG1, WRONG2));
// over args
assertEquals(C(val), select(WRONG1, WRONG2, val, WRONG3));
}
})();

View File

@ -0,0 +1,72 @@
// 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
function testAddGetter(object, name, val) {
Object.defineProperty(object, name, { get: function() { return val; } });
assertSame(val, object[name]);
}
function testAddGetterFails(object, name, val) {
function assign() {
Object.defineProperty(object, name, { get: function() { return val; } });
}
assertThrows(assign, TypeError);
}
function testAddGetterBothWays(object, name, val) {
print("Object.defineProperty");
Object.defineProperty(object, name, { get: function() { return val; } });
print("object.__defineGetter__");
object.__defineGetter__(name, () => val);
assertSame(val, object[name]);
}
function testFailToAddGetter(object, name, val) {
assertThrows(() => Object.defineProperty(object, name, { get: function() { return val; } }));
}
testAddGetter(testAddGetter, "name", 11);
function makeBuilder() {
var builder = new WasmModuleBuilder();
builder.addFunction("f", kSig_v_v)
.addBody([])
.exportFunc();
return builder;
}
(function TestAddGetterToFunction() {
print("TestAddGetterToFunction...");
var builder = makeBuilder();
var f = builder.instantiate().exports.f;
testAddGetterBothWays(f, "name", "foo");
testAddGetter(f, "blam", "baz");
})();
(function TestAddGetterToModule() {
print("TestAddGetterToModule...");
var builder = makeBuilder();
var module = new WebAssembly.Module(builder.toBuffer());
testAddGetter(module, "exports", 290);
testAddGetter(module, "xyz", new Object());
})();
(function TestAddGetterToInstance() {
print("TestAddGetterToInstance...");
var builder = makeBuilder();
var instance = builder.instantiate();
testAddGetter(instance, "exports", 290);
testAddGetter(instance, "xyz", new Object());
})();
(function TestAddGetterToExports() {
print("TestAddGetterToExports...");
var builder = makeBuilder();
var exports = builder.instantiate().exports;
testFailToAddGetter(exports, "f", 9834);
testAddGetterFails(exports, "nag", new Number(2));
})();

224
deps/v8/test/mjsunit/wasm/anyfunc.js vendored Normal file
View File

@ -0,0 +1,224 @@
// 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-gc
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testAnyFuncIdentityFunction() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_a_a)
.addBody([kExprLocalGet, 0])
.exportFunc();
const instance = builder.instantiate();
assertThrows(() => instance.exports.main(print), TypeError);
assertThrows(() => instance.exports.main({'hello': 'world'}), TypeError);
assertSame(
instance.exports.main, instance.exports.main(instance.exports.main));
})();
(function testPassAnyFuncToImportedFunction() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_v_a);
const imp_index = builder.addImport('q', 'func', sig_index);
builder.addFunction('main', sig_index)
.addBody([kExprLocalGet, 0, kExprCallFunction, imp_index])
.exportFunc();
const main = builder.instantiate({q: {func: checkFunction}}).exports.main;
function checkFunction(value) {
assertSame(main, value);
}
main(main);
})();
(function testPassAnyFuncWithGCWithLocals() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const ref_sig = builder.addType(kSig_v_a);
const void_sig = builder.addType(kSig_v_v);
const imp_index = builder.addImport('q', 'func', ref_sig);
const gc_index = builder.addImport('q', 'gc', void_sig);
// First call the gc, then check if the object still exists.
builder.addFunction('main', ref_sig)
.addLocals(kWasmAnyFunc, 10)
.addBody([
kExprLocalGet, 0,
kExprLocalSet, 1, // Set local
kExprLocalGet, 0,
kExprLocalSet, 2, // Set local
kExprLocalGet, 0,
kExprLocalSet, 3, // Set local
kExprLocalGet, 0,
kExprLocalSet, 4, // Set local
kExprLocalGet, 0,
kExprLocalSet, 5, // Set local
kExprLocalGet, 0,
kExprLocalSet, 6, // Set local
kExprLocalGet, 0,
kExprLocalSet, 7, // Set local
kExprLocalGet, 0,
kExprLocalSet, 8, // Set local
kExprLocalGet, 0,
kExprLocalSet, 9, // Set local
kExprLocalGet, 0,
kExprLocalSet, 10, // Set local
kExprCallFunction, gc_index, // call gc
kExprLocalGet, 9,
kExprCallFunction, imp_index // call import
])
.exportFunc();
const main =
builder.instantiate({q: {func: checkFunction, gc: gc}}).exports.main;
function checkFunction(value) {
assertSame(main, value);
}
main(main);
})();
(function testPassAnyFuncWithGC() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const ref_sig = builder.addType(kSig_v_a);
const void_sig = builder.addType(kSig_v_v);
const imp_index = builder.addImport('q', 'func', ref_sig);
const gc_index = builder.addImport('q', 'gc', void_sig);
// First call the gc, then check if the object still exists.
builder.addFunction('main', ref_sig)
.addBody([
kExprCallFunction, gc_index, // call gc
kExprLocalGet, 0, kExprCallFunction, imp_index // call import
])
.exportFunc();
function checkFunction(value) {
assertSame(main, value);
}
const main =
builder.instantiate({q: {func: checkFunction, gc: gc}}).exports.main;
main(main);
})();
(function testPassAnyFuncWithGCInWrapper() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const kSig_a_iai =
makeSig([kWasmI32, kWasmAnyFunc, kWasmI32], [kWasmAnyFunc]);
const sig_index = builder.addType(kSig_a_iai);
builder.addFunction('main', sig_index)
.addBody([kExprLocalGet, 1])
.exportFunc();
const main = builder.instantiate().exports.main;
const triggerGCParam = {
valueOf: () => {
gc();
return 17;
}
};
const result = main(triggerGCParam, main, triggerGCParam);
assertSame(main, result);
})();
(function testAnyFuncDefaultValue() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addLocals(kWasmAnyFunc, 1)
.addBody([kExprLocalGet, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testAssignNullToAnyFuncLocal() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_a);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kAnyFuncCode, kExprLocalSet, 0, kExprLocalGet, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main(main));
})();
(function testImplicitReturnNullAsAnyFunc() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kAnyFuncCode])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testExplicitReturnNullAsAnyFunc() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kAnyFuncCode, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testRefFuncOutOfBounds() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_a_v).addBody([kExprRefFunc, 10]);
assertThrows(() => builder.toModule(), WebAssembly.CompileError);
})();
(function testRefFuncIsCallable() {
print(arguments.callee.name);
const expected = 54;
const builder = new WasmModuleBuilder();
const function_index = builder.addFunction('hidden', kSig_i_v)
.addBody([kExprI32Const, expected])
.index;
builder.addDeclarativeElementSegment([function_index]);
builder.addFunction('main', kSig_a_v)
.addBody([kExprRefFunc, function_index])
.exportFunc();
const instance = builder.instantiate();
assertEquals(expected, instance.exports.main()());
})();
(function testRefFuncPreservesIdentity() {
print(arguments.callee.name);
const expected = 54;
const builder = new WasmModuleBuilder();
const foo = builder.addFunction('foo', kSig_i_v)
.addBody([kExprI32Const, expected])
.exportFunc();
builder.addDeclarativeElementSegment([foo.index]);
builder.addFunction('main', kSig_a_v)
.addBody([kExprRefFunc, foo.index])
.exportFunc();
const instance = builder.instantiate();
assertSame(instance.exports.foo, instance.exports.main());
})();

View File

@ -0,0 +1,462 @@
// Copyright 2023 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestArrayFillImmutable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI32, false);
// Parameters: array, starting index, value, length.
builder.addFunction(
"array_fill",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kExprLocalGet, 2, kExprLocalGet, 3,
kGCPrefix, kExprArrayFill, array])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/immediate array type #0 is immutable/);
})();
(function TestArrayFill() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let array = builder.addArray(wasmRefNullType(struct), true);
let array16 = builder.addArray(kWasmI16, true);
builder.addFunction(
"make_array", makeSig([kWasmI32], [wasmRefType(array)]))
.addBody([kExprLocalGet, 0, kGCPrefix, kExprArrayNewDefault, array])
.exportFunc();
builder.addFunction(
"array_get", makeSig([wasmRefNullType(array), kWasmI32], [kWasmI32]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayGet, array,
kGCPrefix, kExprStructGet, struct, 0])
.exportFunc();
// Parameters: array, starting index, value in struct, length.
builder.addFunction(
"array_fill",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kExprLocalGet, 2, kGCPrefix, kExprStructNew, struct,
kExprLocalGet, 3, kGCPrefix, kExprArrayFill, array])
.exportFunc();
builder.addFunction("array_fill_i16", kSig_i_v).exportFunc()
.addLocals(wasmRefType(array16), 1)
.addBody([
kExprI32Const, 4, // Array length for allocation.
kGCPrefix, kExprArrayNewDefault, array16,
kExprLocalTee, 0, // Array (for fill).
kExprI32Const, 0, // Offset (for fill).
kExprI32Const, 42, // Value (for fill).
kExprI32Const, 4, // Length (for fill).
kGCPrefix, kExprArrayFill, array16,
kExprLocalGet, 0, // Array (for get).
kExprI32Const, 0, // Index (for get).
kGCPrefix, kExprArrayGetS, array16,
]);
let wasm = builder.instantiate().exports;
assertTraps(kTrapNullDereference, () => wasm.array_fill(null, 10, 20, 30));
let array_obj = wasm.make_array(8);
assertTraps(kTrapArrayOutOfBounds,
() => wasm.array_fill(array_obj, 5, 42, 4));
// Out-of-bounds array.fill traps even if length is 0, if the index is greater
// than array.len.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.array_fill(array_obj, 10, 42, 0));
// Overflow of (index + length) traps.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.array_fill(array_obj, 5, 42, -1));
wasm.array_fill(array_obj, 2, 42, 3);
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 0));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 1));
assertEquals(42, wasm.array_get(array_obj, 2));
assertEquals(42, wasm.array_get(array_obj, 3));
assertEquals(42, wasm.array_get(array_obj, 4));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 5));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 6));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 7));
// Index = array.len and length = 0 works, it just does nothing.
wasm.array_fill(array_obj, 8, 42, 0)
wasm.array_fill(array_obj, 4, 54, 2);
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 0));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 1));
assertEquals(42, wasm.array_get(array_obj, 2));
assertEquals(42, wasm.array_get(array_obj, 3));
assertEquals(54, wasm.array_get(array_obj, 4));
assertEquals(54, wasm.array_get(array_obj, 5));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 6));
assertTraps(kTrapNullDereference, () => wasm.array_get(array_obj, 7));
assertEquals(42, wasm.array_fill_i16());
})();
(function TestArrayNewNonNullable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let array = builder.addArray(wasmRefType(struct), true);
builder.addFunction(
"make_array", makeSig([wasmRefType(struct), kWasmI32],
[wasmRefType(array)]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayNew, array])
.exportFunc();
builder.addFunction(
"array_get", makeSig([wasmRefNullType(array), kWasmI32],
[wasmRefType(struct)]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayGet, array])
.exportFunc();
builder.addFunction(
"make_struct", makeSig([], [wasmRefType(struct)]))
.addBody([kGCPrefix, kExprStructNewDefault, struct])
.exportFunc();
let wasm = builder.instantiate().exports;
let length = 50; // Enough to go through initialization builtin.
let struct_obj = wasm.make_struct();
let array_obj = wasm.make_array(struct_obj, length);
for (let i = 0; i < length; i++) {
assertEquals(struct_obj, wasm.array_get(array_obj, i));
}
})();
(function TestArrayInitData() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI16, true);
builder.addMemory(10, 10);
let passive = builder.addPassiveDataSegment([0, 1, 2, 3, 4, 5]);
let active = builder.addActiveDataSegment(0, [kExprI32Const, 0],
[6, 7, 8, 9]);
builder.addFunction(
"make_array", makeSig([kWasmI32], [wasmRefType(array)]))
.addBody([kExprLocalGet, 0, kGCPrefix, kExprArrayNewDefault, array])
.exportFunc();
builder.addFunction(
"init_passive",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitData, array, passive])
.exportFunc();
builder.addFunction(
"init_active",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitData, array, active])
.exportFunc();
builder.addFunction(
"array_get", makeSig([wasmRefNullType(array), kWasmI32], [kWasmI32]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayGetS, array])
.exportFunc();
builder.addFunction("data_drop", kSig_v_v)
.addBody([kNumericPrefix, kExprDataDrop, passive])
.exportFunc();
let wasm = builder.instantiate().exports;
let array_length = 10;
assertTraps(kTrapNullDereference, () => wasm.init_passive(null, 0, 0, 0));
let array_obj = wasm.make_array(array_length);
for (i = 0; i < array_length; i++) {
assertEquals(0, wasm.array_get(array_obj, i));
}
// Does nothing.
wasm.init_active(array_obj, 0, 0, 0);
// Active segments count as dropped.
assertTraps(kTrapDataSegmentOutOfBounds,
() => wasm.init_active(array_obj, 0, 0, 1));
// Loading 0 bytes from the end of data segment does nothing.
wasm.init_passive(array_obj, 0, 6, 0);
// Loading 0 bytes to the end of the array does nothing.
wasm.init_passive(array_obj, array_length, 0, 0);
// Loading 0 bytes beyond the end of data segment traps.
assertTraps(kTrapDataSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 0, 7, 0));
// Loading 0 bytes beyond the end of array traps.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, array_length + 1, 1, 1));
// Out-of-bounds data segment.
assertTraps(kTrapDataSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 1, 2, 3));
// Out-of-bounds data segment with out-of-smi-range segment offset.
assertTraps(kTrapDataSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 1, 0x80000000, 0));
// Out-of-bounds array.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, array_length - 1, 0, 2));
// Out-of-bounds array with out-of-smi-range length.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, 0, 0, 0x80000000));
// Load [[1, 2], [3, 4]] at index 5 of array.
wasm.init_passive(array_obj, 5, 1, 2);
assertEquals(0, wasm.array_get(array_obj, 0));
assertEquals(0, wasm.array_get(array_obj, 1));
assertEquals(0, wasm.array_get(array_obj, 2));
assertEquals(0, wasm.array_get(array_obj, 3));
assertEquals(0, wasm.array_get(array_obj, 4));
// Bytes will be loaded in little-endian order.
assertEquals(0x0201, wasm.array_get(array_obj, 5));
assertEquals(0x0403, wasm.array_get(array_obj, 6));
assertEquals(0, wasm.array_get(array_obj, 7));
assertEquals(0, wasm.array_get(array_obj, 8));
assertEquals(0, wasm.array_get(array_obj, 9));
// Now drop the segment.
wasm.data_drop();
assertTraps(kTrapDataSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 5, 1, 2));
})();
(function TestArrayInitDataLengthOverflow() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI64, true);
let passive = builder.addPassiveDataSegment([0, 1, 2, 3, 4, 5]);
builder.addFunction(
"make_array", makeSig([kWasmI32], [wasmRefType(array)]))
.addBody([kExprLocalGet, 0, kGCPrefix, kExprArrayNewDefault, array])
.exportFunc();
builder.addFunction(
"init_passive",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitData, array, passive])
.exportFunc();
let wasm = builder.instantiate().exports;
let array_length = 10;
let array_obj = wasm.make_array(array_length);
// Out-of-bounds array: length is within Smi range, but computation of length
// in bytes causes 32-bit overflow.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, array_length - 1, 0,
0x20000000));
})();
(function TestArrayInitDataImmutable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI16, false);
let passive = builder.addPassiveDataSegment([0, 1, 2, 3, 4, 5]);
builder.addFunction(
"init_passive",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitData, array, passive])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/array.init_data can only be used with mutable arrays/);
})();
(function TestArrayInitElem() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig = builder.addType(kSig_i_i);
let array = builder.addArray(wasmRefNullType(sig), true);
let table = builder.addTable(wasmRefNullType(sig), 10, 10);
let elem1 = builder.addFunction("succ", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add]);
let elem2 = builder.addFunction("pred", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
let passive = builder.addPassiveElementSegment(
[[kExprRefNull, sig], [kExprRefFunc, elem1.index],
[kExprRefFunc, elem2.index], [kExprRefFunc, elem1.index]],
wasmRefNullType(sig));
let active = builder.addActiveElementSegment(
table, [kExprI32Const, 0], [[kExprRefFunc, elem1.index]],
wasmRefNullType(sig));
builder.addFunction(
"make_array", makeSig([kWasmI32], [wasmRefType(array)]))
.addBody([kExprLocalGet, 0, kGCPrefix, kExprArrayNewDefault, array])
.exportFunc();
builder.addFunction(
"init_passive",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitElem, array, passive])
.exportFunc();
builder.addFunction(
"init_active",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitElem, array, active])
.exportFunc();
builder.addFunction(
"array_get", makeSig([wasmRefNullType(array), kWasmI32],
[wasmRefNullType(sig)]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayGet, array])
.exportFunc();
builder.addFunction("elem_drop", kSig_v_v)
.addBody([kNumericPrefix, kExprElemDrop, passive])
.exportFunc();
let wasm = builder.instantiate().exports;
let array_length = 10;
assertTraps(kTrapNullDereference, () => wasm.init_passive(null, 0, 0, 0));
let array_obj = wasm.make_array(array_length);
for (i = 0; i < array_length; i++) {
assertEquals(null, wasm.array_get(array_obj, i));
}
// Does nothing.
wasm.init_active(array_obj, 0, 0, 0);
// Active segments count as dropped.
assertTraps(kTrapElementSegmentOutOfBounds,
() => wasm.init_active(array_obj, 0, 0, 1));
// Loading 0 elements from the end of element segment does nothing.
wasm.init_passive(array_obj, 0, 4, 0);
// Loading 0 elements to the end of the array does nothing.
wasm.init_passive(array_obj, array_length, 0, 0);
// Loading 0 elements beyond the end of data segment traps.
assertTraps(kTrapElementSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 0, 5, 0));
// Loading 0 elements beyond the end of array traps.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, array_length + 1, 1, 1));
// Out-of-bounds element segment.
assertTraps(kTrapElementSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 1, 2, 3));
// Out-of-bounds element segment with out-of-smi-bounds segment offset.
assertTraps(kTrapElementSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 1, 0x80000000, 0));
// Out-of-bounds array.
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, array_length - 1, 0, 2));
// Out-of-bounds array with out-of-smi-bounds length
assertTraps(kTrapArrayOutOfBounds,
() => wasm.init_passive(array_obj, array_length - 1, 0,
0x80000000));
// Load the last three elements of the element segment at index 5.
wasm.init_passive(array_obj, 5, 1, 3);
assertEquals(null, wasm.array_get(array_obj, 0));
assertEquals(null, wasm.array_get(array_obj, 1));
assertEquals(null, wasm.array_get(array_obj, 2));
assertEquals(null, wasm.array_get(array_obj, 3));
assertEquals(null, wasm.array_get(array_obj, 4));
assertEquals(11, wasm.array_get(array_obj, 5)(10));
assertEquals(9, wasm.array_get(array_obj, 6)(10));
assertEquals(11, wasm.array_get(array_obj, 7)(10));
assertEquals(null, wasm.array_get(array_obj, 8));
assertEquals(null, wasm.array_get(array_obj, 9));
// Now drop the segment.
wasm.elem_drop();
assertTraps(kTrapElementSegmentOutOfBounds,
() => wasm.init_passive(array_obj, 5, 1, 3));
})();
(function TestArrayInitElemImmutable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmFuncRef, false);
let elem1 = builder.addFunction("succ", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add]);
let elem2 = builder.addFunction("pred", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub]);
let passive = builder.addPassiveDataSegment([elem1.index, elem2.index,
elem1.index, elem2.index]);
builder.addFunction(
"init_passive",
makeSig([wasmRefNullType(array), kWasmI32, kWasmI32, kWasmI32], []))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2,
kExprLocalGet, 3, kGCPrefix, kExprArrayInitElem, array, passive])
.exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/array.init_elem can only be used with mutable arrays/);
})();
(function TestArrayCopyLargeI64Array() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI64, true);
builder.addFunction(
// initial value, length, index to check
"main", makeSig([kWasmI64, kWasmI32, kWasmI32], [kWasmI64]))
.addBody([kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayNew, array,
kExprLocalGet, 2, kGCPrefix, kExprArrayGet, array])
.exportFunc();
let instance = builder.instantiate();
assertEquals(1234n, instance.exports.main(1234n, 1000, 0));
assertEquals(-2345n, instance.exports.main(-2345n, 2000, 1000));
assertEquals(42n, instance.exports.main(42n, 2000, 1999));
})();

View File

@ -0,0 +1,193 @@
// Copyright 2021 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: --no-liftoff
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// This is benchmark to investigate at which point it is more efficient to call
// a memcpy-based builtin for array.copy, rather than copying
// element-by-element.
// How to run:
// - Set {iterations} to a high number to get better measurements
// - Change the value of {length} to find point at which the builtin becomes
// faster.
// - Change {array_type} if you want to test different types.
// Right now, the limit is found to be around 10.
(function ArrayCopyBenchmark() {
let array_length = 10;
let iterations = 1;
var builder = new WasmModuleBuilder();
let struct_index = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI8, false)]);
// Also try wasmRefNullType(struct_index), other numeric types.
let array_type = kWasmI32;
var array_index = builder.addArray(array_type, true);
var from = builder.addGlobal(wasmRefNullType(array_index), true, false);
var to = builder.addGlobal(wasmRefNullType(array_index), true, false);
builder.addFunction("init", kSig_v_v)
.addBody([
...wasmI32Const(array_length),
kGCPrefix, kExprArrayNewDefault, array_index,
kExprGlobalSet, from.index,
...wasmI32Const(array_length),
kGCPrefix, kExprArrayNewDefault, array_index,
kExprGlobalSet, to.index
])
.exportFunc();
builder.addFunction("array_copy", kSig_v_v)
.addLocals(kWasmI32, 1)
.addBody([
kExprLoop, kWasmVoid,
kExprGlobalGet, to.index,
...wasmI32Const(0),
kExprGlobalGet, from.index,
...wasmI32Const(0),
...wasmI32Const(array_length),
kGCPrefix, kExprArrayCopy, array_index, array_index,
// Outer loop: run everything {iterations} times.
kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 0,
kExprLocalGet, 0, ...wasmI32Const(iterations), kExprI32LtS,
kExprBrIf, 0,
kExprEnd])
.exportFunc();
builder.addFunction("loop_copy", kSig_v_v)
.addLocals(wasmRefType(array_index), 2)
.addLocals(kWasmI32, 2)
.addBody([
kExprLoop, kWasmVoid,
...wasmI32Const(0),
kExprLocalSet, 2,
kExprGlobalGet, from.index, kExprRefAsNonNull, kExprLocalSet, 0,
kExprGlobalGet, to.index, kExprRefAsNonNull, kExprLocalSet, 1,
kExprLoop, kWasmVoid,
kExprLocalGet, 1, // array
kExprLocalGet, 2, // index
// value
kExprLocalGet, 0, kExprLocalGet, 2,
kGCPrefix, kExprArrayGet, array_index,
// array.set
kGCPrefix, kExprArraySet, array_index,
// index++
kExprLocalGet, 2, kExprI32Const, 1, kExprI32Add, kExprLocalSet, 2,
// if (index < array_length) goto loop;
kExprLocalGet, 2, ...wasmI32Const(array_length), kExprI32LtU,
kExprBrIf, 0,
kExprEnd,
// Outer loop: run everything {iterations} times.
kExprLocalGet, 3, kExprI32Const, 3, kExprI32Add, kExprLocalSet, 3,
kExprLocalGet, 3, ...wasmI32Const(iterations), kExprI32LtS,
kExprBrIf, 0,
kExprEnd])
.exportFunc();
var instance = builder.instantiate({});
instance.exports.init();
print("Array length: " + array_length + ", #iterations: " + iterations);
{
let before = Date.now();
instance.exports.array_copy();
let after = Date.now();
print("array.copy: " + (after - before) + "ms");
}
{
let before = Date.now();
instance.exports.loop_copy();
let after = Date.now();
print("loop copy: " + (after - before) + "ms");
}
})();
// The following two benchmarks measure the efficiency of array allocation.
// Change the three first parameters of each benchmarks to measure different
// array sizes, iterations, and element types.
(function ArrayNewDefaultBenchmark() {
let array_length = 10;
let iterations = 1
let test_object_type = false;
var builder = new WasmModuleBuilder();
let struct_index = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI8, true)]);
let array_type = test_object_type ? wasmRefNullType(struct_index) : kWasmI32;
var array_index = builder.addArray(array_type, true);
let array_new = builder.addFunction(
"array_new", makeSig([], [wasmRefNullType(array_index)]))
.addBody([
...wasmI32Const(array_length),
kGCPrefix, kExprArrayNewDefault, array_index])
.exportFunc();
builder.addFunction("loop_array_new", kSig_v_v)
.addLocals(wasmRefNullType(array_index), 1)
.addLocals(kWasmI32, 1)
.addBody([
kExprLoop, kWasmVoid,
kExprCallFunction, array_new.index,
kExprLocalSet, 0,
kExprLocalGet, 1, kExprI32Const, 1, kExprI32Add, kExprLocalTee, 1,
...wasmI32Const(iterations), kExprI32LtS,
kExprBrIf, 0,
kExprEnd])
.exportFunc();
var instance = builder.instantiate({});
print(`Type: ${test_object_type ? "object" : "i32"}, array length: ` +
`${array_length}, #iterations: ${iterations}`);
let before = Date.now();
instance.exports.loop_array_new();
let after = Date.now();
print(`array.new_default: ${after - before} ms`);
})();
(function ArrayNewBenchmark() {
let array_length = 10;
let iterations = 1;
let test_object_type = true;
var builder = new WasmModuleBuilder();
let struct_index = builder.addStruct([makeField(kWasmI32, true),
makeField(kWasmI8, true)]);
let array_type = test_object_type ? wasmRefNullType(struct_index) : kWasmI32;
var array_index = builder.addArray(array_type, true);
let array_new = builder.addFunction(
"array_new", makeSig([], [wasmRefNullType(array_index)]))
.addBody([
...(test_object_type ? [kGCPrefix, kExprStructNewDefault, struct_index]
: wasmI32Const(10)),
...wasmI32Const(array_length),
kGCPrefix, kExprArrayNew, array_index])
.exportFunc();
builder.addFunction("loop_array_new", kSig_v_v)
.addLocals(wasmRefNullType(array_index), 1)
.addLocals(kWasmI32, 1)
.addBody([
kExprLoop, kWasmVoid,
kExprCallFunction, array_new.index,
kExprLocalSet, 0,
kExprLocalGet, 1, kExprI32Const, 1, kExprI32Add, kExprLocalTee, 1,
...wasmI32Const(iterations), kExprI32LtS,
kExprBrIf, 0,
kExprEnd])
.exportFunc();
var instance = builder.instantiate({});
print(`Type: ${test_object_type ? "object" : "i32"}, array length: ` +
`${array_length}, #iterations: ${iterations}`);
let before = Date.now();
instance.exports.loop_array_new();
let after = Date.now();
print(`array.new: ${after - before} ms`);
})();

View File

@ -0,0 +1,61 @@
// Copyright 2024 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestArrayCopyErrorReasons() {
var builder = new WasmModuleBuilder();
let array = builder.addArray(kWasmI32, true);
// The array.copy has two different reasons to trap. (null dereference, oob)
// All compilers should emit the same reason in all cases. Which one doesn't
// really matter and could be changed in the future, e.g. if it improves
// generated code.
builder.addFunction("tgtNullSrcNull", kSig_v_v)
.addBody([
kExprRefNull, array, // target array (nullptr)
kExprI32Const, 2, // target index
kExprRefNull, array, // source array (nullptr)
kExprI32Const, 2, // source index
kExprI32Const, 2, // length
kGCPrefix, kExprArrayCopy, array, array,
]).exportFunc();
builder.addFunction("tgtOobSrcNull", kSig_v_v)
.addBody([
kExprI32Const, 4, kGCPrefix, kExprArrayNewDefault, array, // target array
kExprI32Const, 42, // target index (out of bounds)
kExprRefNull, array, // source array (nullptr)
kExprI32Const, 2, // source index
kExprI32Const, 2, // length
kGCPrefix, kExprArrayCopy, array, array,
]).exportFunc();
builder.addFunction("tgtNullSrcOob", kSig_v_v)
.addBody([
kExprRefNull, array, // target array (nullptr)
kExprI32Const, 2, // target index
kExprI32Const, 4, kGCPrefix, kExprArrayNewDefault, array, // source array
kExprI32Const, 42, // source index (out of bounds)
kExprI32Const, 2, // length
kGCPrefix, kExprArrayCopy, array, array,
]).exportFunc();
builder.addFunction("tgtOobSrcOob", kSig_v_v)
.addBody([
kExprI32Const, 4, kGCPrefix, kExprArrayNewDefault, array, // target array
kExprI32Const, 42, // target index (out of bounds)
kExprI32Const, 4, kGCPrefix, kExprArrayNewDefault, array, // source array
kExprI32Const, 42, // source index (out of bounds)
kExprI32Const, 2, // length
kGCPrefix, kExprArrayCopy, array, array,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertTraps(kTrapNullDereference, wasm.tgtNullSrcNull);
assertTraps(kTrapArrayOutOfBounds, wasm.tgtOobSrcNull);
assertTraps(kTrapNullDereference, wasm.tgtNullSrcOob);
assertTraps(kTrapArrayOutOfBounds, wasm.tgtOobSrcOob);
})();

View File

@ -0,0 +1,74 @@
// Copyright 2024 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-gc
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestRepeatedArrayFillWithGC() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI64, true)]);
let array = builder.addArray(wasmRefNullType(struct), true);
let gcFunc = builder.addImport("m", "gc", kSig_v_v);
// This needs to be large enough, so that we call into C++ instead of running
// a loop in the generated code. (see kArrayNewMinimumSizeForMemSet)
let arrayLength = 50;
builder.addFunction("create", makeSig([kWasmI64], [wasmRefNullType(array)]))
.addLocals(wasmRefNullType(array), 1)
.addLocals(wasmRefNullType(struct), 1)
.addBody([
// Create the fill value (young generation).
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct,
kExprLocalTee, 2,
// Create an array.
// As the size is larger than kArrayNewMinimumSizeForMemSet (16), this will
// call to C++ for initializing the elements. The fill value is passed as a
// pointer. There were two issues with that:
// 1) For a reference value on pointer-compressed builds, this emitted a
// __ ChangeInt32ToInt64() operation causing the value to become
// untagged.
// 2) This untagged value was then stored in an untagged stack slot. So any
// GC would not visit this stack slot. Note that this bug doesn't seem
// possible to be triggered as the stack slot is not GVN'ed (so the
// second fill initializes a separate stack slot).
kExprI32Const, arrayLength,
kGCPrefix, kExprArrayNew, array,
kExprDrop,
// Trigger a gc.
kExprCallFunction, gcFunc,
// Create another array.
// The initialization emitted the same __ ChangeInt32ToInt64() for the fill
// value (the struct.new above). Due to GVN the compiler decides to reuse
// the instruction from above. However, due to the GC in between this
// "int64" now contains an outdated pointer pointing into the previous
// half-space for the young generation. (Note that anyways we should pass
// the decompressed pointer, zeroing out the upper half doesn't fit to our
// calling convention, it just happens to not be an issue as we only use the
// value to store it inside the tagged (compressed) slots inside the array.)
kExprLocalGet, 2,
kExprI32Const, arrayLength,
kGCPrefix, kExprArrayNew, array,
]).exportFunc();
builder.addFunction("get",
makeSig([wasmRefNullType(array), kWasmI32], [kWasmI64]))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kGCPrefix, kExprArrayGet, array,
kGCPrefix, kExprStructGet, struct, 0,
]).exportFunc();
let wasm = builder.instantiate({m: {gc}}).exports;
let arr = wasm.create(43n);
for (let i = 0; i < arrayLength; ++i) {
assertEquals(43n, wasm.get(arr, i));
}
})();

View File

@ -0,0 +1,416 @@
// Copyright 2022 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestArrayNewElem() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
function makeStruct(element) {
return [...wasmI32Const(element),
kGCPrefix, kExprStructNew, struct_type_index];
}
let table = builder.addTable(kWasmAnyRef, 10, 10);
let elems = [10, -10, 42, 55];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
[kExprRefNull, struct_type_index]],
struct_type);
let active_segment = builder.addActiveElementSegment(
table, wasmI32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
struct_type);
function generator(name, segment) {
builder.addFunction(name, makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0, // offset
kExprLocalGet, 1, // length
kGCPrefix, kExprArrayNewElem, array_type_index, segment,
kExprLocalGet, 2, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kGCPrefix, kExprStructGet, struct_type_index, 0])
.exportFunc()
}
// Respective segment elements should be pointer-identical.
builder.addFunction("identical", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprI32Const, 0, // offset
kExprLocalGet, 0, // length
kGCPrefix, kExprArrayNewElem, array_type_index, passive_segment,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kExprI32Const, 0, // offset
kExprLocalGet, 0, // length
kGCPrefix, kExprArrayNewElem, array_type_index, passive_segment,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kExprRefEq])
.exportFunc()
generator("init_and_get", passive_segment);
generator("init_and_get_active", active_segment);
builder.addFunction("drop", kSig_v_v)
.addBody([kNumericPrefix, kExprElemDrop, passive_segment])
.exportFunc();
let instance = builder.instantiate();
let init_and_get = instance.exports.init_and_get;
let init_and_get_active = instance.exports.init_and_get_active;
// Initializing from a passive segment works. The third element is null, so
// we get a null dereference.
assertEquals(elems[0], init_and_get(0, 3, 0));
assertEquals(elems[1], init_and_get(0, 3, 1));
assertTraps(kTrapNullDereference, () => init_and_get(0, 3, 2));
// The array has the correct length.
assertTraps(kTrapArrayOutOfBounds, () => init_and_get(0, 3, 3));
// Too large arrays are disallowed, in and out of Smi range.
assertTraps(kTrapArrayTooLarge, () => init_and_get(0, 1000000000, 10));
assertTraps(kTrapArrayTooLarge, () => init_and_get(0, 1 << 31, 10));
// Element is out of bounds.
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get(0, 5, 0));
// Element index out of Smi range.
assertTraps(kTrapElementSegmentOutOfBounds,
() => init_and_get(0x80000000, 0, 0));
// Respective segment elements should be pointer-identical.
assertEquals(1, instance.exports.identical(3, 0));
// Now drop the segment.
instance.exports.drop();
// A 0-length array should still be created...
assertTraps(kTrapArrayOutOfBounds, () => init_and_get(0, 0, 0));
// ... but not a longer one.
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get(0, 1, 0));
// Same holds for an active segment.
assertTraps(kTrapArrayOutOfBounds, () => init_and_get_active(0, 0, 0));
assertTraps(kTrapElementSegmentOutOfBounds,
() => init_and_get_active(0, 1, 0));
})();
// TODO(14034): Reenable when we have constant array.new_elem.
/*
(function TestArrayNewElemConstant() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmRefNullType(array_type_index);
function makeStruct(element) {
return [...wasmI32Const(element),
kGCPrefix, kExprStructNew, struct_type_index];
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10, 42, 55];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
[kExprRefNull, struct_type_index]],
struct_type);
let active_segment = builder.addActiveElementSegment(
0, wasmI32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[[...wasmI32Const(0), ...wasmI32Const(3),
kGCPrefix, kExprArrayNewElem,
array_type_index, passive_segment],
[...wasmI32Const(0), ...wasmI32Const(0),
kGCPrefix, kExprArrayNewElem,
array_type_index, active_segment]],
array_type);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 2,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
builder.addFunction("table_get", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0, // offset in table
kExprTableGet, table,
kGCPrefix, kExprRefCast, array_type_index,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kGCPrefix, kExprStructGet, struct_type_index, 0])
.exportFunc()
builder.addFunction("drop", kSig_v_v)
.addBody([kNumericPrefix, kExprElemDrop, passive_segment])
.exportFunc();
let instance = builder.instantiate();
// First, initialize the table.
instance.exports.init();
let table_get = instance.exports.table_get;
// Initializing from a passive segment works. The third element is null, so
// we get a null dereference.
assertEquals(elems[0], table_get(0, 0));
assertEquals(elems[1], table_get(0, 1));
assertTraps(kTrapNullDereference, () => table_get(0, 2));
// The array has the correct length.
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
// The array generated from the active segment should have length 0.
assertTraps(kTrapArrayOutOfBounds, () => table_get(1, 0));
// Now drop the segment with the array elements and reload the table.
instance.exports.drop();
instance.exports.init();
// Nothing should change: a constant expression should not observe the dropped
// segments.
assertEquals(elems[0], table_get(0, 0));
assertEquals(elems[1], table_get(0, 1));
assertTraps(kTrapNullDereference, () => table_get(0, 2));
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
})();
*/
(function TestArrayNewElemMistypedSegment() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let passive_segment = builder.addPassiveElementSegment([
[kExprRefNull, array_type_index]],
wasmRefNullType(array_type_index));
builder.addFunction("mistyped", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprI32Const, 0, // offset
kExprLocalGet, 0, // length
kGCPrefix, kExprArrayNewElem, array_type_index,
passive_segment,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kGCPrefix, kExprStructGet, struct_type_index, 0])
.exportFunc()
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/segment type.*is not a subtype of array element type.*/);
})();
// TODO(14034): Reenable when we have constant array.new_elem.
/*
// Element segments are defined after globals, so currently it is not valid
// to refer to an element segment in the global section.
(function TestArrayNewFixedFromElemInGlobal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let passive_segment = builder.addPassiveElementSegment([
[kExprRefNull, struct_type_index]],
struct_type_index);
builder.addGlobal(
wasmRefNullType(array_type_index), false, false,
[...wasmI32Const(0), ...wasmI32Const(1),
kGCPrefix, kExprArrayNewElem,
array_type_index, passive_segment]);
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/invalid element segment index/);
})();
(function TestArrayNewElemConstantArrayTooLarge() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmRefNullType(array_type_index);
function makeStruct(element) {
return [...wasmI32Const(element),
kGCPrefix, kExprStructNew, struct_type_index];
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
[kExprRefNull, struct_type_index]],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[[...wasmI32Const(0), ...wasmI32Const(1 << 30),
kGCPrefix, kExprArrayNewElem,
array_type_index, passive_segment]],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapArrayTooLarge, () => instance.exports.init());
})();
(function TestArrayNewElemConstantElementSegmentOutOfBounds() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmRefNullType(array_type_index);
function makeStruct(element) {
return [...wasmI32Const(element),
kGCPrefix, kExprStructNew, struct_type_index];
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
[kExprRefNull, struct_type_index]],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[[...wasmI32Const(0), ...wasmI32Const(10),
kGCPrefix, kExprArrayNewElem,
array_type_index, passive_segment]],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
})();
(function TestArrayNewElemConstantActiveSegment() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmRefNullType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmRefNullType(array_type_index);
function makeStruct(element) {
return [...wasmI32Const(element),
kGCPrefix, kExprStructNew, struct_type_index];
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10];
let active_segment = builder.addActiveElementSegment(
table, wasmI32Const(0),
[makeStruct(elems[0]), makeStruct(elems[1]),
[kExprRefNull, struct_type_index]],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[[...wasmI32Const(0), ...wasmI32Const(3),
kGCPrefix, kExprArrayNewElem,
array_type_index, active_segment]],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
let instance = builder.instantiate();
// An active segment counts as having 0 length.
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
})();
*/
(function TestArrayNewData() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let array_type_index = builder.addArray(kWasmI16, true);
let dummy_byte = 0xff;
let element_0 = 1000;
let element_1 = -2222;
let data_segment = builder.addPassiveDataSegment(
[dummy_byte, element_0 & 0xff, (element_0 >> 8) & 0xff,
element_1 & 0xff, (element_1 >> 8) & 0xff]);
// TODO(14034): Reenable when we have constant array.new_data.
/*
let global = builder.addGlobal(
wasmRefType(array_type_index), true, false,
[...wasmI32Const(1), ...wasmI32Const(2),
kGCPrefix, kExprArrayNewData, array_type_index, data_segment],
builder);
builder.addFunction("global_get", kSig_i_i)
.addBody([
kExprGlobalGet, global.index,
kExprLocalGet, 0,
kGCPrefix, kExprArrayGetS, array_type_index])
.exportFunc();
*/
// parameters: (segment offset, array length, array index)
builder.addFunction("init_from_data", kSig_i_iii)
.addBody([
kExprLocalGet, 0, kExprLocalGet, 1,
kGCPrefix, kExprArrayNewData,
array_type_index, data_segment,
kExprLocalGet, 2,
kGCPrefix, kExprArrayGetS, array_type_index])
.exportFunc();
builder.addFunction("drop_segment", kSig_v_v)
.addBody([kNumericPrefix, kExprDataDrop, data_segment])
.exportFunc();
let instance = builder.instantiate();
// TODO(14034): Reenable when we have constant array.new_elem.
// assertEquals(element_0, instance.exports.global_get(0));
// assertEquals(element_1, instance.exports.global_get(1));
let init = instance.exports.init_from_data;
assertEquals(element_0, init(1, 2, 0));
assertEquals(element_1, init(1, 2, 1));
assertTraps(kTrapArrayTooLarge, () => init(1, 1000000000, 0));
assertTraps(kTrapDataSegmentOutOfBounds, () => init(2, 2, 0));
instance.exports.drop_segment();
assertTraps(kTrapDataSegmentOutOfBounds, () => init(1, 2, 0));
})();

View File

@ -0,0 +1,26 @@
// 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.
(function TestCopyBug() {
// This was tickling a register allocation issue with
// idiv in embenchen/copy.
function asmModule(){
'use asm';
function func() {
var ret = 0;
var x = 1, y = 0, z = 0;
var a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0;
do {
y = (x + 0) | 0;
z = (y | 0) % 2 | 0;
ret = (y + z + a + b + c + d + e + f + g) | 0;
} while(0);
return ret | 0;
}
return { func: func };
}
var wasm = asmModule();
var js = eval('(' + asmModule.toString().replace('use asm', '') + ')')();
assertEquals(js.func(), wasm.func());
})();

View File

@ -0,0 +1,27 @@
// 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: --validate-asm --allow-natives-syntax
(function TestDeoptimizeArgMismatch() {
function deopt() {
%DeoptimizeFunction(test);
}
function Module(global, env, buffer) {
"use asm";
var deopt = env.deopt;
function _main(i4, i5) {
i4 = i4 | 0;
i5 = i5 | 0;
deopt();
return i5 | 0;
}
return {'_main': _main}
}
function test() {
var wasm = Module(null, {'deopt': deopt});
wasm._main(0, 0, 0);
}
test();
})();

View File

@ -0,0 +1,147 @@
// 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: --validate-asm
var filename = '(?:[^ ]+/)?test/mjsunit/wasm/asm-wasm-exception-in-tonumber.js';
filename = filename.replace(/\//g, '[/\\\\]');
function verifyStack(frames, expected) {
assertTrue(frames.length >= expected.length, 'too few frames');
print('frames on detailed stack (' + frames.length + '):');
frames.forEach((fr, i) => print('[' + i + '] ' + fr));
expected.forEach(function(exp, i) {
assertEquals(
exp[0], frames[i].getFunctionName(), '[' + i + '].getFunctionName()');
assertEquals(
exp[1], frames[i].getColumnNumber(), '[' + i + '].getColumnNumber()');
});
}
function verifyPreformattedStack(e, expected_lines) {
print('preformatted stack: ' + e.stack);
var lines = e.stack.split('\n');
assertTrue(lines.length >= expected_lines.length, 'too few lines');
for (var i = 0; i < expected_lines.length; ++i) {
assertMatches(expected_lines[i], lines[i], 'line ' + i);
}
}
function sym(return_sym) {
if (return_sym) return Symbol();
throw Error("user-thrown");
}
function generateAsmJsInteger(stdlib, foreign) {
'use asm';
var sym = foreign.sym;
function callSym(i) {
i=i|0;
return sym(i|0) | 0;
}
return callSym;
}
function generateAsmJsDouble(stdlib, foreign) {
'use asm';
var sym = foreign.sym;
function callSym(i) {
i=i|0;
return +sym(i|0);
}
return callSym;
}
function testHelper(use_asm_js, check_detailed, expected, input, source) {
if (check_detailed) {
Error.prepareStackTrace = (error, frames) => frames;
} else {
delete Error.prepareStackTrace;
}
var fn_code = '(' + source.toString() + ')({}, {sym: sym})';
if (!use_asm_js) fn_code = fn_code.replace('use asm', '');
//print('executing:\n' + fn_code);
var asm_js_fn = eval(fn_code);
try {
asm_js_fn(input);
} catch (e) {
if (check_detailed) {
verifyStack(e.stack, expected);
} else {
verifyPreformattedStack(e, expected);
}
}
}
function testAll(expected_stack, expected_frames, input, source) {
for (use_asm_js = 0; use_asm_js <= 1; ++use_asm_js) {
for (test_detailed = 0; test_detailed <= 1; ++test_detailed) {
print('\nConfig: asm ' + use_asm_js + '; detailed ' + test_detailed);
testHelper(
use_asm_js, test_detailed,
test_detailed ? expected_frames : expected_stack, input, source);
}
}
}
(function testStackForThrowAtIntegerCall() {
var expected_stack = [
'^Error: user-thrown$',
'^ *at sym \\(' + filename + ':\\d+:9\\)$',
'^ *at callSym \\(eval at testHelper \\(' + filename +
':\\d+:19\\), <anonymous>:\\d+:12\\)$',
];
var expected_frames = [
// function pos
[ "sym", 9],
[ "callSym", 12],
];
testAll(expected_stack, expected_frames, 0, generateAsmJsInteger);
})();
(function testStackForThrowAtIntegerConversion() {
var expected_stack = [
'^TypeError: Cannot convert a Symbol value to a number$',
'^ *at callSym \\(eval at testHelper \\(' + filename +
':\\d+:19\\), <anonymous>:\\d+:21\\)$',
];
var expected_frames = [
// function pos
[ "callSym", 21],
];
testAll(expected_stack, expected_frames, 1, generateAsmJsInteger);
})();
(function testStackForThrowAtDoubleCall() {
var expected_stack = [
'^Error: user-thrown$',
'^ *at sym \\(' + filename + ':\\d+:9\\)$',
'^ *at callSym \\(eval at testHelper \\(' + filename +
':\\d+:19\\), <anonymous>:\\d+:13\\)$',
];
var expected_frames = [
// function pos
[ "sym", 9],
[ "callSym", 13],
];
testAll(expected_stack, expected_frames, 0, generateAsmJsDouble);
})();
(function testStackForThrowAtDoubleConversion() {
var expected_stack = [
'^TypeError: Cannot convert a Symbol value to a number$',
'^ *at callSym \\(eval at testHelper \\(' + filename +
':\\d+:19\\), <anonymous>:\\d+:12\\)$',
];
var expected_frames = [
// function pos
[ "callSym", 12],
];
testAll(expected_stack, expected_frames, 1, generateAsmJsDouble);
})();

View File

@ -0,0 +1,145 @@
// 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: --validate-asm --allow-natives-syntax
var selectedTest = undefined;
//selectedTest = 16;
function skip(a) {
return selectedTest != undefined ? a != selectedTest : false;
}
const assign_in_stmt = [
"if (E) =",
"if (=) E",
"if (E) E; else =",
"for (=; E; S) S",
"for (E; =; S) S",
"for (E; E; =) E",
"for (E; E; E) =",
"do { = } while(E)",
"do { S } while (=)",
];
const assign_in_expr = [
"i32_func(=) | 0",
"(=) ? E : E",
"E ? (=) : E",
"E ? E : (=)",
"(=) + E",
"E + (=)",
"imul(=, E)",
"imul(E, =)",
"~(=)",
"(=) | 0",
"(=), E",
"E, (=)",
"E, E, (=)",
"E, (=), E",
"(=), E, E",
];
const stdlib = {
Math: Math,
Int8Array: Int8Array,
Int16Array: Int16Array,
Int32Array: Int32Array,
Uint8Array: Uint8Array,
Uint16Array: Uint16Array,
Uint32Array: Uint32Array,
Float32Array: Float32Array,
Float64Array: Float64Array,
};
const buffer = new ArrayBuffer(65536);
// Template for a module.
function MODULE_TEMPLATE(stdlib, foreign, buffer) {
"use asm";
var imul = stdlib.Math.imul;
var fround = stdlib.Math.fround;
var M = new stdlib.Int32Array(buffer);
var G = 0;
function void_func() {}
function i32_func(a) {
a = a | 0;
return a | 0;
}
FUNC_DECL
return {main: main};
}
// Template for main function.
{
function main(i32, f32, f64) {
i32 = i32 | 0;
f32 = fround(f32);
f64 = +f64;
FUNC_BODY
}
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib, {}, buffer);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
var test = 0;
function DoTheTests(expr, assign, stmt) {
// ==== Expression assignment tests ========================================
for (let e of assign_in_expr) {
if (skip(++test)) continue;
var orig = e;
e = e.replace(/=/g, assign);
e = e.replace(/E/g, expr);
e = e.replace(/S/g, stmt);
var str = main.toString().replace("FUNC_BODY", "return (" + e + ") | 0;");
var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str);
doTest(asm_source, "(" + test + ") " + e);
}
// ==== Statement assignment tests =========================================
for (let e of assign_in_stmt) {
if (skip(++test)) continue;
var orig = e;
e = e.replace(/=/g, assign);
e = e.replace(/E/g, expr);
e = e.replace(/S/g, stmt);
var str = main.toString().replace("FUNC_BODY", e + "; return 0;");
var asm_source = MODULE_TEMPLATE.toString().replace("FUNC_DECL", str);
doTest(asm_source, "(" + test + ") " + e);
}
function doTest(asm_source, orig) {
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
print("Testing JS: " + orig);
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
expect(js_module);
print("Testing ASMJS: " + orig);
var asmfunc = eval("(" + asm_source + ")");
var asm_module = asmfunc(stdlib, {}, buffer);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
function expect(module) { module.main(0, 0, 0); print(" ok"); return true; }
}
DoTheTests("(i32 | 0)", "i32 = 0", "void_func()");
DoTheTests("G", "G = 0", "void_func()");
DoTheTests("G", "G = 0", "G");
DoTheTests("(M[0] | 0)", "M[0] = 0", "void_func()");

View File

@ -0,0 +1,218 @@
// 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: --validate-asm --allow-natives-syntax
function WrapInAsmModule(func) {
function MODULE_NAME(stdlib) {
"use asm";
var fround = stdlib.Math.fround;
var Math_ceil = stdlib.Math.ceil;
var Math_floor = stdlib.Math.floor;
var Math_sqrt = stdlib.Math.sqrt;
var Math_abs = stdlib.Math.abs;
var Math_min = stdlib.Math.min;
var Math_max = stdlib.Math.max;
FUNC_BODY
return {main: FUNC_NAME};
}
var source = MODULE_NAME.toString()
.replace(/MODULE_NAME/g, func.name + "_module")
.replace(/FUNC_BODY/g, func.toString())
.replace(/FUNC_NAME/g, func.name);
return eval("(" + source + ")");
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
var stdlib = {Math: Math};
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
var fround = Math.fround;
var Math_ceil = Math.ceil;
var Math_floor = Math.floor;
var Math_sqrt = Math.sqrt;
var Math_abs = Math.abs;
var Math_min = Math.min;
var Math_max = Math.max;
function f32_add(a, b) {
a = fround(a);
b = fround(b);
return fround(fround(a) + fround(b));
}
function f32_sub(a, b) {
a = fround(a);
b = fround(b);
return fround(fround(a) - fround(b));
}
function f32_mul(a, b) {
a = fround(a);
b = fround(b);
return fround(fround(a) * fround(b));
}
function f32_div(a, b) {
a = fround(a);
b = fround(b);
return fround(fround(a) / fround(b));
}
function f32_ceil(a) {
a = fround(a);
return fround(Math_ceil(fround(a)));
}
function f32_floor(a) {
a = fround(a);
return fround(Math_floor(fround(a)));
}
function f32_sqrt(a) {
a = fround(a);
return fround(Math_sqrt(fround(a)));
}
function f32_abs(a) {
a = fround(a);
return fround(Math_abs(fround(a)));
}
function f32_min(a, b) {
a = fround(a);
b = fround(b);
return fround(Math_min(fround(a), fround(b)));
}
function f32_max(a, b) {
a = fround(a);
b = fround(b);
return fround(Math_max(fround(a), fround(b)));
}
function f32_eq(a, b) {
a = fround(a);
b = fround(b);
if (fround(a) == fround(b)) {
return 1;
}
return 0;
}
function f32_ne(a, b) {
a = fround(a);
b = fround(b);
if (fround(a) != fround(b)) {
return 1;
}
return 0;
}
function f32_lt(a, b) {
a = fround(a);
b = fround(b);
if (fround(a) < fround(b)) {
return 1;
}
return 0;
}
function f32_lteq(a, b) {
a = fround(a);
b = fround(b);
if (fround(a) <= fround(b)) {
return 1;
}
return 0;
}
function f32_gt(a, b) {
a = fround(a);
b = fround(b);
if (fround(a) > fround(b)) {
return 1;
}
return 0;
}
function f32_gteq(a, b) {
a = fround(a);
b = fround(b);
if (fround(a) >= fround(b)) {
return 1;
}
return 0;
}
function f32_neg(a) {
a = fround(a);
return fround(-a);
}
var inputs = [
0, 1,
NaN,
Infinity,
-Infinity,
2147483646,
2147483647,
2147483648,
2147483649,
4026531840, // 0xf0000000
4294967293, // 0xfffffffd
4294967295, // 0xffffffff
-0, -1,
-2147483646,
-2147483647,
-2147483648,
-2147483649,
0.1,
1.1e-2,
1.6e-13
];
var funcs = [
f32_add, f32_sub, f32_mul, f32_div, f32_ceil, f32_floor, f32_sqrt, f32_abs,
f32_min, f32_max, f32_eq, f32_ne, f32_lt, f32_lteq, f32_gt, f32_gteq, f32_neg
];
(function () {
for (func of funcs) {
RunAsmJsTest(WrapInAsmModule(func), function (module) {
if (func.length == 1) {
for (a of inputs) {
assertEquals(func(a), module.main(a));
assertEquals(func(a / 11), module.main(a / 11));
assertEquals(func(a / 430.9), module.main(a / 430.9));
assertEquals(func(a / -31.1), module.main(a / -31.1));
}
} else {
for (a of inputs) {
for (b of inputs) {
assertEquals(func(a, b), module.main(a, b));
assertEquals(func(a / 11, b), module.main(a / 11, b));
assertEquals(func(a, b / 420.9), module.main(a, b / 420.9));
assertEquals(func(a / -31.1, b), module.main(a / -31.1, b));
}
}
}
});
}
})();

View File

@ -0,0 +1,281 @@
// 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: --validate-asm --allow-natives-syntax
function WrapInAsmModule(func) {
function MODULE_NAME(stdlib) {
"use asm";
var Math_ceil = stdlib.Math.ceil;
var Math_floor = stdlib.Math.floor;
var Math_sqrt = stdlib.Math.sqrt;
var Math_abs = stdlib.Math.abs;
var Math_min = stdlib.Math.min;
var Math_max = stdlib.Math.max;
var Math_acos = stdlib.Math.acos;
var Math_asin = stdlib.Math.asin;
var Math_atan = stdlib.Math.atan;
var Math_cos = stdlib.Math.cos;
var Math_sin = stdlib.Math.sin;
var Math_tan = stdlib.Math.tan;
var Math_exp = stdlib.Math.exp;
var Math_log = stdlib.Math.log;
var Math_atan2 = stdlib.Math.atan2;
FUNC_BODY
return {main: FUNC_NAME};
}
var source = MODULE_NAME.toString()
.replace(/MODULE_NAME/g, func.name + "_module")
.replace(/FUNC_BODY/g, func.toString())
.replace(/FUNC_NAME/g, func.name);
return eval("(" + source + ")");
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
var stdlib = {Math: Math};
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
const Math_ceil = Math.ceil;
const Math_floor = Math.floor;
const Math_sqrt = Math.sqrt;
const Math_abs = Math.abs;
const Math_min = Math.min;
const Math_max = Math.max;
const Math_acos = Math.acos;
const Math_asin = Math.asin;
const Math_atan = Math.atan;
const Math_cos = Math.cos;
const Math_sin = Math.sin;
const Math_tan = Math.tan;
const Math_exp = Math.exp;
const Math_log = Math.log;
const Math_atan2 = Math.atan2;
function f64_add(a, b) {
a = +a;
b = +b;
return +(+a + +b);
}
function f64_sub(a, b) {
a = +a;
b = +b;
return +(+a - +b);
}
function f64_mul(a, b) {
a = +a;
b = +b;
return +(+a * +b);
}
function f64_div(a, b) {
a = +a;
b = +b;
return +(+a / +b);
}
function f64_eq(a, b) {
a = +a;
b = +b;
if (+a == +b) {
return 1;
}
return 0;
}
function f64_ne(a, b) {
a = +a;
b = +b;
if (+a != +b) {
return 1;
}
return 0;
}
function f64_lt(a, b) {
a = +a;
b = +b;
if (+a < +b) {
return 1;
}
return 0;
}
function f64_lteq(a, b) {
a = +a;
b = +b;
if (+a <= +b) {
return 1;
}
return 0;
}
function f64_gt(a, b) {
a = +a;
b = +b;
if (+a > +b) {
return 1;
}
return 0;
}
function f64_gteq(a, b) {
a = +a;
b = +b;
if (+a >= +b) {
return 1;
}
return 0;
}
function f64_ceil(a) {
a = +a;
return +(Math_ceil(+a));
}
function f64_floor(a) {
a = +a;
return +(Math_floor(+a));
}
function f64_sqrt(a) {
a = +a;
return +(Math_sqrt(+a));
}
function f64_abs(a) {
a = +a;
return +(Math_abs(+a));
}
function f64_min(a, b) {
a = +a;
b = +b;
return +(Math_min(+a, +b));
}
function f64_max(a, b) {
a = +a;
b = +b;
return +(Math_max(+a, +b));
}
function f64_acos(a) {
a = +a;
return +Math_acos(+a);
}
function f64_asin(a) {
a = +a;
return +Math_asin(+a);
}
function f64_atan(a) {
a = +a;
return +Math_atan(+a);
}
function f64_cos(a) {
a = +a;
return +Math_cos(+a);
}
function f64_sin(a) {
a = +a;
return +Math_sin(+a);
}
function f64_tan(a) {
a = +a;
return +Math_tan(+a);
}
function f64_exp(a) {
a = +a;
return +Math_exp(+a);
}
function f64_log(a) {
a = +a;
return +Math_log(+a);
}
function f64_atan2(a, b) {
a = +a;
b = +b;
return +Math_atan2(+a, +b);
}
function f64_neg(a) {
a = +a;
return +(-a);
}
var inputs = [
0, 1,
NaN,
Infinity,
-Infinity,
2147483646,
2147483647,
2147483648,
2147483649,
4026531840, // 0xf0000000
4294967293, // 0xfffffffd
4294967295, // 0xffffffff
-0, -1,
-2147483646,
-2147483647,
-2147483648,
-2147483649,
0.1,
1.1e-2,
1.6e-13
];
var funcs = [
f64_add, f64_sub, f64_mul, f64_div, f64_eq, f64_ne, f64_lt,
f64_lteq, f64_gt, f64_gteq, f64_ceil, f64_floor, f64_sqrt, f64_abs,
f64_neg, f64_min, f64_max, f64_acos, f64_asin, f64_atan, f64_cos,
f64_sin, f64_tan, f64_exp, f64_log, f64_atan2,
];
(function () {
for (func of funcs) {
RunAsmJsTest(WrapInAsmModule(func), function (module) {
if (func.length == 1) {
for (a of inputs) {
assertEquals(func(a), module.main(a));
assertEquals(func(a / 10), module.main(a / 10));
assertEquals(func(a / 440.9), module.main(a / 440.9));
assertEquals(func(a / -33.1), module.main(a / -33.1));
}
} else {
for (a of inputs) {
for (b of inputs) {
assertEquals(func(a, b), module.main(a, b));
assertEquals(func(a / 10, b), module.main(a / 10, b));
assertEquals(func(a, b / 440.9), module.main(a, b / 440.9));
assertEquals(func(a / -33.1, b), module.main(a / -33.1, b));
}
}
}
});
}
})();

View File

@ -0,0 +1,236 @@
// 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: --validate-asm --allow-natives-syntax
const stdlib = {
Math: Math,
Int8Array: Int8Array,
Int16Array: Int16Array,
Int32Array: Int32Array,
Uint8Array: Uint8Array,
Uint16Array: Uint16Array,
Uint32Array: Uint32Array,
Float32Array: Float32Array,
Float64Array: Float64Array,
};
const buffer = new ArrayBuffer(65536);
const BASE = 1000000000;
const OOB_INDEXES = [
buffer.byteLength,
buffer.byteLength + 1,
buffer.byteLength + 2,
buffer.byteLength + 3,
buffer.byteLength + 4,
buffer.byteLength + 5,
buffer.byteLength + 6,
buffer.byteLength + 7,
buffer.byteLength + 8,
buffer.byteLength + 9,
buffer.byteLength + 10,
0x80000000,
0x80000004,
0xF0000000,
0xFFFFFFFF,
0xFFFFFFFE,
-1, -2, -3, -4, -5, -6, -7, -8
];
function resetBuffer() {
var view = new Int32Array(buffer);
for (var i = 0; i < view.length; i++) {
view[i] = BASE | (i << 2);
}
}
resetBuffer();
function checkView(view, load, shift) {
for (var i = 0; i < 300; i++) {
assertEquals(view[i >> shift], load(i));
}
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib, {}, buffer);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib, {}, buffer);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
function LoadAt_i32(stdlib, foreign, buffer) {
"use asm";
var HEAP32 = new stdlib.Int32Array(buffer);
function load(a) {
a = a | 0;
return HEAP32[a >> 2] | 0;
}
return {load: load};
}
RunAsmJsTest(LoadAt_i32, function(module) {
var load = module.load;
assertEquals(BASE, load(0));
assertEquals(BASE | 0x30, load(0x30));
assertEquals(BASE | 0x704, load(0x704));
assertEquals(BASE | 0x704, load(0x705));
assertEquals(BASE | 0x704, load(0x706));
assertEquals(BASE | 0x704, load(0x707));
var length = buffer.byteLength;
assertEquals(BASE | (length - 4), load(length - 4));
assertEquals(BASE | (length - 4), load(length - 4 + 1));
assertEquals(BASE | (length - 4), load(length - 4 + 2));
assertEquals(BASE | (length - 4), load(length - 4 + 3));
for (index of OOB_INDEXES) assertEquals(0, load(index));
checkView(new Int32Array(buffer), load, 2);
});
function LoadAt_i16(stdlib, foreign, buffer) {
"use asm";
var HEAP16 = new stdlib.Int16Array(buffer);
function load(a) {
a = a | 0;
return HEAP16[a >> 1] | 0;
}
return {load: load};
}
RunAsmJsTest(LoadAt_i16, function(module) {
var load = module.load;
var LOWER = (BASE << 16) >> 16;
var UPPER = BASE >> 16;
assertEquals(LOWER, load(0));
assertEquals(UPPER, load(2));
assertEquals(LOWER | 0x30, load(0x30));
assertEquals(UPPER, load(0x32));
assertEquals(LOWER | 0x504, load(0x504));
assertEquals(LOWER | 0x504, load(0x505));
assertEquals(UPPER, load(0x706));
assertEquals(UPPER, load(0x707));
var length = buffer.byteLength;
assertEquals(LOWER | (length - 4), load(length - 4));
assertEquals(LOWER | (length - 4), load(length - 4 + 1));
assertEquals(UPPER, load(length - 4 + 2));
assertEquals(UPPER, load(length - 4 + 3));
for (index of OOB_INDEXES) assertEquals(0, load(index));
checkView(new Int16Array(buffer), load, 1);
});
function LoadAt_u16(stdlib, foreign, buffer) {
"use asm";
var HEAP16 = new stdlib.Uint16Array(buffer);
function load(a) {
a = a | 0;
return HEAP16[a >> 1] | 0;
}
return {load: load};
}
RunAsmJsTest(LoadAt_u16, function(module) {
var load = module.load;
for (index of OOB_INDEXES) assertEquals(0, load(index));
checkView(new Uint16Array(buffer), load, 1);
});
function LoadAt_i8(stdlib, foreign, buffer) {
"use asm";
var HEAP8 = new stdlib.Int8Array(buffer);
function load(a) {
a = a | 0;
return HEAP8[a >> 0] | 0;
}
return {load: load};
}
RunAsmJsTest(LoadAt_i8, function(module) {
var load = module.load;
for (index of OOB_INDEXES) assertEquals(0, load(index));
checkView(new Int8Array(buffer), load, 0);
});
function LoadAt_u8(stdlib, foreign, buffer) {
"use asm";
var HEAP8 = new stdlib.Uint8Array(buffer);
function load(a) {
a = a | 0;
return HEAP8[a >> 0] | 0;
}
return {load: load};
}
RunAsmJsTest(LoadAt_u8, function(module) {
var load = module.load;
for (index of OOB_INDEXES) assertEquals(0, load(index));
checkView(new Uint8Array(buffer), load, 0);
});
function LoadAt_u32(stdlib, foreign, buffer) {
"use asm";
var HEAP32 = new stdlib.Uint32Array(buffer);
function load(a) {
a = a | 0;
return +(HEAP32[a >> 2] >>> 0);
}
return {load: load};
}
RunAsmJsTest(LoadAt_u32, function(module) {
var load = module.load;
for (index of OOB_INDEXES) assertEquals(0, load(index));
checkView(new Uint32Array(buffer), load, 2);
});
function LoadAt_f32(stdlib, foreign, buffer) {
"use asm";
var HEAP32 = new stdlib.Float32Array(buffer);
var fround = stdlib.Math.fround;
function load(a) {
a = a | 0;
return fround(HEAP32[a >> 2]);
}
return {load: load};
}
RunAsmJsTest(LoadAt_f32, function(module) {
var load = module.load;
for (index of OOB_INDEXES) assertEquals(NaN, load(index));
checkView(new Float32Array(buffer), load, 2);
});
function LoadAt_f64(stdlib, foreign, buffer) {
"use asm";
var HEAP64 = new stdlib.Float64Array(buffer);
function load(a) {
a = a | 0;
return +HEAP64[a >> 3];
}
return {load: load};
}
RunAsmJsTest(LoadAt_f64, function(module) {
var load = module.load;
for (index of OOB_INDEXES) assertEquals(NaN, load(index));
checkView(new Float64Array(buffer), load, 3);
});
// TODO(titzer): constant heap indexes
// TODO(titzer): heap accesses with offsets and arithmetic
// TODO(titzer): [i >> K] where K is greater than log(size)

View File

@ -0,0 +1,248 @@
// 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: --validate-asm --allow-natives-syntax
function WrapInAsmModule(func) {
function MODULE_NAME(stdlib) {
"use asm";
var imul = stdlib.Math.imul;
var Math_max = stdlib.Math.max;
var Math_min = stdlib.Math.min;
var Math_abs = stdlib.Math.abs;
FUNC_BODY
return {main: FUNC_NAME};
}
var source = MODULE_NAME.toString()
.replace(/MODULE_NAME/g, func.name + "_module")
.replace(/FUNC_BODY/g, func.toString())
.replace(/FUNC_NAME/g, func.name);
return eval("(" + source + ")");
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
var stdlib = {Math: Math};
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
const imul = Math.imul;
const Math_max = Math.max;
const Math_min = Math.min;
const Math_abs = Math.abs;
function i32_add(a, b) {
a = a | 0;
b = b | 0;
return (a + b) | 0;
}
function i32_sub(a, b) {
a = a | 0;
b = b | 0;
return (a - b) | 0;
}
function i32_mul(a, b) {
a = a | 0;
b = b | 0;
return imul(a, b) | 0;
}
function i32_div(a, b) {
a = a | 0;
b = b | 0;
return ((a | 0) / (b | 0)) | 0;
}
function i32_mod(a, b) {
a = a | 0;
b = b | 0;
return ((a | 0) % (b | 0)) | 0;
}
function i32_and(a, b) {
a = a | 0;
b = b | 0;
return (a & b) | 0;
}
function i32_or(a, b) {
a = a | 0;
b = b | 0;
return (a | b) | 0;
}
function i32_xor(a, b) {
a = a | 0;
b = b | 0;
return (a ^ b) | 0;
}
function i32_shl(a, b) {
a = a | 0;
b = b | 0;
return (a << b) | 0;
}
function i32_shr(a, b) {
a = a | 0;
b = b | 0;
return (a >> b) | 0;
}
function i32_sar(a, b) {
a = a | 0;
b = b | 0;
return (a >>> b) | 0;
}
function i32_eq(a, b) {
a = a | 0;
b = b | 0;
if ((a | 0) == (b | 0)) {
return 1;
}
return 0;
}
function i32_ne(a, b) {
a = a | 0;
b = b | 0;
if ((a | 0) < (b | 0)) {
return 1;
}
return 0;
}
function i32_lt(a, b) {
a = a | 0;
b = b | 0;
if ((a | 0) < (b | 0)) {
return 1;
}
return 0;
}
function i32_lteq(a, b) {
a = a | 0;
b = b | 0;
if ((a | 0) <= (b | 0)) {
return 1;
}
return 0;
}
function i32_gt(a, b) {
a = a | 0;
b = b | 0;
if ((a | 0) > (b | 0)) {
return 1;
}
return 0;
}
function i32_gteq(a, b) {
a = a | 0;
b = b | 0;
if ((a | 0) >= (b | 0)) {
return 1;
}
return 0;
}
function i32_min(a, b) {
a = a | 0;
b = b | 0;
return Math_min(a | 0, b | 0) | 0;
}
function i32_max(a, b) {
a = a | 0;
b = b | 0;
return Math_max(a | 0, b | 0) | 0;
}
function i32_abs(a) {
a = a | 0;
return Math_abs(a | 0) | 0;
}
function i32_neg(a) {
a = a | 0;
return (-a) | 0;
}
function i32_invert(a) {
a = a | 0;
return (~a) | 0;
}
var inputs = [
0, 1, 2, 3, 4,
2147483646,
2147483647, // max positive int32
2147483648, // overflow max positive int32
0x0000009e, 0x00000043, 0x0000af73, 0x0000116b, 0x00658ecc, 0x002b3b4c,
0xeeeeeeee, 0xfffffffd, 0xf0000000, 0x007fffff, 0x0003ffff, 0x00001fff,
-1, -2, -3, -4,
-2147483647,
-2147483648, // min negative int32
-2147483649, // overflow min negative int32
];
var funcs = [
i32_add,
i32_sub,
i32_mul,
i32_div,
i32_mod,
i32_and,
i32_or,
i32_xor,
i32_shl,
i32_shr,
i32_sar,
i32_eq,
i32_ne,
i32_lt,
i32_lteq,
i32_gt,
i32_gteq,
i32_min,
i32_max,
i32_abs,
i32_neg,
i32_invert,
];
(function () {
for (func of funcs) {
RunAsmJsTest(WrapInAsmModule(func), function (module) {
if (func.length == 1) {
for (a of inputs) {
assertEquals(func(a), module.main(a));
}
} else {
for (a of inputs) {
for (b of inputs) {
assertEquals(func(a, b), module.main(a, b));
}
}
}
});
}
})();

View File

@ -0,0 +1,181 @@
// Copyright 2018 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: --validate-asm --allow-natives-syntax
var stdlib = this;
function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func), "must be valid asm code");
}
function assertWasm(expected, func, ffi) {
print("Testing " + func.name + "...");
assertEquals(
expected, func(stdlib, ffi, new ArrayBuffer(1024)).caller());
assertValidAsm(func);
}
function TestForeignFunctions() {
function AsmModule(stdlib, foreign, buffer) {
"use asm";
var setVal = foreign.setVal;
var getVal = foreign.getVal;
function caller(initial_value, new_value) {
initial_value = initial_value|0;
new_value = new_value|0;
if ((getVal()|0) == (initial_value|0)) {
setVal(new_value|0);
return getVal()|0;
}
return 0;
}
return {caller:caller};
}
function ffi(initial_val) {
var val = initial_val;
function getVal() {
return val;
}
function setVal(new_val) {
val = new_val;
}
return {getVal:getVal, setVal:setVal};
}
var foreign = new ffi(23);
var module = AsmModule({Math: Math}, foreign, null);
assertValidAsm(AsmModule);
assertEquals(103, module.caller(23, 103));
}
print("TestForeignFunctions...");
TestForeignFunctions();
function TestForeignFunctionMultipleUse() {
function AsmModule(stdlib, foreign, buffer) {
"use asm";
var getVal = foreign.getVal;
function caller(int_val, double_val) {
int_val = int_val|0;
double_val = +double_val;
if ((getVal()|0) == (int_val|0)) {
if ((+getVal()) == (+double_val)) {
return 89;
}
}
return 0;
}
return {caller:caller};
}
function ffi() {
function getVal() {
return 83.25;
}
return {getVal:getVal};
}
var foreign = new ffi();
var module_decl = eval('(' + AsmModule.toString() + ')');
var module = module_decl(stdlib, foreign, null);
assertValidAsm(module_decl);
assertEquals(89, module.caller(83, 83.25));
}
print("TestForeignFunctionMultipleUse...");
TestForeignFunctionMultipleUse();
function TestForeignVariables() {
function AsmModule(stdlib, foreign, buffer) {
"use asm";
var i1 = foreign.foo | 0;
var f1 = +foreign.bar;
var i2 = foreign.baz | 0;
var f2 = +foreign.baz;
function geti1() {
return i1|0;
}
function getf1() {
return +f1;
}
function geti2() {
return i2|0;
}
function getf2() {
return +f2;
}
return {geti1:geti1, getf1:getf1, geti2:geti2, getf2:getf2};
}
function TestCase(env, i1, f1, i2, f2) {
print("Testing foreign variables...");
var module_decl = eval('(' + AsmModule.toString() + ')');
var module = module_decl(stdlib, env);
assertValidAsm(module_decl);
assertEquals(i1, module.geti1());
assertEquals(f1, module.getf1());
assertEquals(i2, module.geti2());
assertEquals(f2, module.getf2());
}
// Check normal operation.
TestCase({foo: 123, bar: 234.5, baz: 345.7}, 123, 234.5, 345, 345.7);
// Check partial operation.
TestCase({baz: 345.7}, 0, NaN, 345, 345.7);
// Check that undefined values are converted to proper defaults.
TestCase({qux: 999}, 0, NaN, 0, NaN);
// Check that true values are converted properly.
TestCase({foo: true, bar: true, baz: true}, 1, 1.0, 1, 1.0);
// Check that false values are converted properly.
TestCase({foo: false, bar: false, baz: false}, 0, 0, 0, 0);
// Check that null values are converted properly.
TestCase({foo: null, bar: null, baz: null}, 0, 0, 0, 0);
// Check that string values are converted properly.
TestCase({foo: 'hi', bar: 'there', baz: 'dude'}, 0, NaN, 0, NaN);
TestCase({foo: '0xff', bar: '234', baz: '456.1'}, 255, 234, 456, 456.1);
// Check that function values are converted properly.
TestCase({foo: TestCase, bar: TestCase, qux: TestCase}, 0, NaN, 0, NaN);
}
print("TestForeignVariables...");
TestForeignVariables();
function TestGlobalBlock(stdlib, foreign, buffer) {
"use asm";
var x = foreign.x | 0, y = foreign.y | 0;
function test() {
return (x + y) | 0;
}
return {caller: test};
}
assertWasm(15, TestGlobalBlock, { x: 4, y: 11 });

View File

@ -0,0 +1,262 @@
// 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: --validate-asm --allow-natives-syntax
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
var stdlib = {Math: Math};
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
function PositiveIntLiterals() {
"use asm";
function f0() { return 0; }
function f1() { return 1; }
function f4() { return 4; }
function f64() { return 64; }
function f127() { return 127; }
function f128() { return 128; }
function f256() { return 256; }
function f1000() { return 1000; }
function f2000000() { return 2000000; }
function fmax() { return 2147483647; }
return {f0: f0, f1: f1, f4: f4, f64: f64, f127: f127, f128: f128,
f256: f256, f1000: f1000, f2000000: f2000000, fmax: fmax};
}
RunAsmJsTest(PositiveIntLiterals, function(module) {
assertEquals(0, module.f0());
assertEquals(1, module.f1());
assertEquals(1, module.f1());
assertEquals(4, module.f4());
assertEquals(64, module.f64());
assertEquals(128, module.f128());
assertEquals(256, module.f256());
assertEquals(1000, module.f1000());
assertEquals(2000000, module.f2000000());
assertEquals(2147483647, module.fmax());
});
function NegativeIntLiterals() {
"use asm";
function f1() { return -1; }
function f4() { return -4; }
function f64() { return -64; }
function f127() { return -127; }
function f128() { return -128; }
function f256() { return -256; }
function f1000() { return -1000; }
function f2000000() { return -2000000; }
function fmin() { return -2147483648; }
return {f1: f1, f4: f4, f64: f64, f127: f127, f128: f128,
f256: f256, f1000: f1000, f2000000: f2000000, fmin: fmin};
}
RunAsmJsTest(NegativeIntLiterals, function (module) {
assertEquals(-1, module.f1());
assertEquals(-4, module.f4());
assertEquals(-64, module.f64());
assertEquals(-127, module.f127());
assertEquals(-128, module.f128());
assertEquals(-256, module.f256());
assertEquals(-1000, module.f1000());
assertEquals(-2000000, module.f2000000());
assertEquals(-2147483648, module.fmin());
});
function PositiveUnsignedLiterals() {
"use asm";
function f0() { return +(0 >>> 0); }
function f1() { return +(1 >>> 0); }
function f4() { return +(4 >>> 0); }
function f64() { return +(64 >>> 0); }
function f127() { return +(127 >>> 0); }
function f128() { return +(128 >>> 0); }
function f256() { return +(256 >>> 0); }
function f1000() { return +(1000 >>> 0); }
function f2000000() { return +(2000000 >>> 0); }
function fmax() { return +(2147483647 >>> 0); }
return {f0: f0, f1: f1, f4: f4, f64: f64, f127: f127, f128: f128,
f256: f256, f1000: f1000, f2000000: f2000000, fmax: fmax};
}
RunAsmJsTest(PositiveUnsignedLiterals, function (module) {
assertEquals(0, module.f0());
assertEquals(1, module.f1());
assertEquals(4, module.f4());
assertEquals(64, module.f64());
assertEquals(128, module.f128());
assertEquals(256, module.f256());
assertEquals(1000, module.f1000());
assertEquals(2000000, module.f2000000());
assertEquals(2147483647, module.fmax());
});
function LargeUnsignedLiterals() {
"use asm";
function a() {
var x = 2147483648;
return +(x >>> 0);
}
function b() {
var x = 2147483649;
return +(x >>> 0);
}
function c() {
var x = 0x80000000;
return +(x >>> 0);
}
function d() {
var x = 0x80000001;
return +(x >>> 0);
}
function e() {
var x = 0xffffffff;
return +(x >>> 0);
}
return {a: a, b: b, c: c, d: d, e: e};
}
RunAsmJsTest(LargeUnsignedLiterals, function(module) {
assertEquals(2147483648, module.a());
assertEquals(2147483649, module.b());
assertEquals(0x80000000, module.c());
assertEquals(0x80000001, module.d());
assertEquals(0xffffffff, module.e());
});
function ManyI32() {
"use asm";
function main() {
var a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, i = 0, j = 0;
a = 1 + -2 + 3 + -4 | 0;
b = 11 + -22 + 33 + -44 | 0;
c = 111 + -222 + 333 + -444 | 0;
d = 1111 + -2222 + 3333 + -4444 | 0;
e = 11111 + -22222 + 33333 + -44444 | 0;
f = 155555 + -266666 + 377777 + -488888 | 0;
g = 1155555 + -2266666 + 3377777 + -4488888 | 0;
h = 11155555 + -22266666 + 33377777 + -44488888 | 0;
i = 111155555 + -222266666 + 333377777 + -444488888 | 0;
j = (
0x1 + 0x2 + 0x4 + 0x8 +
0x10 + 0x20 + 0x40 + 0x80 +
0x10F + 0x200 + 0x400 + 0x800 +
0x10E0 + 0x20F0 + 0x4000 + 0x8000 +
0x10D00 + 0x20E00 + 0x400F0 + 0x80002 +
0x10C000 + 0x20D000 + 0x400E00 + 0x800030 +
0x10B0000 + 0x20C0000 + 0x400D000 + 0x8000400 +
0x10A00000 + 0x20B00000 + 0x400C0000 + 0x80005000
) | 0;
return (a + b + c + d + e + f + g + h + i + j) | 0;
}
return {main: main};
}
RunAsmJsTest(ManyI32, function(module) {
assertEquals(-222411306, module.main());
});
function ManyF64a() {
"use asm";
function main() {
var a = 0.0, b = 0.0, c = 0.0, d = 0.0,
e = 0.0, f = 0.0, g = 0.0, h = 0.0, i = 0.0;
a = +( 0.1 + -0.2 + 0.3 + -0.4);
b = +( 1.1 + -2.2 + 0.33 + -4.4);
c = +( 11.1 + -22.2 + 3.33 + -4.44);
d = +( 111.1 + -222.2 + 33.33 + -4.444);
e = +( 1111.1 + -2222.2 + 333.33 + -4.4444);
f = +( 15555.5 + -26666.6 + 3777.77 + -4.88888);
g = +( 115555.5 + -226666.6 + 33777.77 + -4.488888);
h = +( 1115555.5 + -2226666.6 + 333777.77 + -4.4488888);
i = +(11115555.5 + -22226666.6 + 3333777.77 + -4.44488888);
return +(a + b + c + d + e + f + g + h + i);
}
return {main: main};
}
RunAsmJsTest(ManyF64a, function(module) {
assertEquals(-8640233.599945681, module.main());
});
function ManyF64b() {
"use asm";
function k1() { return +(1.0e-25 + 3.0e-25 + 5.0e-25 + 6.0e-25 + 9.0e-25); }
function k2() { return +(1.0e-20 + 3.0e-20 + 5.0e-20 + 6.0e-20 + 9.0e-20); }
function k3() { return +(1.0e-15 + 3.0e-15 + 5.0e-15 + 6.0e-15 + 9.0e-15); }
function k4() { return +(1.0e-10 + 3.0e-10 + 5.0e-10 + 6.0e-10 + 9.0e-10); }
function k5() { return +(1.0e-5 + 3.0e-5 + 5.0e-5 + 6.0e-5 + 9.0e-5); }
function k6() { return +(1.1e+0 + 3.1e+0 + 5.1e+0 + 6.1e+0 + 9.1e+0); }
return {k1: k1, k2: k2, k3: k3, k4: k4, k5: k5, k6: k6};
}
RunAsmJsTest(ManyF64b, function(module) {
assertEquals(2.4e-24, module.k1());
assertEquals(2.4e-19, module.k2());
assertEquals(2.4e-14, module.k3());
assertEquals(2.4e-9, module.k4());
assertEquals(0.00024000000000000003, module.k5());
assertEquals(24.5, module.k6());
});
function ManyF64c() {
"use asm";
function k1() { return +(1.0e+25 + 3.0e+25 + 5.0e+25 + 6.0e+25 + 9.0e+25); }
function k2() { return +(1.0e+20 + 3.0e+20 + 5.0e+20 + 6.0e+20 + 9.0e+20); }
function k3() { return +(1.0e+15 + 3.0e+15 + 5.0e+15 + 6.0e+15 + 9.0e+15); }
function k4() { return +(1.0e+10 + 3.0e+10 + 5.0e+10 + 6.0e+10 + 9.0e+10); }
function k5() { return +(1.0e+5 + 3.0e+5 + 5.0e+5 + 6.0e+5 + 9.0e+5); }
function k6() { return +(1.4e+0 + 3.4e+0 + 5.4e+0 + 6.4e+0 + 9.4e+0); }
return {k1: k1, k2: k2, k3: k3, k4: k4, k5: k5, k6: k6};
}
RunAsmJsTest(ManyF64c, function(module) {
assertEquals(2.4000000000000004e+26, module.k1());
assertEquals(2.4e+21, module.k2());
assertEquals(2.4e+16, module.k3());
assertEquals(2.4e+11, module.k4());
assertEquals(2.4e+6, module.k5());
assertEquals(26, module.k6());
});
function ManyF32a(stdlib) {
"use asm";
var F = stdlib.Math.fround;
function k1() { return F(F(1.0e-25) + F(5.0e-25) + F(6.0e-25) + F(9.0e-25)); }
function k2() { return F(F(1.0e-20) + F(5.0e-20) + F(6.0e-20) + F(9.0e-20)); }
function k3() { return F(F(1.0e-15) + F(5.0e-15) + F(6.0e-15) + F(9.0e-15)); }
function k4() { return F(F(1.0e-10) + F(5.0e-10) + F(6.0e-10) + F(9.0e-10)); }
function k5() { return F(F(1.0e-5) + F(5.0e-5) + F(6.0e-5) + F(9.0e-5)); }
function k6() { return F(F(1.1e+0) + F(5.1e+0) + F(6.1e+0) + F(9.1e+0)); }
return {k1: k1, k2: k2, k3: k3, k4: k4, k5: k5, k6: k6};
}
if (false) {
// TODO(bradnelson): fails validation of F32 literals somehow.
RunAsmJsTest(ManyF32a, function(module) {
assertEquals(2.0999999917333043e-24, module.k1());
assertEquals(2.099999868734112e-19, module.k2());
assertEquals(2.099999997029825e-14, module.k3());
assertEquals(2.099999951710174e-9, module.k4());
assertEquals(0.0002099999983329326, module.k5());
assertEquals(21.399999618530273, module.k6());
});
}

View File

@ -0,0 +1,292 @@
// Copyright 2018 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: --validate-asm --allow-natives-syntax
function verbose(args) {
// print(...args);
}
//=============================================
// Global count of failures
//=============================================
let numFailures = 0;
function reportFailure(name, vals, m, w) {
print(' error: ' + name + '(' + vals + ') == ' + w + ', expected ' + m);
numFailures++;
}
let inputs = [
1 / 0,
-1 / 0,
0 / 0,
-2.70497e+38,
-1.4698e+37,
-1.22813e+35,
-1.34584e+34,
-1.0079e+32,
-6.49364e+26,
-3.06077e+25,
-1.46821e+25,
-1.17658e+23,
-1.9617e+22,
-2.7357e+20,
-9223372036854775808.0, // INT64_MIN
-1.48708e+13,
-1.89633e+12,
-4.66622e+11,
-2.22581e+11,
-1.45381e+10,
-2147483904.0, // First float32 after INT32_MIN
-2147483648.0, // INT32_MIN
-2147483520.0, // Last float32 before INT32_MIN
-1.3956e+09,
-1.32951e+09,
-1.30721e+09,
-1.19756e+09,
-9.26822e+08,
-5.09256e+07,
-964300.0,
-192446.0,
-28455.0,
-27194.0,
-20575.0,
-17069.0,
-9167.0,
-960.178,
-113.0,
-62.0,
-15.0,
-7.0,
-1.0,
-0.0256635,
-4.60374e-07,
-3.63759e-10,
-4.30175e-14,
-5.27385e-15,
-1.5707963267948966,
-1.48084e-15,
-2.220446049250313e-16,
-1.05755e-19,
-3.2995e-21,
-1.67354e-23,
-1.11885e-23,
-1.78506e-30,
-1.43718e-34,
-1.27126e-38,
-0.0,
3e-88,
-2e66,
0.0,
2e66,
1.17549e-38,
1.56657e-37,
4.08512e-29,
6.25073e-22,
4.1723e-13,
1.44343e-09,
1.5707963267948966,
5.27004e-08,
9.48298e-08,
5.57888e-07,
4.89988e-05,
0.244326,
1.0,
12.4895,
19.0,
47.0,
106.0,
538.324,
564.536,
819.124,
7048.0,
12611.0,
19878.0,
20309.0,
797056.0,
1.77219e+09,
2147483648.0, // INT32_MAX + 1
4294967296.0, // UINT32_MAX + 1
1.51116e+11,
4.18193e+13,
3.59167e+16,
9223372036854775808.0, // INT64_MAX + 1
18446744073709551616.0, // UINT64_MAX + 1
3.38211e+19,
2.67488e+20,
1.78831e+21,
9.20914e+21,
8.35654e+23,
1.4495e+24,
5.94015e+25,
4.43608e+30,
2.44502e+33,
1.38178e+37,
1.71306e+37,
3.31899e+38,
3.40282e+38,
];
let stdlib = this;
// Module template for generating f64 unop functions.
function ModuleTemplate_f64_unop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
function NAME(a) {
a = +a;
return +Stdlib(a);
}
return {NAME: NAME};
}
// Module template for generating f64 binop functions.
function ModuleTemplate_f64_binop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
function NAME(a, b) {
a = +a;
b = +b;
return +Stdlib(a, b);
}
return {NAME: NAME};
}
// Module template for generating f64 unop functions.
function ModuleTemplate_f32_unop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
var fround = stdlib.Math.fround;
function NAME(a) {
a = fround(a);
return fround(Stdlib(a));
}
return {NAME: NAME};
}
// Module template for generating f64 binop functions.
function ModuleTemplate_f32_binop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
var fround = stdlib.Math.fround;
function NAME(a, b) {
a = fround(a);
b = fround(b);
return fround(Stdlib(a, b));
}
return {NAME: NAME};
}
function instantiateTemplate(func, name) {
let src = func.toString();
src = src.replace(/NAME/g, name);
let module = eval('(' + src + ')');
let instance = module(stdlib);
assertTrue(%IsAsmWasmCode(module));
let asm_func = instance[name];
if (typeof asm_func != 'function') throw 'asm[' + full_name + '] not found';
return asm_func;
}
function genUnop(name, f32) {
return instantiateTemplate(
f32 ? ModuleTemplate_f32_unop : ModuleTemplate_f64_unop, name);
}
function genBinop(name, f32) {
return instantiateTemplate(
f32 ? ModuleTemplate_f32_binop : ModuleTemplate_f64_binop, name);
}
function assertUnop(name, math_func, asm_func) {
for (let val of inputs) {
verbose(' ', val);
let m = math_func(val);
let w = asm_func(val);
if (!deepEquals(m, w)) reportFailure(name, [val], m, w);
}
}
function assertBinop(name, math_func, asm_func) {
let inputs2 = [1, 0.5, -1, -0.5, 0, -0, 1 / 0, -1 / 0, 0 / 0];
for (let val of inputs) {
verbose(' ', val);
for (let val2 of inputs2) {
verbose(' ', val2);
let m = math_func(val, val2);
let w = asm_func(val, val2);
if (!deepEquals(m, w)) reportFailure(name, [val, val2], m, w);
m = math_func(val2, val);
w = asm_func(val2, val);
if (!deepEquals(m, w)) reportFailure(name, [val2, val], m, w);
}
}
}
(function TestF64() {
let f64_intrinsics = [
'acos', 'asin', 'atan', 'cos', 'sin', 'tan', 'exp', 'log',
'atan2', 'pow', 'ceil', 'floor', 'sqrt', 'min', 'max', 'abs',
];
for (let name of f64_intrinsics) {
let math_func = Math[name];
print('Testing (f64) Math.' + name);
switch (math_func.length) {
case 1: {
let asm_func = genUnop(name, false);
assertUnop('(f64)' + name, math_func, asm_func);
break;
}
case 2: {
let asm_func = genBinop(name, false);
assertBinop('(f64)' + name, math_func, asm_func);
break;
}
default:
throw 'Unexpected param count: ' + func.length;
}
}
})();
(function TestF32() {
let f32_intrinsics = ['min', 'max', 'abs', 'ceil', 'floor', 'sqrt'];
for (let name of f32_intrinsics) {
let r = Math.fround, f = Math[name];
print('Testing (f32) Math.' + name);
switch (f.length) {
case 1: {
let asm_func = genUnop(name, true);
let math_func = (val) => r(f(r(val)));
assertUnop('(f32)' + name, math_func, asm_func);
break;
}
case 2: {
let asm_func = genBinop(name, true);
let math_func = (v1, v2) => r(f(r(v1), r(v2)));
assertBinop('(f32)' + name, math_func, asm_func);
break;
}
default:
throw 'Unexpected param count: ' + func.length;
}
}
})();
assertEquals(0, numFailures);

View File

@ -0,0 +1,212 @@
// Copyright 2018 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: --validate-asm --allow-natives-syntax
var stdlib = this;
let kMinHeapSize = 4096;
function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func), "must be valid asm code");
}
function assertWasm(expected, func, ffi) {
print("Testing " + func.name + "...");
assertEquals(
expected, func(stdlib, ffi, new ArrayBuffer(kMinHeapSize)).caller());
assertValidAsm(func);
}
function TestInt32HeapAccess(stdlib, foreign, buffer) {
"use asm";
var m = new stdlib.Int32Array(buffer);
function caller() {
var i = 4;
m[0] = (i + 1) | 0;
m[i >> 2] = ((m[0]|0) + 1) | 0;
m[2] = ((m[i >> 2]|0) + 1) | 0;
return m[2] | 0;
}
return {caller: caller};
}
assertWasm(7, TestInt32HeapAccess);
function TestInt32HeapAccessExternal() {
var memory = new ArrayBuffer(kMinHeapSize);
var memory_int32 = new Int32Array(memory);
var module_decl = eval('(' + TestInt32HeapAccess.toString() + ')');
var module = module_decl(stdlib, null, memory);
assertValidAsm(module_decl);
assertEquals(7, module.caller());
assertEquals(7, memory_int32[2]);
}
TestInt32HeapAccessExternal();
function TestHeapAccessIntTypes() {
var types = [
[Int8Array, 'Int8Array', '>> 0'],
[Uint8Array, 'Uint8Array', '>> 0'],
[Int16Array, 'Int16Array', '>> 1'],
[Uint16Array, 'Uint16Array', '>> 1'],
[Int32Array, 'Int32Array', '>> 2'],
[Uint32Array, 'Uint32Array', '>> 2'],
];
for (var i = 0; i < types.length; i++) {
var code = TestInt32HeapAccess.toString();
code = code.replace('Int32Array', types[i][1]);
code = code.replace(/>> 2/g, types[i][2]);
var memory = new ArrayBuffer(kMinHeapSize);
var memory_view = new types[i][0](memory);
var module_decl = eval('(' + code + ')');
var module = module_decl(stdlib, null, memory);
assertValidAsm(module_decl);
assertEquals(7, module.caller());
assertEquals(7, memory_view[2]);
assertValidAsm(module_decl);
}
}
TestHeapAccessIntTypes();
function TestFloatHeapAccess(stdlib, foreign, buffer) {
"use asm";
var f32 = new stdlib.Float32Array(buffer);
var f64 = new stdlib.Float64Array(buffer);
var fround = stdlib.Math.fround;
function caller() {
var i = 8;
var j = 8;
var v = 6.0;
f64[2] = v + 1.0;
f64[i >> 3] = +f64[2] + 1.0;
f64[j >> 3] = +f64[j >> 3] + 1.0;
i = +f64[i >> 3] == 9.0;
return i|0;
}
return {caller: caller};
}
assertWasm(1, TestFloatHeapAccess);
function TestFloatHeapAccessExternal() {
var memory = new ArrayBuffer(kMinHeapSize);
var memory_float64 = new Float64Array(memory);
var module_decl = eval('(' + TestFloatHeapAccess.toString() + ')');
var module = module_decl(stdlib, null, memory);
assertValidAsm(module_decl);
assertEquals(1, module.caller());
assertEquals(9.0, memory_float64[1]);
}
TestFloatHeapAccessExternal();
(function() {
function TestByteHeapAccessCompat(stdlib, foreign, buffer) {
"use asm";
var HEAP8 = new stdlib.Uint8Array(buffer);
var HEAP32 = new stdlib.Int32Array(buffer);
function store(i, v) {
i = i | 0;
v = v | 0;
HEAP32[i >> 2] = v;
}
function storeb(i, v) {
i = i | 0;
v = v | 0;
HEAP8[i | 0] = v;
}
function load(i) {
i = i | 0;
return HEAP8[i] | 0;
}
function iload(i) {
i = i | 0;
return HEAP8[HEAP32[i >> 2] | 0] | 0;
}
return {load: load, iload: iload, store: store, storeb: storeb};
}
var memory = new ArrayBuffer(kMinHeapSize);
var module_decl = eval('(' + TestByteHeapAccessCompat.toString() + ')');
var m = module_decl(stdlib, null, memory);
assertValidAsm(module_decl);
m.store(0, 20);
m.store(4, 21);
m.store(8, 22);
m.storeb(20, 123);
m.storeb(21, 42);
m.storeb(22, 77);
assertEquals(123, m.load(20));
assertEquals(42, m.load(21));
assertEquals(77, m.load(22));
assertEquals(123, m.iload(0));
assertEquals(42, m.iload(4));
assertEquals(77, m.iload(8));
})();
function TestIntishAssignment(stdlib, foreign, heap) {
"use asm";
var HEAP32 = new stdlib.Int32Array(heap);
function func() {
var a = 1;
var b = 2;
HEAP32[0] = a + b;
return HEAP32[0] | 0;
}
return {caller: func};
}
assertWasm(3, TestIntishAssignment);
function TestFloatishAssignment(stdlib, foreign, heap) {
"use asm";
var HEAPF32 = new stdlib.Float32Array(heap);
var fround = stdlib.Math.fround;
function func() {
var a = fround(1.0);
var b = fround(2.0);
HEAPF32[0] = a + b;
return +HEAPF32[0];
}
return {caller: func};
}
assertWasm(3, TestFloatishAssignment);
function TestDoubleToFloatAssignment(stdlib, foreign, heap) {
"use asm";
var HEAPF32 = new stdlib.Float32Array(heap);
var fround = stdlib.Math.fround;
function func() {
var a = 1.23;
HEAPF32[0] = a;
return +HEAPF32[0];
}
return {caller: func};
}
assertWasm(Math.fround(1.23), TestDoubleToFloatAssignment);

View File

@ -0,0 +1,28 @@
// 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: --validate-asm
function Module(stdlib, foreign, buffer) {
"use asm";
function foo() {}
return {bar: foo};
}
var func = Module({}, {}, new ArrayBuffer(65536)).bar;
assertEquals("Module", Module.name);
assertEquals("foo", func.name);
assertEquals("function foo() {}", func.toString());
function imp() {}
function Module2(stdlib, imports) {
"use asm";
var imp = imports.imp;
function bar() {}
return {bar: bar};
}
var bar = Module2({}, {imp: imp}).bar;
assertEquals("bar", bar.name);
assertEquals("function bar() {}", bar.toString());

View File

@ -0,0 +1,171 @@
// 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: --validate-asm --allow-natives-syntax
var filename = '(?:[^ ]+/)?test/mjsunit/wasm/asm-wasm-stack.js';
filename = filename.replace(/\//g, '[/\\\\]');
function checkPreformattedStack(e, expected_lines) {
print('preformatted stack: ' + e.stack);
var lines = e.stack.split('\n');
assertEquals(expected_lines.length, lines.length);
for (var i = 0; i < lines.length; ++i) {
assertMatches(expected_lines[i], lines[i], 'line ' + i);
}
}
function printCallsites(stack) {
print('callsite objects (size ' + stack.length + '):');
for (var i = 0; i < stack.length; ++i) {
var s = stack[i];
print(
' [' + i + '] ' + s.getFunctionName() + ' (' + s.getFileName() + ':' +
s.getLineNumber() + ':' + s.getColumnNumber() + ')');
}
}
function checkCallsiteArray(stack, expected) {
assertEquals(expected.length, stack.length, 'stack size');
for (var i = 0; i < expected.length; ++i) {
var cs = stack[i];
assertMatches('^' + filename + '$', cs.getFileName(), 'file name at ' + i);
assertEquals(expected[i][0], cs.getFunctionName(), 'function name at ' + i);
assertEquals(expected[i][1], cs.getLineNumber(), 'line number at ' + i);
assertEquals(expected[i][2], cs.getColumnNumber(), 'column number at ' + i);
assertNotNull(cs.getThis(), 'receiver should be global');
assertEquals(stack[0].getThis(), cs.getThis(), 'receiver should be global');
}
}
function checkFunctionsOnCallsites(e, expected) {
printCallsites(e.stack);
checkCallsiteArray(e.stack, expected);
}
function checkTopFunctionsOnCallsites(e, expected) {
printCallsites(e.stack);
assertTrue(
e.stack.length >= expected.length, 'expected at least ' +
expected.length + ' callsites, got ' + e.stack.length);
checkCallsiteArray(e.stack.slice(0, expected.length), expected);
}
function throwException() {
throw new Error('exception from JS');
}
function generateWasmFromAsmJs(stdlib, foreign) {
'use asm';
var throwFunc = foreign.throwFunc;
function callThrow() {
throwFunc();
}
function redirectFun(i) {
i = i | 0;
switch (i | 0) {
case 0: callThrow(); break;
case 1: redirectFun(0); break;
case 2: redirectFun(1); break;
case 3: funTable[i & 0](2); break;
case 4: forwardFun(); break;
}
}
function forwardFun() {
redirectFun(3);
}
var funTable = [ redirectFun ];
return redirectFun;
}
(function PreformattedStackTraceFromJS() {
var fun = generateWasmFromAsmJs(this, {throwFunc: throwException});
assertTrue(%IsWasmCode(fun));
var e = null;
try {
fun(0);
} catch (ex) {
e = ex;
}
assertInstanceof(e, Error, 'exception should have been thrown');
checkPreformattedStack(e, [
'^Error: exception from JS$',
'^ *at throwException \\(' + filename + ':56:9\\)$',
'^ *at callThrow \\(' + filename + ':63:5\\)$',
'^ *at redirectFun \\(' + filename + ':68:15\\)$',
'^ *at PreformattedStackTraceFromJS \\(' + filename + ':87:5\\)$',
'^ *at ' + filename + ':100:3$'
]);
})();
// Now collect the Callsite objects instead of just a string.
Error.prepareStackTrace = function(error, frames) {
return frames;
};
(function CallsiteObjectsFromJS() {
var fun = generateWasmFromAsmJs(this, {throwFunc: throwException});
assertTrue(%IsWasmCode(fun));
var e = null;
try {
fun(4);
} catch (ex) {
e = ex;
}
assertInstanceof(e, Error, 'exception should have been thrown');
checkFunctionsOnCallsites(e, [
['throwException', 56, 9], // --
['callThrow', 63, 5], // --
['redirectFun', 68, 15], // --
['redirectFun', 69, 15], // --
['redirectFun', 70, 15], // --
['redirectFun', 71, 30], // --
['forwardFun', 76, 5], // --
['redirectFun', 72, 15], // --
['CallsiteObjectsFromJS', 112, 5], // --
[null, 129, 3]
]);
})();
function generateOverflowWasmFromAsmJs() {
'use asm';
function f(a) {
a = a | 0;
return f(a) | 0;
}
return f;
}
(function StackOverflowPosition() {
var fun = generateOverflowWasmFromAsmJs();
assertTrue(%IsWasmCode(fun));
var e = null;
try {
fun(23);
} catch (ex) {
e = ex;
}
assertInstanceof(e, RangeError, 'RangeError should have been thrown');
checkTopFunctionsOnCallsites(e, [
['f', 133, 3], // --
['f', 135, 12], // --
['f', 135, 12], // --
['f', 135, 12] // --
]);
})();
(function EnclosingFunctionOffsets() {
const fun = generateWasmFromAsmJs(this, {throwFunc: throwException});
assertTrue(%IsWasmCode(fun));
let e = null;
try {
fun(0);
} catch (ex) {
e = ex;
}
assertEquals(68, e.stack[2].getLineNumber());
assertEquals(15, e.stack[2].getColumnNumber());
assertEquals(65, e.stack[2].getEnclosingLineNumber());
assertEquals(3, e.stack[2].getEnclosingColumnNumber());
})();

View File

@ -0,0 +1,460 @@
// 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: --validate-asm --allow-natives-syntax
var stdlib = this;
function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func));
}
(function TestStdlibConstants() {
function Module(stdlib) {
"use asm";
var StdlibInfinity = stdlib.Infinity;
var StdlibNaN = stdlib.NaN;
var StdlibMathE = stdlib.Math.E;
var StdlibMathLN10 = stdlib.Math.LN10;
var StdlibMathLN2 = stdlib.Math.LN2;
var StdlibMathLOG2E = stdlib.Math.LOG2E;
var StdlibMathLOG10E = stdlib.Math.LOG10E;
var StdlibMathPI = stdlib.Math.PI;
var StdlibMathSQRT1_2 = stdlib.Math.SQRT1_2;
var StdlibMathSQRT2 = stdlib.Math.SQRT2;
function caller() {
if (StdlibInfinity != 1.0 / 0.0) return 0;
if (StdlibMathE != 2.718281828459045) return 0;
if (StdlibMathLN10 != 2.302585092994046) return 0;
if (StdlibMathLN2 != 0.6931471805599453) return 0;
if (StdlibMathLOG2E != 1.4426950408889634) return 0;
if (StdlibMathLOG10E != 0.4342944819032518) return 0;
if (StdlibMathPI != 3.141592653589793) return 0;
if (StdlibMathSQRT1_2 != 0.7071067811865476) return 0;
if (StdlibMathSQRT2 != 1.4142135623730951) return 0;
return 1;
}
function nanCheck() {
return +StdlibNaN;
}
return {caller:caller, nanCheck:nanCheck};
}
var m = Module(stdlib);
assertValidAsm(Module);
assertEquals(1, m.caller());
assertTrue(isNaN(m.nanCheck()));
})();
var stdlib = this;
var stdlib_root_members = [
'Infinity',
'NaN',
];
var stdlib_math_members = [
'E',
'LN10',
'LN2',
'LOG2E',
'LOG10E',
'PI',
'SQRT1_2',
'SQRT2',
'ceil',
'clz32',
'floor',
'sqrt',
'abs',
'min',
'max',
'acos',
'asin',
'atan',
'cos',
'sin',
'tan',
'exp',
'log',
'atan2',
'pow',
'imul',
'fround',
];
(function TestBadStdlib() {
function Module(stdlib) {
"use asm";
var foo = stdlib.NaN;
return {};
}
for (var i = 0; i < stdlib_root_members.length; ++i) {
var member = stdlib_root_members[i];
var stdlib = {};
stdlib[member] = 0;
print(member);
var code = Module.toString().replace('NaN', member);
var decl = eval('(' + code + ')');
decl(stdlib);
assertFalse(%IsAsmWasmCode(decl));
}
for (var i = 0; i < stdlib_math_members.length; ++i) {
var member = stdlib_math_members[i];
var stdlib = {Math:{}};
stdlib['Math'][member] = 0;
print(member);
var code = Module.toString().replace('NaN', 'Math.' + member);
var decl = eval('(' + code + ')');
decl(stdlib);
assertFalse(%IsAsmWasmCode(decl));
}
})();
(function TestMissingNaNStdlib() {
function Module(stdlib) {
"use asm";
var foo = stdlib.NaN;
return {};
}
for (var i = 0; i < stdlib_root_members.length; ++i) {
var member = stdlib_root_members[i];
var code = Module.toString().replace('NaN', member);
var decl = eval('(' + code + ')');
decl({});
assertFalse(%IsAsmWasmCode(decl));
}
for (var i = 0; i < stdlib_math_members.length; ++i) {
var member = stdlib_math_members[i];
var code = Module.toString().replace('NaN', 'Math.' + member);
var decl = eval('(' + code + ')');
assertThrows(function() {
decl({});
assertFalse(%IsAsmWasmCode(decl));
});
}
})();
(function TestStdlibFunctionsInside() {
function Module(stdlib) {
"use asm";
var StdlibMathCeil = stdlib.Math.ceil;
var StdlibMathClz32 = stdlib.Math.clz32;
var StdlibMathFloor = stdlib.Math.floor;
var StdlibMathSqrt = stdlib.Math.sqrt;
var StdlibMathAbs = stdlib.Math.abs;
var StdlibMathMin = stdlib.Math.min;
var StdlibMathMax = stdlib.Math.max;
var StdlibMathAcos = stdlib.Math.acos;
var StdlibMathAsin = stdlib.Math.asin;
var StdlibMathAtan = stdlib.Math.atan;
var StdlibMathCos = stdlib.Math.cos;
var StdlibMathSin = stdlib.Math.sin;
var StdlibMathTan = stdlib.Math.tan;
var StdlibMathExp = stdlib.Math.exp;
var StdlibMathLog = stdlib.Math.log;
var StdlibMathAtan2 = stdlib.Math.atan2;
var StdlibMathPow = stdlib.Math.pow;
var StdlibMathImul = stdlib.Math.imul;
var fround = stdlib.Math.fround;
function deltaEqual(x, y) {
x = +x;
y = +y;
var t = 0.0;
t = x - y;
if (t < 0.0) {
t = t * -1.0;
}
return (t < 1.0e-13) | 0;
}
function caller() {
if (!(deltaEqual(+StdlibMathSqrt(123.0), 11.090536506409418)|0)) return 0;
if (fround(StdlibMathSqrt(fround(256.0))) != fround(16.0)) return 0;
if (+StdlibMathCeil(123.7) != 124.0) return 0;
if (fround(StdlibMathCeil(fround(123.7))) != fround(124.0)) return 0;
if (+StdlibMathFloor(123.7) != 123.0) return 0;
if (fround(StdlibMathFloor(fround(123.7))) != fround(123.0)) return 0;
if (+StdlibMathAbs(-123.0) != 123.0) return 0;
if (fround(StdlibMathAbs(fround(-123.0))) != fround(123.0)) return 0;
if (+StdlibMathMin(123.4, 1236.4) != 123.4) return 0;
if (fround(StdlibMathMin(fround(123.4),
fround(1236.4))) != fround(123.4)) return 0;
if (+StdlibMathMax(123.4, 1236.4) != 1236.4) return 0;
if (fround(StdlibMathMax(fround(123.4), fround(1236.4)))
!= fround(1236.4)) return 0;
if (!(deltaEqual(+StdlibMathAcos(0.1), 1.4706289056333368)|0)) return 0;
if (!(deltaEqual(+StdlibMathAsin(0.2), 0.2013579207903308)|0)) return 0;
if (!(deltaEqual(+StdlibMathAtan(0.2), 0.19739555984988078)|0)) return 0;
if (!(deltaEqual(+StdlibMathCos(0.2), 0.9800665778412416)|0)) return 0;
if (!(deltaEqual(+StdlibMathSin(0.2), 0.19866933079506122)|0)) return 0;
if (!(deltaEqual(+StdlibMathTan(0.2), 0.20271003550867250)|0)) return 0;
if (!(deltaEqual(+StdlibMathExp(0.2), 1.2214027581601699)|0)) return 0;
if (!(deltaEqual(+StdlibMathLog(0.2), -1.6094379124341003)|0)) return 0;
if ((StdlibMathClz32(134217728)|0) != 4) return 0;
if ((StdlibMathImul(6, 7)|0) != 42) return 0;
if (!(deltaEqual(+StdlibMathAtan2(6.0, 7.0), 0.7086262721276703)|0))
return 0;
if (+StdlibMathPow(6.0, 7.0) != 279936.0) return 0;
return 1;
}
return {caller:caller};
}
var m = Module(stdlib);
assertValidAsm(Module);
assertEquals(1, m.caller());
})();
(function TestStdlibFunctionOutside() {
function looseEqual(x, y, delta) {
if (delta === undefined) {
delta = 1.0e-10;
}
if (isNaN(x) && isNaN(y)) {
return true;
}
if (!isFinite(x) && !isFinite(y)) {
return true;
}
x = +x;
y = +y;
var t = 0.0;
t = x - y;
if (t < 0.0) {
t = t * -1.0;
}
return (t < delta) | 0;
}
function plainEqual(x, y) {
if (isNaN(x) && isNaN(y)) {
return true;
}
return x === y;
}
function Module(stdlib) {
"use asm";
var ceil = stdlib.Math.ceil;
var floor = stdlib.Math.floor;
var sqrt = stdlib.Math.sqrt;
var abs = stdlib.Math.abs;
var fround = stdlib.Math.fround;
var fround2 = stdlib.Math.fround;
var acos = stdlib.Math.acos;
var asin = stdlib.Math.asin;
var atan = stdlib.Math.atan;
var cos = stdlib.Math.cos;
var sin = stdlib.Math.sin;
var tan = stdlib.Math.tan;
var exp = stdlib.Math.exp;
var log = stdlib.Math.log;
var atan2 = stdlib.Math.atan2;
var pow = stdlib.Math.pow;
var imul = stdlib.Math.imul;
var min = stdlib.Math.min;
var max = stdlib.Math.max;
function ceil_f64(x) { x = +x; return +ceil(x); }
function ceil_f32(x) { x = fround(x); return fround(ceil(x)); }
function floor_f64(x) { x = +x; return +floor(x); }
function floor_f32(x) { x = fround(x); return fround(floor(x)); }
function sqrt_f64(x) { x = +x; return +sqrt(x); }
function sqrt_f32(x) { x = fround(x); return fround(sqrt(x)); }
function abs_f64(x) { x = +x; return +abs(x); }
function abs_f32(x) { x = fround(x); return fround(abs(x)); }
function abs_i32(x) { x = x | 0; return abs(x|0) | 0; }
function acos_f64(x) { x = +x; return +acos(x); }
function asin_f64(x) { x = +x; return +asin(x); }
function atan_f64(x) { x = +x; return +atan(x); }
function cos_f64(x) { x = +x; return +cos(x); }
function sin_f64(x) { x = +x; return +sin(x); }
function tan_f64(x) { x = +x; return +tan(x); }
function exp_f64(x) { x = +x; return +exp(x); }
function log_f64(x) { x = +x; return +log(x); }
function atan2_f64(x, y) { x = +x; y = +y; return +atan2(x, y); }
function pow_f64(x, y) { x = +x; y = +y; return +atan2(x, y); }
function imul_i32(x, y) { x = x | 0; y = y | 0; return imul(x, y) | 0; }
function imul_u32(x, y) {
x = x | 0; y = y | 0; return imul(x>>>0, y>>>0) | 0; }
// type -> f32
function fround_i32(x) { x = x | 0; return fround(x|0); }
function fround_u32(x) { x = x | 0; return fround(x>>>0); }
function fround_f32(x) { x = fround(x); return fround(x); }
function fround_f64(x) { x = +x; return fround(x); }
// type -> f32 -> type
function fround2_i32(x) { x = x | 0; return ~~fround2(x|0) | 0; }
function fround2_u32(x) { x = x | 0; return ~~fround2(x>>>0) | 0; }
function fround2_f32(x) { x = fround2(x); return fround2(x); }
function fround2_f64(x) { x = +x; return +fround2(x); }
function min_i32(x, y) { x = x | 0; y = y | 0; return min(x|0, y|0) | 0; }
function min_f32(x, y) {
x = fround(x); y = fround(y); return fround(min(x, y)); }
function min_f64(x, y) { x = +x; y = +y; return +min(x, y); }
function max_i32(x, y) { x = x | 0; y = y | 0; return max(x|0, y|0) | 0; }
function max_f32(x, y) {
x = fround(x); y = fround(y); return fround(max(x, y)); }
function max_f64(x, y) { x = +x; y = +y; return +max(x, y); }
return {
ceil_f64: ceil_f64,
ceil_f32: ceil_f32,
floor_f64: floor_f64,
floor_f32: floor_f32,
sqrt_f64: sqrt_f64,
sqrt_f32: sqrt_f32,
abs_f64: abs_f64,
abs_f32: abs_f32,
abs_i32: abs_i32,
acos_f64: acos_f64,
asin_f64: asin_f64,
atan_f64: atan_f64,
cos_f64: cos_f64,
sin_f64: sin_f64,
tan_f64: tan_f64,
exp_f64: exp_f64,
log_f64: log_f64,
imul_i32: imul_i32,
imul_u32: imul_u32,
fround_i32: fround_i32,
fround_u32: fround_u32,
fround_f32: fround_f32,
fround_f64: fround_f64,
fround2_i32: fround2_i32,
fround2_u32: fround2_u32,
fround2_f32: fround2_f32,
fround2_f64: fround2_f64,
min_i32: min_i32,
min_f32: min_f32,
min_f64: min_f64,
max_i32: max_i32,
max_f32: max_f32,
max_f64: max_f64,
};
}
var m = Module(stdlib);
assertValidAsm(Module);
var values = {
i32: [
0, 1, -1, 123, 456, -123, -456,
0x40000000, 0x7FFFFFFF, -0x80000000,
],
u32: [
0, 1, 123, 456,
0x40000000, 0x7FFFFFFF, 0xFFFFFFFF, 0x80000000,
],
f32: [
0, -0, 1, -1, 0.25, 0.125, 0.9, -0.9, 1.414,
0x7F, -0x80, -0x8000, -0x80000000,
0x7FFF, 0x7FFFFFFF, Infinity, -Infinity, NaN,
],
f64: [
0, -0, 1, -1, 0.25, 0.125, 0.9, -0.9, 1.414,
0x7F, -0x80, -0x8000, -0x80000000,
0x7FFF, 0x7FFFFFFF, Infinity, -Infinity, NaN,
],
};
var converts = {
i32: function(x) { return x | 0; },
u32: function(x) { return x >>> 0; },
f32: function(x) { return Math.fround(x); },
f64: function(x) { return x; },
};
var two_args = {atan2: true, pow: true, imul: true,
min: true, max: true};
var funcs = {
ceil: ['f32', 'f64'],
floor: ['f32', 'f64'],
sqrt: ['f32', 'f64'],
abs: ['i32', 'f32', 'f64'],
acos: ['f64'],
asin: ['f64'],
atan: ['f64'],
cos: ['f64'],
sin: ['f64'],
tan: ['f64'],
exp: ['f64'],
log: ['f64'],
imul: ['i32', 'u32'],
fround: ['i32', 'u32', 'f32', 'f64'],
min: ['i32', 'f32', 'f64'],
max: ['i32', 'f32', 'f64'],
};
var per_func_equals = {
// JS uses fdlib for these, so they may not match.
// ECMAscript does not required them to have a particular precision.
exp_f64: function(x, y) { return looseEqual(x, y, 1e55); },
sqrt_f32: function(x, y) { return looseEqual(x, y, 1e-5); },
cos_f64: looseEqual,
sin_f64: looseEqual,
tan_f64: looseEqual,
// TODO(bradnelson):
// Figure out why some builds (avx2, rel_ng) return a uint.
imul_u32: function(x, y) { return (x | 0) === (y | 0); },
};
for (var func in funcs) {
var types = funcs[func];
for (var i = 0; i < types.length; i++) {
var type = types[i];
var interesting = values[type];
for (var j = 0; j < interesting.length; j++) {
for (var k = 0; k < interesting.length; k++) {
var val0 = interesting[j];
var val1 = interesting[k];
var name = func + '_' + type;
if (func === 'fround') {
// fround returns f32 regardless of input.
var expected = Math[func](val0);
var actual = m[name](val0);
} else if (two_args[func]) {
var expected = converts[type](Math[func](val0, val1));
var actual = m[name](val0, val1);
} else {
var expected = converts[type](Math[func](val0, val1));
var actual = m[name](val0, val1);
}
var compare = per_func_equals[name];
if (compare === undefined) {
compare = plainEqual;
}
assertTrue(typeof(compare) === 'function');
if (!compare(expected, actual)) {
print(expected + ' !== ' + actual + ' for ' + name +
' with input ' + val0 + ' ' + val1);
assertTrue(false);
}
}
}
}
}
})();

View File

@ -0,0 +1,486 @@
// 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: --validate-asm --allow-natives-syntax
function assertValidAsm(func) {
assertTrue(%IsAsmWasmCode(func));
}
(function TestSwitch0() {
function asmModule() {
"use asm"
function caller() {
var ret = 0;
var x = 7;
switch (x|0) {
case 1: {
return 0;
}
case 7: {
ret = 5;
break;
}
default: return 0;
}
return ret|0;
}
return {caller:caller};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(5, wasm.caller());
})();
(function TestSwitch() {
function asmModule() {
"use asm"
function caller() {
var ret = 0;
var x = 7;
switch (x|0) {
case 1: return 0;
case 7: {
ret = 12;
break;
}
default: return 0;
}
switch (x|0) {
case 1: return 0;
case 8: return 0;
default: ret = (ret + 11)|0;
}
return ret|0;
}
return {caller:caller};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(23, wasm.caller());
})();
(function TestSwitchFallthrough() {
function asmModule() {
"use asm"
function caller() {
var x = 17;
var ret = 0;
switch (x|0) {
case 17:
case 14: ret = 39;
case 1: ret = (ret + 3)|0;
case 4: break;
default: ret = (ret + 1)|0;
}
return ret|0;
}
return {caller:caller};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(42, wasm.caller());
})();
(function TestNestedSwitch() {
function asmModule() {
"use asm"
function caller() {
var x = 3;
var y = -13;
switch (x|0) {
case 1: return 0;
case 3: {
switch (y|0) {
case 2: return 0;
case -13: return 43;
default: return 0;
}
}
default: return 0;
}
return 0;
}
return {caller:caller};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(43, wasm.caller());
})();
(function TestSwitchWithDefaultOnly() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
switch(x|0) {
default: return -10;
}
return 0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(-10, wasm.main(2));
assertEquals(-10, wasm.main(54));
})();
(function TestEmptySwitch() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
switch(x|0) {
}
return 73;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(73, wasm.main(7));
})();
(function TestSwitchWithBrTable() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
switch(x|0) {
case 14: return 23;
case 12: return 25;
case 15: return 29;
case 19: return 34;
case 18: return 17;
case 16: return 16;
default: return -1;
}
return 0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(25, wasm.main(12));
assertEquals(23, wasm.main(14));
assertEquals(29, wasm.main(15));
assertEquals(16, wasm.main(16));
assertEquals(17, wasm.main(18));
assertEquals(34, wasm.main(19));
assertEquals(-1, wasm.main(-1));
})();
(function TestSwitchWithBalancedTree() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
switch(x|0) {
case 5: return 52;
case 1: return 11;
case 6: return 63;
case 9: return 19;
case -4: return -4;
}
return 0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(-4, wasm.main(-4));
assertEquals(11, wasm.main(1));
assertEquals(52, wasm.main(5));
assertEquals(63, wasm.main(6));
assertEquals(19, wasm.main(9));
assertEquals(0, wasm.main(11));
})();
(function TestSwitchHybrid() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
switch(x|0) {
case 1: return -4;
case 2: return 23;
case 3: return 32;
case 4: return 14;
case 7: return 17;
case 10: return 10;
case 11: return 121;
case 12: return 112;
case 13: return 31;
case 16: return 16;
default: return -1;
}
return 0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(-4, wasm.main(1));
assertEquals(23, wasm.main(2));
assertEquals(32, wasm.main(3));
assertEquals(14, wasm.main(4));
assertEquals(17, wasm.main(7));
assertEquals(10, wasm.main(10));
assertEquals(121, wasm.main(11));
assertEquals(112, wasm.main(12));
assertEquals(31, wasm.main(13));
assertEquals(16, wasm.main(16));
assertEquals(-1, wasm.main(20));
})();
(function TestSwitchFallthroughWithBrTable() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
var ret = 0;
switch(x|0) {
case 1: {
ret = 21;
break;
}
case 2: {
ret = 12;
break;
}
case 3: {
ret = 43;
}
case 4: {
ret = 54;
break;
}
default: {
ret = 10;
break;
}
}
return ret|0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(12, wasm.main(2));
assertEquals(10, wasm.main(10));
assertEquals(54, wasm.main(3));
})();
(function TestSwitchFallthroughHybrid() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
var ret = 0;
switch(x|0) {
case 1: {
ret = 1;
break;
}
case 2: {
ret = 2;
break;
}
case 3: {
ret = 3;
break;
}
case 4: {
ret = 4;
}
case 7: {
ret = 7;
break;
}
case 10: {
ret = 10;
}
case 16: {
ret = 16;
break;
}
case 17: {
ret = 17;
break;
}
case 18: {
ret = 18;
break;
}
case 19: {
ret = 19;
}
default: {
ret = -1;
break;
}
}
return ret|0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(7, wasm.main(4));
assertEquals(16, wasm.main(10));
assertEquals(-1, wasm.main(19));
assertEquals(-1, wasm.main(23));
})();
(function TestSwitchHybridWithNoDefault() {
function asmModule() {
"use asm";
function main(x) {
x = x|0;
var ret = 19;
switch(x|0) {
case 1: {
ret = 1;
break;
}
case 2: {
ret = 2;
break;
}
case 3: {
ret = 3;
break;
}
case 4: {
ret = 4;
break;
}
case 7: {
ret = 7;
break;
}
}
return ret|0;
}
return {
main: main,
};
}
var wasm = asmModule();
assertValidAsm(asmModule);
assertEquals(2, wasm.main(2));
assertEquals(7, wasm.main(7));
assertEquals(19, wasm.main(-1));
})();
(function TestLargeSwitch() {
function LargeSwitchGenerator(begin, end, gap, handle_case) {
var str = "function asmModule() {\
\"use asm\";\
function main(x) {\
x = x|0;\
switch(x|0) {";
for (var i = begin; i <= end; i = i + gap) {
str = str.concat("case ", i.toString(), ": ", handle_case(i));
}
str = str.concat("default: return -1;\
}\
return -2;\
}\
return {main: main}; }");
var decl = eval('(' + str + ')');
var wasm = decl();
assertValidAsm(decl);
return wasm;
}
var handle_case = function(k) {
return "return ".concat(k, ";");
}
var wasm = LargeSwitchGenerator(0, 513, 1, handle_case);
for (var i = 0; i <= 513; i++) {
assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
wasm = LargeSwitchGenerator(0, 1024, 3, handle_case);
for (var i = 0; i <= 1024; i = i + 3) {
assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
wasm = LargeSwitchGenerator(-2147483648, -2147483000, 1, handle_case);
for (var i = -2147483648; i <= -2147483000; i++) {
assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
assertEquals(-1, wasm.main(214748647));
wasm = LargeSwitchGenerator(-2147483648, -2147483000, 3, handle_case);
for (var i = -2147483648; i <= -2147483000; i = i + 3) {
assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
assertEquals(-1, wasm.main(214748647));
wasm = LargeSwitchGenerator(2147483000, 2147483647, 1, handle_case);
for (var i = 2147483000; i <= 2147483647; i++) {
assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
assertEquals(-1, wasm.main(-214748647));
wasm = LargeSwitchGenerator(2147483000, 2147483647, 4, handle_case);
for (var i = 2147483000; i <= 2147483647; i = i + 4) {
assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
assertEquals(-1, wasm.main(-214748647));
handle_case = function(k) {
if (k != 7) return "return ".concat(k, ";");
else return "break;";
}
wasm = LargeSwitchGenerator(0, 1499, 7, handle_case);
for (var i = 0; i <= 1499; i = i + 7) {
if (i == 7) assertEquals(-2, wasm.main(i));
else assertEquals(i, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
handle_case = function(k) {
if (k != 56) return "break;";
else return "return 23;";
}
wasm = LargeSwitchGenerator(0, 638, 2, handle_case);
for (var i = 0; i <= 638; i = i + 2) {
if (i == 56) assertEquals(23, wasm.main(i));
else assertEquals(-2, wasm.main(i));
}
assertEquals(-1, wasm.main(-1));
})();

View File

@ -0,0 +1,221 @@
// 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: --validate-asm --allow-natives-syntax
function WrapInAsmModule(func) {
function MODULE_NAME(stdlib) {
"use asm";
var imul = stdlib.Math.imul;
FUNC_BODY
return {main: FUNC_NAME};
}
var source = MODULE_NAME.toString()
.replace(/MODULE_NAME/g, func.name + "_module")
.replace(/FUNC_BODY/g, func.toString())
.replace(/FUNC_NAME/g, func.name);
return eval("(" + source + ")");
}
function RunAsmJsTest(asmfunc, expect) {
var asm_source = asmfunc.toString();
var nonasm_source = asm_source.replace(new RegExp("use asm"), "");
var stdlib = {Math: Math};
print("Testing " + asmfunc.name + " (js)...");
var js_module = eval("(" + nonasm_source + ")")(stdlib);
expect(js_module);
print("Testing " + asmfunc.name + " (asm.js)...");
var asm_module = asmfunc(stdlib);
assertTrue(%IsAsmWasmCode(asmfunc));
expect(asm_module);
}
const imul = Math.imul;
function u32_add(a, b) {
a = a | 0;
b = b | 0;
return +(((a >>> 0) + (b >>> 0)) >>> 0);
}
function u32_sub(a, b) {
a = a | 0;
b = b | 0;
return +(((a >>> 0) - (b >>> 0)) >>> 0);
}
function u32_mul(a, b) {
a = a | 0;
b = b | 0;
return +imul(a >>> 0, b >>> 0);
}
function u32_div(a, b) {
a = a | 0;
b = b | 0;
return +(((a >>> 0) / (b >>> 0)) >>> 0);
}
function u32_mod(a, b) {
a = a | 0;
b = b | 0;
return +(((a >>> 0) % (b >>> 0)) >>> 0);
}
function u32_and(a, b) {
a = a | 0;
b = b | 0;
return +((a >>> 0) & (b >>> 0));
}
function u32_or(a, b) {
a = a | 0;
b = b | 0;
return +((a >>> 0) | (b >>> 0));
}
function u32_xor(a, b) {
a = a | 0;
b = b | 0;
return +((a >>> 0) ^ (b >>> 0));
}
function u32_shl(a, b) {
a = a | 0;
b = b | 0;
return +((a >>> 0) << (b >>> 0));
}
function u32_shr(a, b) {
a = a | 0;
b = b | 0;
return +((a >>> 0) >> (b >>> 0));
}
function u32_sar(a, b) {
a = a | 0;
b = b | 0;
return ((a >>> 0) >>> (b >>> 0)) | 0;
}
function u32_eq(a, b) {
a = a | 0;
b = b | 0;
if ((a >>> 0) == (b >>> 0)) {
return 1;
}
return 0;
}
function u32_ne(a, b) {
a = a | 0;
b = b | 0;
if ((a >>> 0) < (b >>> 0)) {
return 1;
}
return 0;
}
function u32_lt(a, b) {
a = a | 0;
b = b | 0;
if ((a >>> 0) < (b >>> 0)) {
return 1;
}
return 0;
}
function u32_lteq(a, b) {
a = a | 0;
b = b | 0;
if ((a >>> 0) <= (b >>> 0)) {
return 1;
}
return 0;
}
function u32_gt(a, b) {
a = a | 0;
b = b | 0;
if ((a >>> 0) > (b >>> 0)) {
return 1;
}
return 0;
}
function u32_gteq(a, b) {
a = a | 0;
b = b | 0;
if ((a >>> 0) >= (b >>> 0)) {
return 1;
}
return 0;
}
function u32_neg(a) {
a = a | 0;
return (-a) | 0;
}
function u32_invert(a) {
a = a | 0;
return (~a) | 0;
}
var inputs = [
0, 1, 2, 3, 4,
2147483646,
2147483647, // max positive int32
2147483648, // overflow max positive int32
0x0000009e, 0x00000043, 0x0000af73, 0x0000116b, 0x00658ecc, 0x002b3b4c,
0xeeeeeeee, 0xfffffffd, 0xf0000000, 0x007fffff, 0x0003ffff, 0x00001fff,
-1, -2, -3, -4,
-2147483647,
-2147483648, // min negative int32
-2147483649, // overflow min negative int32
];
var funcs = [
u32_add,
u32_sub,
u32_div,
u32_mod,
// TODO(titzer): u32_mul crashes turbofan in asm.js mode
u32_and,
u32_or,
u32_xor,
u32_shl,
u32_shr,
u32_sar,
u32_eq,
u32_ne,
u32_lt,
u32_lteq,
u32_gt,
u32_gteq,
u32_neg,
u32_invert,
// TODO(titzer): u32_min
// TODO(titzer): u32_max
// TODO(titzer): u32_abs
];
(function () {
for (func of funcs) {
RunAsmJsTest(WrapInAsmModule(func), function (module) {
for (a of inputs) {
for (b of inputs) {
var expected = func(a, b);
assertEquals(expected, module.main(a, b));
}
}
});
}
})();

1423
deps/v8/test/mjsunit/wasm/asm-wasm.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
// Copyright 2017 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: --validate-asm --allow-natives-syntax
// NOTE: This is in its own file because it calls %DisallowWasmCodegen, which
// messes with the isolate's state.
(function testAsmWithWasmOff() {
%DisallowWasmCodegen(true);
function Module() {
'use asm';
function foo() {
return 0;
}
return {foo: foo};
}
Module();
assertTrue(%IsAsmWasmCode(Module));
})();

View File

@ -0,0 +1,7 @@
// Copyright 2025 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: --no-wasm-async-compilation
d8.file.execute('test/mjsunit/wasm/async-compile.js');

View File

@ -0,0 +1,91 @@
// Copyright 2017 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
async function assertCompiles(buffer) {
var module = await WebAssembly.compile(buffer);
assertInstanceof(module, WebAssembly.Module);
}
function assertCompileError(buffer, msg) {
if (typeof msg == 'string') {
msg = 'WebAssembly.compile(): ' + msg;
} else {
assertInstanceof(msg, RegExp);
}
return assertThrowsAsync(
WebAssembly.compile(buffer), WebAssembly.CompileError, msg);
}
assertPromiseResult(async function basicCompile() {
let ok_buffer = (() => {
var builder = new WasmModuleBuilder();
builder.addFunction('f', kSig_i_v)
.addBody([kExprI32Const, 42])
.exportAs('f');
return builder.toBuffer();
})();
// The OK buffer validates and can be made into a module.
assertTrue(WebAssembly.validate(ok_buffer));
let ok_module = new WebAssembly.Module(ok_buffer);
assertTrue(ok_module instanceof WebAssembly.Module);
// The bad buffer does not validate and cannot be made into a module.
let bad_buffer = new ArrayBuffer(0);
assertFalse(WebAssembly.validate(bad_buffer));
assertThrows(
() => new WebAssembly.Module(bad_buffer), WebAssembly.CompileError);
let kNumCompiles = 3;
// Three compilations of the OK module should succeed.
for (var i = 0; i < kNumCompiles; i++) {
await assertCompiles(ok_buffer);
}
// Three compilations of the bad module should fail.
for (var i = 0; i < kNumCompiles; i++) {
await assertCompileError(bad_buffer, 'BufferSource argument is empty');
}
}());
assertPromiseResult(async function badFunctionInTheMiddle() {
// We had an error where an exception was generated by a background task and
// later thrown in a foreground task. The handle to the exception died
// between, since the HandleScope was left.
// This test reproduced that error.
let builder = new WasmModuleBuilder();
let sig = builder.addType(kSig_i_v);
for (var i = 0; i < 10; ++i) {
builder.addFunction('a' + i, sig).addBody([kExprI32Const, 42]);
}
builder.addFunction('bad', sig).addBody([]);
for (var i = 0; i < 10; ++i) {
builder.addFunction('b' + i, sig).addBody([kExprI32Const, 42]);
}
let buffer = builder.toBuffer();
await assertCompileError(
buffer,
'Compiling function #10:\"bad\" failed: ' +
'expected 1 elements on the stack for fallthru, found 0 @+94');
}());
assertPromiseResult(async function importWithoutCode() {
// Regression test for https://crbug.com/898310.
let builder = new WasmModuleBuilder();
builder.addImport('m', 'q', kSig_i_i);
await builder.asyncInstantiate({'m': {'q': i => i}});
}());
assertPromiseResult(async function invalidSectionCode() {
let kInvalidSectionCode = 61;
let builder = new WasmModuleBuilder();
builder.addExplicitSection([kInvalidSectionCode, 0]);
let buffer = builder.toBuffer();
// Async and streaming decoder disagree on the error message, so accept both.
await assertCompileError(buffer, /(unknown|invalid) section code/);
}());

View File

@ -0,0 +1,22 @@
// Copyright 2023 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.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestI32AtomicAddAlignmentCheck() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addMemory(32, 128);
builder.addFunction(`main`,
makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix, kExprI32AtomicAdd, 1, 0,
]).exportFunc();
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/Compiling function #0:"main" failed: invalid alignment for atomic operation; expected alignment is 2, actual alignment is 1/);
})();

View File

@ -0,0 +1,118 @@
// Copyright 2023 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.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
const mem0_idx = builder.addImportedMemory('imp', 'mem0', 0, 20, 'shared');
const mem1_idx = builder.addImportedMemory('imp', 'mem1', 0, 20, 'shared');
for (let mem_idx of [mem0_idx, mem1_idx]) {
builder.addFunction(`notify${mem_idx}`, kSig_i_ii)
.addBody([
kExprLocalGet, 0, // -
kExprLocalGet, 1, // -
kAtomicPrefix, kExprAtomicNotify, 0x42, mem_idx, 0 // -
])
.exportFunc();
builder.addFunction(`wait${mem_idx}`, kSig_i_iii)
.addBody([
kExprLocalGet, 0, // -
kExprLocalGet, 1, // -
kExprLocalGet, 2, // -
// Convert to i64 and multiply by 1e6 (ms -> ns).
kExprI64UConvertI32, // -
...wasmI64Const(1e6), // -
kExprI64Mul, // -
kAtomicPrefix, kExprI32AtomicWait, 0x42, mem_idx, 0 // -
])
.exportFunc();
}
const module = builder.toModule();
const mem0 = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
const mem1 = new WebAssembly.Memory({initial: 2, maximum: 2, shared: true});
const mem0_value = 0;
const mem1_value = 1;
new Uint32Array(mem1.buffer).fill(mem1_value);
const imports = {
imp: {mem0: mem0, mem1: mem1}
};
const instance = new WebAssembly.Instance(module, imports);
const {notify0, notify1, wait0, wait1} = instance.exports;
const k10Ms = 10;
const k10s = 10000;
(function TestWaitNotEqual() {
print(arguments.callee.name);
assertEquals(kAtomicWaitNotEqual, wait0(0, mem0_value + 1, k10Ms));
assertEquals(kAtomicWaitNotEqual, wait1(0, mem1_value + 1, k10Ms));
})();
(function TestWaitTimeout() {
print(arguments.callee.name);
assertEquals(kAtomicWaitTimedOut, wait0(0, mem0_value, k10Ms));
assertEquals(kAtomicWaitTimedOut, wait1(0, mem1_value, k10Ms));
})();
(function TestWakeUpWorker() {
print(arguments.callee.name);
function workerCode() {
instance = undefined;
onmessage = function({data:msg}) {
if (!instance) {
instance = new WebAssembly.Instance(msg.module, msg.imports);
postMessage('instantiated');
return;
}
if (msg.action === 'wait0' || msg.action === 'wait1') {
let result = instance.exports[msg.action](...msg.arguments);
print(`[worker] ${msg.action} ->: ${result}`);
postMessage(result);
return;
}
postMessage(`Invalid action: ${msg.action}`);
}
}
let worker = new Worker(workerCode, {type: 'function'});
worker.postMessage({module: module, imports: imports});
assertEquals('instantiated', worker.getMessage());
const offset = 48;
for (let [mem_idx, mem_value] of [
[mem0_idx, mem0_value], [mem1_idx, mem1_value]]) {
print(`- memory ${mem_idx}`);
// Test "not equals".
worker.postMessage(
{action: `wait${mem_idx}`, arguments: [offset, mem_value + 1, k10Ms]});
assertEquals(kAtomicWaitNotEqual, worker.getMessage());
// Test "timed out".
worker.postMessage(
{action: `wait${mem_idx}`, arguments: [offset, mem_value, k10Ms]});
assertEquals(kAtomicWaitTimedOut, worker.getMessage());
// Test "ok".
worker.postMessage(
{action: `wait${mem_idx}`, arguments: [offset, mem_value, k10s]});
const started = performance.now();
let notify = mem_idx == 0 ? notify0 : notify1;
let notified;
while ((notified = notify(offset, 1)) === 0) {
const now = performance.now();
if (now - started > k10s) {
throw new Error('Could not notify worker within 10s');
}
}
assertEquals(1, notified);
assertEquals(kAtomicWaitOk, worker.getMessage());
}
worker.terminate();
})();

View File

@ -0,0 +1,306 @@
// Copyright 2023 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: --allow-natives-syntax
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
function allowOOM(fn) {
try {
fn();
} catch (e) {
const is_oom =
(e instanceof RangeError) && e.message.includes('Out of memory');
if (!is_oom) throw e;
}
}
function CreateBigSharedWasmMemory64(num_pages) {
let builder = new WasmModuleBuilder();
builder.addMemory64(num_pages, num_pages, true);
builder.exportMemoryAs('memory');
return builder.instantiate().exports.memory;
}
function WasmAtomicNotify(memory, offset, index, num) {
let builder = new WasmModuleBuilder();
let pages = memory.buffer.byteLength / kPageSize;
builder.addImportedMemory("m", "memory", pages, pages, "shared", "memory64");
builder.addFunction("main", makeSig([kWasmF64, kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprI64SConvertF64,
kExprLocalGet, 1,
kAtomicPrefix,
kExprAtomicNotify, /* alignment */ 2, ...wasmSignedLeb64(offset, 10)])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {m: {memory}});
return instance.exports.main(index, num);
}
function WasmI32AtomicWait(memory, offset, index, val, timeout) {
let builder = new WasmModuleBuilder();
let pages = memory.buffer.byteLength / kPageSize;
builder.addImportedMemory("m", "memory", pages, pages, "shared", "memory64");
builder.addFunction("main",
makeSig([kWasmF64, kWasmI32, kWasmF64], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprI64SConvertF64,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprI64SConvertF64,
kAtomicPrefix,
kExprI32AtomicWait, /* alignment */ 2, ...wasmSignedLeb64(offset, 10)])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {m: {memory}});
return instance.exports.main(index, val, timeout);
}
function WasmI64AtomicWait(memory, offset, index, val_low,
val_high, timeout) {
let builder = new WasmModuleBuilder();
let pages = memory.buffer.byteLength / kPageSize;
builder.addImportedMemory("m", "memory", pages, pages, "shared", "memory64");
// Wrapper for I64AtomicWait that takes two I32 values and combines to into
// I64 for the instruction parameter.
builder.addFunction("main",
makeSig([kWasmF64, kWasmI32, kWasmI32, kWasmF64], [kWasmI32]))
.addLocals(kWasmI64, 1)
.addBody([
kExprLocalGet, 1,
kExprI64UConvertI32,
kExprI64Const, 32,
kExprI64Shl,
kExprLocalGet, 2,
kExprI64UConvertI32,
kExprI64Ior,
kExprLocalSet, 4, // Store the created I64 value in local
kExprLocalGet, 0,
kExprI64SConvertF64,
kExprLocalGet, 4,
kExprLocalGet, 3,
kExprI64SConvertF64,
kAtomicPrefix,
kExprI64AtomicWait, /* alignment */ 3, ...wasmSignedLeb64(offset, 10)])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {m: {memory}});
return instance.exports.main(index, val_high, val_low, timeout);
}
function TestAtomicI32Wait(pages, offset) {
if (!%IsAtomicsWaitAllowed()) return;
print(arguments.callee.name);
let memory = CreateBigSharedWasmMemory64(pages);
assertEquals(kAtomicWaitNotEqual, WasmI32AtomicWait(memory, offset, 0, 42, -1));
assertEquals(kAtomicWaitTimedOut, WasmI32AtomicWait(memory, offset, 0, 0, 0));
assertEquals(kAtomicWaitNotEqual, WasmI32AtomicWait(memory, 0, offset, 42, -1));
assertEquals(kAtomicWaitTimedOut, WasmI32AtomicWait(memory, 0, offset, 0, 0));
let i32a = new Int32Array(memory.buffer);
i32a[offset / 4] = 1;
assertEquals(kAtomicWaitNotEqual, WasmI32AtomicWait(memory, offset, 0, 0, -1));
assertEquals(kAtomicWaitTimedOut, WasmI32AtomicWait(memory, offset, 0, 1, 0));
assertEquals(kAtomicWaitNotEqual, WasmI32AtomicWait(memory, 0, offset, 0, -1));
assertEquals(kAtomicWaitTimedOut, WasmI32AtomicWait(memory, 0, offset, 1, 0));
}
function TestAtomicI64Wait(pages, offset) {
if (!%IsAtomicsWaitAllowed()) return;
print(arguments.callee.name);
let memory = CreateBigSharedWasmMemory64(pages);
assertEquals(kAtomicWaitNotEqual, WasmI64AtomicWait(memory, offset, 0, 42, 0, -1));
assertEquals(kAtomicWaitTimedOut, WasmI64AtomicWait(memory, offset, 0, 0, 0, 0));
assertEquals(kAtomicWaitNotEqual, WasmI64AtomicWait(memory, 0, offset, 42, 0, -1));
assertEquals(kAtomicWaitTimedOut, WasmI64AtomicWait(memory, 0, offset, 0, 0, 0));
let i32a = new Int32Array(memory.buffer);
i32a[offset / 4] = 1;
i32a[(offset / 4) + 1] = 2;
assertEquals(kAtomicWaitNotEqual, WasmI64AtomicWait(memory, offset, 0, 2, 1, -1));
assertEquals(kAtomicWaitTimedOut, WasmI64AtomicWait(memory, offset, 0, 1, 2, 0));
assertEquals(kAtomicWaitNotEqual, WasmI64AtomicWait(memory, 0, offset, 2, 1, -1));
assertEquals(kAtomicWaitTimedOut, WasmI64AtomicWait(memory, 0, offset, 1, 2, 0));
}
//// WORKER ONLY TESTS
function TestWasmAtomicsInWorkers(OFFSET, INDEX, PAGES) {
// This test creates 4 workers that wait on consecutive (8 byte separated to
// satisfy alignments for all kinds of wait) memory locations to test various
// wait/wake combinations. For each combination, each thread waits 3 times
// expecting all 4 threads to be woken with wake(4) in first iteration, all 4
// to be woken with wake(5) in second iteration and, 3 and 1 to be woken in
// third iteration.
print(arguments.callee.name);
const INDEX_JS = OFFSET + INDEX;
let memory = CreateBigSharedWasmMemory64(PAGES);
let i32a = new Int32Array(memory.buffer);
const numWorkers = 4;
let workerScript = `onmessage = function({data:msg}) {
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
${WasmI32AtomicWait.toString()}
${WasmI64AtomicWait.toString()}
let id = msg.id;
let memory = msg.memory;
let INDEX = msg.INDEX;
let OFFSET = msg.OFFSET;
let INDEX_JS = OFFSET + INDEX;
let i32a = new Int32Array(memory.buffer);
// Indices are divided by 4 for Atomics.wait to convert them to index
// for Int32Array.
// For wasm-wake numWorkers threads.
let result = Atomics.wait(i32a, INDEX_JS / 4, 0);
postMessage(result);
// For wasm-wake numWorkers + 1 threads.
result = Atomics.wait(i32a, (INDEX_JS + 8) / 4, 0);
postMessage(result);
// For wasm-wake numWorkers - 1 threads.
result = Atomics.wait(i32a, (INDEX_JS + 16) / 4, 0);
postMessage(result);
// For js-wake numWorkers threads.
result = WasmI32AtomicWait(memory, OFFSET, INDEX + 24, 0, -1);
postMessage(result);
// For js-wake numWorkers + 1 threads.
result = WasmI32AtomicWait(memory, OFFSET, INDEX + 32, 0, -1);
postMessage(result);
// For js-wake numWorkers - 1 threads.
result = WasmI32AtomicWait(memory, OFFSET, INDEX + 40, 0, -1);
postMessage(result);
// For wasm-wake numWorkers threads.
result = WasmI32AtomicWait(memory, OFFSET, INDEX + 48, 0, -1);
postMessage(result);
// For wasm-wake numWorkers + 1 threads.
result = WasmI32AtomicWait(memory, OFFSET, INDEX + 56, 0, -1);
postMessage(result);
// For wasm-wake numWorkers - 1 threads.
result = WasmI32AtomicWait(memory, OFFSET, INDEX + 64, 0, -1);
postMessage(result);
// For js-wake numWorkers threads.
result = WasmI64AtomicWait(memory, OFFSET, INDEX + 72, 0, 0, -1);
postMessage(result);
// For js-wake numWorkers + 1 threads.
result = WasmI64AtomicWait(memory, OFFSET, INDEX + 80, 0, 0, -1);
postMessage(result);
// For js-wake numWorkers - 1 threads.
result = WasmI64AtomicWait(memory, OFFSET, INDEX + 88, 0, 0, -1);
postMessage(result);
// For wasm-wake numWorkers threads.
result = WasmI64AtomicWait(memory, OFFSET, INDEX + 96, 0, 0, -1);
postMessage(result);
// For wasm-wake numWorkers + 1 threads.
result = WasmI64AtomicWait(memory, OFFSET, INDEX + 104, 0, 0, -1);
postMessage(result);
// For wasm-wake numWorkers - 1 threads.
result = WasmI64AtomicWait(memory, OFFSET, INDEX + 112, 0, 0, -1);
postMessage(result);
};`;
let waitForAllWorkers = function(index) {
while (%AtomicsNumWaitersForTesting(i32a, index / 4) != numWorkers) {}
}
let jsWakeCheck = function(index, num, workers, msg) {
waitForAllWorkers(index);
let indexJs = index / 4;
if (num >= numWorkers) {
// If numWorkers or more is passed to wake, numWorkers workers should be
// woken.
assertEquals(numWorkers, Atomics.notify(i32a, indexJs, num));
} else {
// If num < numWorkers is passed to wake, num workers should be woken.
// Then the remaining workers are woken for the next part.
assertEquals(num, Atomics.notify(i32a, indexJs, num));
assertEquals(numWorkers-num, Atomics.notify(i32a, indexJs, numWorkers));
}
for (let id = 0; id < numWorkers; id++) {
assertEquals(msg, workers[id].getMessage());
}
};
let wasmWakeCheck = function(offset, index, num, workers, msg) {
waitForAllWorkers(offset + index);
if (num >= numWorkers) {
assertEquals(numWorkers, WasmAtomicNotify(memory, offset, index, num));
} else {
assertEquals(num, WasmAtomicNotify(memory, offset, index, num));
assertEquals(numWorkers-num,
WasmAtomicNotify(memory, offset, index, numWorkers));
}
for (let id = 0; id < numWorkers; id++) {
assertEquals(msg, workers[id].getMessage());
}
};
let workers = [];
for (let id = 0; id < numWorkers; id++) {
workers[id] = new Worker(workerScript, {type: 'string'});
workers[id].postMessage({id, memory, INDEX, OFFSET});
}
wasmWakeCheck(OFFSET, INDEX + 0, numWorkers, workers, "ok");
wasmWakeCheck(OFFSET, INDEX + 8, numWorkers + 1, workers, "ok");
wasmWakeCheck(OFFSET, INDEX + 16, numWorkers - 1, workers, "ok");
jsWakeCheck(INDEX_JS + 24, numWorkers, workers, kAtomicWaitOk);
jsWakeCheck(INDEX_JS + 32, numWorkers + 1, workers, kAtomicWaitOk);
jsWakeCheck(INDEX_JS + 40, numWorkers - 1, workers, kAtomicWaitOk);
wasmWakeCheck(OFFSET, INDEX + 48, numWorkers, workers, kAtomicWaitOk);
wasmWakeCheck(OFFSET, INDEX + 56, numWorkers + 1, workers, kAtomicWaitOk);
wasmWakeCheck(OFFSET, INDEX + 64, numWorkers - 1, workers, kAtomicWaitOk);
jsWakeCheck(INDEX_JS + 72, numWorkers, workers, kAtomicWaitOk);
jsWakeCheck(INDEX_JS + 80, numWorkers + 1, workers, kAtomicWaitOk);
jsWakeCheck(INDEX_JS + 88, numWorkers - 1, workers, kAtomicWaitOk);
wasmWakeCheck(OFFSET, INDEX + 96, numWorkers, workers, kAtomicWaitOk);
wasmWakeCheck(OFFSET, INDEX + 104, numWorkers + 1, workers, kAtomicWaitOk);
wasmWakeCheck(OFFSET, INDEX + 112, numWorkers - 1, workers, kAtomicWaitOk);
for (let id = 0; id < numWorkers; id++) {
workers[id].terminate();
}
}
const OFFSET = 4000;
const MEM_PAGES = 100;
const GB = 1024 * 1024 * 1024;
const BIG_OFFSET = 4294970000;
const BIG_MEM_PAGES = 5 * GB / kPageSize;
(function(){
TestAtomicI32Wait(MEM_PAGES, OFFSET);
TestAtomicI64Wait(MEM_PAGES, OFFSET);
if (this.Worker) {
TestWasmAtomicsInWorkers(OFFSET, 0, MEM_PAGES);
TestWasmAtomicsInWorkers(0, OFFSET, MEM_PAGES);
}
})();
// Tests require Big memory >4GB, they may fail because of OOM.
(function(){
allowOOM(() => TestAtomicI32Wait(BIG_MEM_PAGES, BIG_OFFSET));
allowOOM(() => TestAtomicI64Wait(BIG_MEM_PAGES, BIG_OFFSET));
if (this.Worker) {
allowOOM(() => TestWasmAtomicsInWorkers(BIG_OFFSET, 0, BIG_MEM_PAGES));
allowOOM(() => TestWasmAtomicsInWorkers(0, BIG_OFFSET, BIG_MEM_PAGES));
}
})();

View File

@ -0,0 +1,122 @@
// 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// TODO(gdeepti): If non-shared atomics are moving forward, ensure that
// the tests here are more comprehensive -i.e. reuse atomics.js/atomics64.js
// and cctests to run on both shared/non-shared memory.
(function TestCompileGenericAtomicOp() {
print(arguments.callee.name);
let memory = new WebAssembly.Memory({initial: 0, maximum: 10});
let builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix,
kExprI32AtomicAdd, 2, 0]);
builder.addImportedMemory("m", "imported_mem");
let module = new WebAssembly.Module(builder.toBuffer());
})();
(function TestCompileWasmAtomicNotify() {
print(arguments.callee.name);
let memory = new WebAssembly.Memory({initial: 0, maximum: 10});
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "memory", 0, 20);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix,
kExprAtomicNotify, 2, 0])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {m: {memory}});
})();
(function TestCompileWasmI32AtomicWait() {
print(arguments.callee.name);
let memory = new WebAssembly.Memory({initial: 0, maximum: 10});
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "memory", 0, 20);
builder.addFunction("main",
makeSig([kWasmI32, kWasmI32, kWasmF64], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprI64SConvertF64,
kAtomicPrefix,
kExprI32AtomicWait, 2, 0])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {m: {memory}});
})();
(function TestWasmAtomicNotifyResult() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "memory", 0, 20);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix,
kExprAtomicNotify, 2, 0])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let memory = new WebAssembly.Memory({initial: 1, maximum: 1});
let instance = new WebAssembly.Instance(module, {m: {memory}});
assertEquals(0, instance.exports.main(0, 100));
})();
(function TestWasmI32AtomicWaitTraps() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "memory", 0, 20);
builder.addFunction("main",
makeSig([kWasmI32, kWasmI32, kWasmF64], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprI64SConvertF64,
kAtomicPrefix,
kExprI32AtomicWait, 2, 0])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let memory = new WebAssembly.Memory({initial: 1, maximum: 1});
let instance = new WebAssembly.Instance(module, {m: {memory}});
assertThrows(() => instance.exports.main(0, 5, 0), WebAssembly.RuntimeError);
})();
(function TestWasmI64AtomicWaitTraps() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "memory", 0, 20);
builder.addFunction("main",
makeSig([kWasmI32, kWasmI32, kWasmF64], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI64UConvertI32,
kExprLocalGet, 2,
kExprI64SConvertF64,
kAtomicPrefix,
kExprI64AtomicWait, 3, 0])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let memory = new WebAssembly.Memory({initial: 1, maximum: 1});
let instance = new WebAssembly.Instance(module, {m: {memory}});
assertThrows(() => instance.exports.main(0, 5, 0), WebAssembly.RuntimeError);
})();

View File

@ -0,0 +1,505 @@
// Copyright 2018 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.
// This test might time out if the search space for a sequential
// interleaving becomes to large. However, it should never fail.
// Note that results of this test are flaky by design. While the test is
// deterministic with a fixed seed, bugs may introduce non-determinism.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const kDebug = false;
const kSequenceLength = 256;
const kNumberOfWorker = 4;
const kNumberOfSteps = 10000000;
const kFirstOpcodeWithInput = 3;
const kFirstOpcodeWithoutOutput = 3;
const kLastOpcodeWithoutOutput = 5;
const opCodes = [
kExprI32AtomicLoad, kExprI32AtomicLoad8U, kExprI32AtomicLoad16U,
kExprI32AtomicStore, kExprI32AtomicStore8U, kExprI32AtomicStore16U,
kExprI32AtomicAdd, kExprI32AtomicAdd8U, kExprI32AtomicAdd16U,
kExprI32AtomicSub, kExprI32AtomicSub8U, kExprI32AtomicSub16U,
kExprI32AtomicAnd, kExprI32AtomicAnd8U, kExprI32AtomicAnd16U,
kExprI32AtomicOr, kExprI32AtomicOr8U, kExprI32AtomicOr16U,
kExprI32AtomicXor, kExprI32AtomicXor8U, kExprI32AtomicXor16U,
kExprI32AtomicExchange, kExprI32AtomicExchange8U, kExprI32AtomicExchange16U
];
const opCodeNames = [
'kExprI32AtomicLoad', 'kExprI32AtomicLoad8U',
'kExprI32AtomicLoad16U', 'kExprI32AtomicStore',
'kExprI32AtomicStore8U', 'kExprI32AtomicStore16U',
'kExprI32AtomicAdd', 'kExprI32AtomicAdd8U',
'kExprI32AtomicAdd16U', 'kExprI32AtomicSub',
'kExprI32AtomicSub8U', 'kExprI32AtomicSub16U',
'kExprI32AtomicAnd', 'kExprI32AtomicAnd8U',
'kExprI32AtomicAnd16U', 'kExprI32AtomicOr',
'kExprI32AtomicOr8U', 'kExprI32AtomicOr16U',
'kExprI32AtomicXor', 'kExprI32AtomicXor8U',
'kExprI32AtomicXor16U', 'kExprI32AtomicExchange',
'kExprI32AtomicExchange8U', 'kExprI32AtomicExchange16U'
];
let kMaxMemPages = 10;
let gSharedMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gSharedMemoryView = new Int32Array(gSharedMemory.buffer);
let gPrivateMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gPrivateMemoryView = new Int32Array(gPrivateMemory.buffer);
class Operation {
constructor(opcode, input, offset) {
this.opcode = opcode != undefined ? opcode : Operation.nextOpcode();
this.size = Operation.opcodeToSize(this.opcode);
this.input = input != undefined ? input : Operation.inputForSize(this.size);
this.offset =
offset != undefined ? offset : Operation.offsetForSize(this.size);
}
static nextOpcode() {
let random = Math.random();
return Math.floor(random * opCodes.length);
}
static opcodeToSize(opcode) {
// Instructions are ordered in 32, 8, 16 bits size
return [32, 8, 16][opcode % 3];
}
static opcodeToAlignment(opcode) {
// Instructions are ordered in 32, 8, 16 bits size
return [2, 0, 1][opcode % 3];
}
static inputForSize(size) {
let random = Math.random();
// Avoid 32 bit overflow for integer here :(
return Math.floor(random * (1 << (size - 1)) * 2);
}
static offsetForSize(size) {
// Pick an offset in bytes between 0 and 7.
let offset = Math.floor(Math.random() * 8);
// Make sure the offset matches the required alignment by masking out the
// lower bits.
let size_in_bytes = size / 8;
let mask = ~(size_in_bytes - 1);
return offset & mask;
}
get wasmOpcode() {
// [opcode, alignment, offset]
return [
opCodes[this.opcode], Operation.opcodeToAlignment(this.opcode),
this.offset
];
}
get hasInput() {
return this.opcode >= kFirstOpcodeWithInput;
}
get hasOutput() {
return this.opcode < kFirstOpcodeWithoutOutput ||
this.opcode > kLastOpcodeWithoutOutput;
}
truncateResultBits(low, high) {
// Shift the lower part. For offsets greater four it drops out of the
// visible window.
let shiftedL = this.offset >= 4 ? 0 : low >>> (this.offset * 8);
// The higher part is zero for offset 0, left shifted for [1..3] and right
// shifted for [4..7].
let shiftedH = this.offset == 0 ?
0 :
this.offset >= 4 ? high >>> (this.offset - 4) * 8 :
high << ((4 - this.offset) * 8);
let value = shiftedL | shiftedH;
switch (this.size) {
case 8:
return value & 0xFF;
case 16:
return value & 0xFFFF;
case 32:
return value;
default:
throw 'Unexpected size: ' + this.size;
}
}
static get builder() {
if (!Operation.__builder) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
Operation.__builder = builder;
}
return Operation.__builder;
}
static get exports() {
if (!Operation.__instance) {
return {};
}
return Operation.__instance.exports;
}
static set instance(instance) {
Operation.__instance = instance;
}
compute(state) {
let evalFun = Operation.exports[this.key];
if (!evalFun) {
let builder = Operation.builder;
let body = [
// Load address of low 32 bits.
kExprI32Const, 0,
// Load expected value.
kExprLocalGet, 0, kExprI32StoreMem, 2, 0,
// Load address of high 32 bits.
kExprI32Const, 4,
// Load expected value.
kExprLocalGet, 1, kExprI32StoreMem, 2, 0,
// Load address of where our window starts.
kExprI32Const, 0,
// Load input if there is one.
...(this.hasInput ? [kExprLocalGet, 2] : []),
// Perform operation.
kAtomicPrefix, ...this.wasmOpcode,
// Drop output if it had any.
...(this.hasOutput ? [kExprDrop] : []),
// Load resulting value.
kExprI32Const, 0, kExprI32LoadMem, 2, 0,
// Return.
kExprReturn
]
builder.addFunction(this.key, kSig_i_iii)
.addBody(body)
.exportAs(this.key);
// Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer());
Operation.instance =
new WebAssembly.Instance(module, {m: {imported_mem: gPrivateMemory}});
evalFun = Operation.exports[this.key];
}
let result = evalFun(state.low, state.high, this.input);
let ta = gPrivateMemoryView;
if (kDebug) {
print(
state.high + ':' + state.low + ' ' + this.toString() + ' -> ' +
ta[1] + ':' + ta[0]);
}
if (result != ta[0]) throw '!';
return {low: ta[0], high: ta[1]};
}
toString() {
return opCodeNames[this.opcode] + '[+' + this.offset + '] ' + this.input;
}
get key() {
return this.opcode + '-' + this.offset;
}
}
class State {
constructor(low, high, indices, count) {
this.low = low;
this.high = high;
this.indices = indices;
this.count = count;
}
isFinal() {
return (this.count == kNumberOfWorker * kSequenceLength);
}
toString() {
return this.high + ':' + this.low + ' @ ' + this.indices;
}
}
function makeSequenceOfOperations(size) {
let result = new Array(size);
for (let i = 0; i < size; i++) {
result[i] = new Operation();
}
return result;
}
function toSLeb128(val) {
let result = [];
while (true) {
let v = val & 0x7f;
val = val >> 7;
let msbIsSet = (v & 0x40) || false;
if (((val == 0) && !msbIsSet) || ((val == -1) && msbIsSet)) {
result.push(v);
break;
}
result.push(v | 0x80);
}
return result;
}
function generateFunctionBodyForSequence(sequence) {
// We expect the int32* to perform ops on as arg 0 and
// the int32* for our value log as arg1. Argument 2 gives
// an int32* we use to count down spinning workers.
let body = [];
// Initially, we spin until all workers start running.
if (!kDebug) {
body.push(
// Decrement the wait count.
kExprLocalGet, 2, kExprI32Const, 1, kAtomicPrefix, kExprI32AtomicSub, 2,
0,
// Spin until zero.
kExprLoop, kWasmVoid, kExprLocalGet, 2, kAtomicPrefix,
kExprI32AtomicLoad, 2, 0, kExprI32Const, 0, kExprI32GtU, kExprBrIf, 0,
kExprEnd);
}
for (let operation of sequence) {
body.push(
// Pre-load address of results sequence pointer for later.
kExprLocalGet, 1,
// Load address where atomic pointers are stored.
kExprLocalGet, 0,
// Load the second argument if it had any.
...(operation.hasInput ?
[kExprI32Const, ...toSLeb128(operation.input)] :
[]),
// Perform operation
kAtomicPrefix, ...operation.wasmOpcode,
// Generate fake output in needed.
...(operation.hasOutput ? [] : [kExprI32Const, 0]),
// Store read intermediate to sequence.
kExprI32StoreMem, 2, 0,
// Increment result sequence pointer.
kExprLocalGet, 1, kExprI32Const, 4, kExprI32Add, kExprLocalSet, 1);
}
// Return end of sequence index.
body.push(kExprLocalGet, 1, kExprReturn);
return body;
}
function getSequence(start, end) {
return new Int32Array(
gSharedMemory.buffer, start,
(end - start) / Int32Array.BYTES_PER_ELEMENT);
}
function spawnWorkers() {
function workerCode() {
onmessage = function({data:msg}) {
if (msg.module) {
let module = msg.module;
let mem = msg.mem;
this.instance = new WebAssembly.Instance(module, {m: {imported_mem: mem}});
postMessage({instantiated: true});
} else {
let address = msg.address;
let sequence = msg.sequence;
let index = msg.index;
let spin = msg.spin;
let result = instance.exports["worker" + index](address, sequence, spin);
postMessage({index: index, sequence: sequence, result: result});
}
}
}
let workers = [];
for (let i = 0; i < kNumberOfWorker; i++) {
let worker = new Worker(workerCode, {type: 'function'});
workers.push(worker);
}
return workers;
}
function instantiateModuleInWorkers(workers) {
for (let worker of workers) {
worker.postMessage({module: module, mem: gSharedMemory});
let msg = worker.getMessage();
if (!msg.instantiated) throw 'Worker failed to instantiate';
}
}
function executeSequenceInWorkers(workers) {
for (i = 0; i < workers.length; i++) {
let worker = workers[i];
worker.postMessage({
index: i,
address: 0,
spin: 16,
sequence: 32 + ((kSequenceLength * 4) + 32) * i
});
// In debug mode, keep execution sequential.
if (kDebug) {
let msg = worker.getMessage();
results[msg.index] = getSequence(msg.sequence, msg.result);
}
}
}
function selectMatchingWorkers(state) {
let matching = [];
let indices = state.indices;
for (let i = 0; i < indices.length; i++) {
let index = indices[i];
if (index >= kSequenceLength) continue;
// We need to project the expected value to the number of bits this
// operation will read at runtime.
let expected =
sequences[i][index].truncateResultBits(state.low, state.high);
let hasOutput = sequences[i][index].hasOutput;
if (!hasOutput || (results[i][index] == expected)) {
matching.push(i);
}
}
return matching;
}
function computeNextState(state, advanceIdx) {
let newIndices = state.indices.slice();
let sequence = sequences[advanceIdx];
let operation = sequence[state.indices[advanceIdx]];
newIndices[advanceIdx]++;
let {low, high} = operation.compute(state);
return new State(low, high, newIndices, state.count + 1);
}
function findSequentialOrdering() {
let startIndices = new Array(results.length);
let steps = 0;
startIndices.fill(0);
let matchingStates = [new State(0, 0, startIndices, 0)];
while (matchingStates.length > 0) {
let current = matchingStates.pop();
if (kDebug) {
print(current);
}
let matchingResults = selectMatchingWorkers(current);
if (matchingResults.length == 0) {
continue;
}
for (let match of matchingResults) {
let newState = computeNextState(current, match);
if (newState.isFinal()) {
return true;
}
matchingStates.push(newState);
}
if (steps++ > kNumberOfSteps) {
print('Search timed out, aborting...');
return true;
}
}
// We have no options left.
return false;
}
// Helpful for debugging failed tests.
function loadSequencesFromStrings(inputs) {
let reverseOpcodes = {};
for (let i = 0; i < opCodeNames.length; i++) {
reverseOpcodes[opCodeNames[i]] = i;
}
let sequences = [];
let parseRE = /([a-zA-Z0-9]*)\[\+([0-9])\] ([\-0-9]*)/;
for (let input of inputs) {
let parts = input.split(',');
let sequence = [];
for (let part of parts) {
let parsed = parseRE.exec(part);
sequence.push(
new Operation(reverseOpcodes[parsed[1]], parsed[3], parsed[2] | 0));
}
sequences.push(sequence);
}
return sequences;
}
// Helpful for debugging failed tests.
function loadResultsFromStrings(inputs) {
let results = [];
for (let input of inputs) {
let parts = input.split(',');
let result = [];
for (let number of parts) {
result.push(number | 0);
}
results.push(result);
}
return results;
}
let sequences = [];
let results = [];
let builder = new WasmModuleBuilder();
builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
for (let i = 0; i < kNumberOfWorker; i++) {
sequences[i] = makeSequenceOfOperations(kSequenceLength);
builder.addFunction('worker' + i, kSig_i_iii)
.addBody(generateFunctionBodyForSequence(sequences[i]))
.exportAs('worker' + i);
}
// Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer());
let instance =
new WebAssembly.Instance(module, {m: {imported_mem: gSharedMemory}});
// Spawn off the workers and run the sequences.
let workers = spawnWorkers();
// Set spin count.
gSharedMemoryView[4] = kNumberOfWorker;
instantiateModuleInWorkers(workers);
executeSequenceInWorkers(workers);
if (!kDebug) {
// Collect results, d8 style.
for (let worker of workers) {
let msg = worker.getMessage();
results[msg.index] = getSequence(msg.sequence, msg.result);
}
}
// Terminate all workers.
for (let worker of workers) {
worker.terminate();
}
// In debug mode, print sequences and results.
if (kDebug) {
for (let result of results) {
print(result);
}
for (let sequence of sequences) {
print(sequence);
}
}
// Try to reconstruct a sequential ordering.
let passed = findSequentialOrdering();
if (passed) {
print('PASS');
} else {
for (let i = 0; i < kNumberOfWorker; i++) {
print('Worker ' + i);
print(sequences[i]);
print(results[i]);
}
print('FAIL');
quit(-1);
}

775
deps/v8/test/mjsunit/wasm/atomics.js vendored Normal file
View File

@ -0,0 +1,775 @@
// Copyright 2017 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const kMemtypeSize64 = 8;
const kMemtypeSize32 = 4;
const kMemtypeSize16 = 2;
const kMemtypeSize8 = 1;
function Add(a, b) { return a + b; }
function Sub(a, b) { return a - b; }
function And(a, b) { return a & b; }
function Or(a, b) { return a | b; }
function Xor(a, b) { return a ^ b; }
function Exchange(a, b) { return b; }
let maxSize = 10;
let memory = new WebAssembly.Memory({initial: 1, maximum: maxSize, shared: true});
function GetAtomicBinOpFunction(wasmExpression, alignment, offset) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix,
wasmExpression, alignment, offset])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module,
{m: {imported_mem: memory}});
return instance.exports.main;
}
function GetI64AtomicBinOpFunction(wasmExpression, alignment, offset) {
const kSig_l_il = makeSig([kWasmI32, kWasmI64], [kWasmI64]);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
builder.addFunction("main", kSig_l_il)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix,
wasmExpression, alignment, offset
])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module,
{m: {imported_mem: memory}});
return instance.exports.main;
}
function GetAtomicCmpExchangeFunction(wasmExpression, alignment, offset) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
builder.addFunction("main", kSig_i_iii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kAtomicPrefix,
wasmExpression, alignment, ...wasmSignedLeb(offset, 5)])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module,
{m: {imported_mem: memory}});
return instance.exports.main;
}
function GetAtomicLoadFunction(wasmExpression, alignment, offset) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kAtomicPrefix,
wasmExpression, alignment, offset])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module,
{m: {imported_mem: memory}});
return instance.exports.main;
}
function GetAtomicStoreFunction(wasmExpression, alignment, offset) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
builder.addFunction("main", kSig_v_ii)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kAtomicPrefix,
wasmExpression, alignment, offset])
.exportAs("main");
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module,
{m: {imported_mem: memory}});
return instance.exports.main;
}
function VerifyBoundsCheck64(func, memtype_size) {
const kPageSize = 65536;
// Test out of bounds at boundary
for (let i = memory.buffer.byteLength - memtype_size + 1;
i < memory.buffer.byteLength + memtype_size + 4; i++) {
assertTrapsOneOf(
// If an underlying platform uses traps for a bounds check,
// kTrapUnalignedAccess will be thrown before kTrapMemOutOfBounds.
// Otherwise, kTrapMemOutOfBounds will be first.
[kTrapMemOutOfBounds, kTrapUnalignedAccess],
() => func(i, 5n, 10n)
);
}
// Test out of bounds at maximum + 1
assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5n, 1n));
}
function VerifyBoundsCheck(func, memtype_size) {
const kPageSize = 65536;
// Test out of bounds at boundary
for (let i = memory.buffer.byteLength - memtype_size + 1;
i < memory.buffer.byteLength + memtype_size + 4; i++) {
assertTrapsOneOf(
// If an underlying platform uses traps for a bounds check,
// kTrapUnalignedAccess will be thrown before kTrapMemOutOfBounds.
// Otherwise, kTrapMemOutOfBounds will be first.
[kTrapMemOutOfBounds, kTrapUnalignedAccess],
() => func(i, 5, 10)
);
}
// Test out of bounds at maximum + 1
assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5, 1));
}
// Test many elements in the small range, make bigger steps later. This is still
// O(2^n), but takes 213 steps to reach 2^32.
const inc = i => i + Math.floor(i/10) + 1;
function Test32Op(operation, func) {
let i32 = new Uint32Array(memory.buffer);
for (let i = 0; i < i32.length; i = inc(i)) {
let expected = 0x9cedf00d;
let value = 0x11111111;
i32[i] = expected;
assertEquals(expected, func(i * kMemtypeSize32, value) >>> 0);
assertEquals(operation(expected, value) >>> 0, i32[i]);
}
VerifyBoundsCheck(func, kMemtypeSize32);
}
function Test16Op(operation, func) {
let i16 = new Uint16Array(memory.buffer);
for (let i = 0; i < i16.length; i = inc(i)) {
let expected = 0xd00d;
let value = 0x1111;
i16[i] = expected;
assertEquals(expected, func(i * kMemtypeSize16, value));
assertEquals(operation(expected, value), i16[i]);
}
VerifyBoundsCheck(func, kMemtypeSize16);
}
function Test8Op(operation, func) {
let i8 = new Uint8Array(memory.buffer);
for (let i = 0; i < i8.length; i = inc(i)) {
let expected = 0xbe;
let value = 0x12;
i8[i] = expected;
assertEquals(expected, func(i * kMemtypeSize8, value));
assertEquals(operation(expected, value), i8[i]);
}
VerifyBoundsCheck(func, kMemtypeSize8, 10);
}
function Test64Op(operation, func) {
let i64 = new BigUint64Array(memory.buffer);
for (let i = 0; i < i64.length; i = inc(i)) {
let expected = 987659876543210n;
let value = 111111111111111n;
i64[i] = expected;
assertEquals(expected, func(i * kMemtypeSize64, value));
assertEquals(operation(expected, value), i64[i]);
}
VerifyBoundsCheck64(func, kMemtypeSize64);
}
function Test32Op64(operation, func) {
let i32 = new Uint32Array(memory.buffer);
for (let i = 0; i < i32.length; i = inc(i)) {
let expected = 123456;
let value = 111111n;
i32[i] = expected;
assertEquals(expected, Number(func(i * kMemtypeSize32, value)));
assertEquals(operation(expected, Number(value)), i32[i]);
}
VerifyBoundsCheck64(func, kMemtypeSize32);
}
function Test16Op64(operation, func) {
let i16 = new Uint16Array(memory.buffer);
for (let i = 0; i < i16.length; i = inc(i)) {
let expected = 0xd00d;
let value = 0x1111n;
i16[i] = expected;
assertEquals(expected, Number(func(i * kMemtypeSize16, value)));
assertEquals(operation(expected, Number(value)), i16[i]);
}
VerifyBoundsCheck64(func, kMemtypeSize16);
}
function Test8Op64(operation, func) {
let i8 = new Uint8Array(memory.buffer);
for (let i = 0; i < i8.length; i = inc(i)) {
let expected = 0xbe;
let value = 0x12n;
i8[i] = expected;
assertEquals(expected, Number(func(i * kMemtypeSize8, value)));
assertEquals(operation(expected, Number(value)), i8[i]);
}
VerifyBoundsCheck64(func, kMemtypeSize8, 10);
}
(function TestAtomicAdd() {
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 0);
Test32Op(Add, wasmAdd);
})();
(function TestAtomicAdd16U() {
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd16U, 1, 0);
Test16Op(Add, wasmAdd);
})();
(function TestAtomicAdd8U() {
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd8U, 0, 0);
Test8Op(Add, wasmAdd);
})();
(function TestI64AtomicAdd() {
print(arguments.callee.name);
let wasmAdd = GetI64AtomicBinOpFunction(kExprI64AtomicAdd, 3, 0);
Test64Op(Add, wasmAdd);
})();
(function TestI64AtomicAdd32U() {
print(arguments.callee.name);
let wasmAdd = GetI64AtomicBinOpFunction(kExprI64AtomicAdd32U, 2, 0);
Test32Op64(Add, wasmAdd);
})();
(function TestI64AtomicAdd16U() {
print(arguments.callee.name);
let wasmAdd = GetI64AtomicBinOpFunction(kExprI64AtomicAdd16U, 1, 0);
Test16Op64(Add, wasmAdd);
})();
(function TestI64AtomicAdd8U() {
print(arguments.callee.name);
let wasmAdd = GetI64AtomicBinOpFunction(kExprI64AtomicAdd8U, 0, 0);
Test8Op64(Add, wasmAdd);
})();
(function TestAtomicSub() {
print(arguments.callee.name);
let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub, 2, 0);
Test32Op(Sub, wasmSub);
})();
(function TestAtomicSub16U() {
print(arguments.callee.name);
let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub16U, 1, 0);
Test16Op(Sub, wasmSub);
})();
(function TestAtomicSub8U() {
print(arguments.callee.name);
let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub8U, 0, 0);
Test8Op(Sub, wasmSub);
})();
(function TestI64AtomicSub() {
print(arguments.callee.name);
let wasmSub = GetI64AtomicBinOpFunction(kExprI64AtomicSub, 3, 0);
Test64Op(Sub, wasmSub);
})();
(function TestI64AtomicSub32U() {
print(arguments.callee.name);
let wasmSub = GetI64AtomicBinOpFunction(kExprI64AtomicSub32U, 2, 0);
Test32Op64(Sub, wasmSub);
})();
(function TestI64AtomicSub16U() {
print(arguments.callee.name);
let wasmSub = GetI64AtomicBinOpFunction(kExprI64AtomicSub16U, 1, 0);
Test16Op64(Sub, wasmSub);
})();
(function TestI64AtomicSub8U() {
print(arguments.callee.name);
let wasmSub = GetI64AtomicBinOpFunction(kExprI64AtomicSub8U, 0, 0);
Test8Op64(Sub, wasmSub);
})();
(function TestAtomicAnd() {
print(arguments.callee.name);
let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd, 2, 0);
Test32Op(And, wasmAnd);
})();
(function TestAtomicAnd16U() {
print(arguments.callee.name);
let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd16U, 1, 0);
Test16Op(And, wasmAnd);
})();
(function TestAtomicAnd8U() {
print(arguments.callee.name);
let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd8U, 0, 0);
Test8Op(And, wasmAnd);
})();
(function TestI64AtomicAnd() {
print(arguments.callee.name);
let wasmAnd = GetI64AtomicBinOpFunction(kExprI64AtomicAnd, 3, 0);
Test64Op(And, wasmAnd);
})();
(function TestI64AtomicAnd32U() {
print(arguments.callee.name);
let wasmAnd = GetI64AtomicBinOpFunction(kExprI64AtomicAnd32U, 2, 0);
Test32Op64(And, wasmAnd);
})();
(function TestI64AtomicAnd16U() {
print(arguments.callee.name);
let wasmAnd = GetI64AtomicBinOpFunction(kExprI64AtomicAnd16U, 1, 0);
Test16Op64(And, wasmAnd);
})();
(function TestI64AtomicAnd8U() {
print(arguments.callee.name);
let wasmAnd = GetI64AtomicBinOpFunction(kExprI64AtomicAnd8U, 0, 0);
Test8Op64(And, wasmAnd);
})();
(function TestAtomicOr() {
print(arguments.callee.name);
let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr, 2, 0);
Test32Op(Or, wasmOr);
})();
(function TestAtomicOr16U() {
print(arguments.callee.name);
let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr16U, 1, 0);
Test16Op(Or, wasmOr);
})();
(function TestAtomicOr8U() {
print(arguments.callee.name);
let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr8U, 0, 0);
Test8Op(Or, wasmOr);
})();
(function TestI64AtomicOr() {
print(arguments.callee.name);
let wasmOr = GetI64AtomicBinOpFunction(kExprI64AtomicOr, 3, 0);
Test64Op(Or, wasmOr);
})();
(function TestI64AtomicOr32U() {
print(arguments.callee.name);
let wasmOr = GetI64AtomicBinOpFunction(kExprI64AtomicOr32U, 2, 0);
Test32Op64(Or, wasmOr);
})();
(function TestI64AtomicOr16U() {
print(arguments.callee.name);
let wasmOr = GetI64AtomicBinOpFunction(kExprI64AtomicOr16U, 1, 0);
Test16Op64(Or, wasmOr);
})();
(function TestI64AtomicOr8U() {
print(arguments.callee.name);
let wasmOr = GetI64AtomicBinOpFunction(kExprI64AtomicOr8U, 0, 0);
Test8Op64(Or, wasmOr);
})();
(function TestAtomicXor() {
print(arguments.callee.name);
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor, 2, 0);
Test32Op(Xor, wasmXor);
})();
(function TestAtomicXor16U() {
print(arguments.callee.name);
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor16U, 1, 0);
Test16Op(Xor, wasmXor);
})();
(function TestAtomicXor8U() {
print(arguments.callee.name);
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor8U, 0, 0);
Test8Op(Xor, wasmXor);
})();
(function TestI64AtomicXor() {
print(arguments.callee.name);
let wasmXor = GetI64AtomicBinOpFunction(kExprI64AtomicXor, 3, 0);
Test64Op(Xor, wasmXor);
})();
(function TestI64AtomicXor32U() {
print(arguments.callee.name);
let wasmXor = GetI64AtomicBinOpFunction(kExprI64AtomicXor32U, 2, 0);
Test32Op64(Xor, wasmXor);
})();
(function TestI64AtomicXor16U() {
print(arguments.callee.name);
let wasmXor = GetI64AtomicBinOpFunction(kExprI64AtomicXor16U, 1, 0);
Test16Op64(Xor, wasmXor);
})();
(function TestI64AtomicXor8U() {
print(arguments.callee.name);
let wasmXor = GetI64AtomicBinOpFunction(kExprI64AtomicXor8U, 0, 0);
Test8Op64(Xor, wasmXor);
})();
(function TestAtomicExchange() {
print(arguments.callee.name);
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange, 2, 0);
Test32Op(Exchange, wasmExchange);
})();
(function TestAtomicExchange16U() {
print(arguments.callee.name);
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange16U, 1, 0);
Test16Op(Exchange, wasmExchange);
})();
(function TestAtomicExchange8U() {
print(arguments.callee.name);
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange8U, 0, 0);
Test8Op(Exchange, wasmExchange);
})();
(function TestI64AtomicExchange() {
print(arguments.callee.name);
let wasmExchange = GetI64AtomicBinOpFunction(kExprI64AtomicExchange, 3, 0);
Test64Op(Exchange, wasmExchange);
})();
(function TestI64AtomicExchange32U() {
print(arguments.callee.name);
let wasmExchange = GetI64AtomicBinOpFunction(kExprI64AtomicExchange32U, 2, 0);
Test32Op64(Exchange, wasmExchange);
})();
(function TestI64AtomicExchange16U() {
print(arguments.callee.name);
let wasmExchange = GetI64AtomicBinOpFunction(kExprI64AtomicExchange16U, 1, 0);
Test16Op64(Exchange, wasmExchange);
})();
(function TestI64AtomicExchange8U() {
print(arguments.callee.name);
let wasmExchange = GetI64AtomicBinOpFunction(kExprI64AtomicExchange8U, 0, 0);
Test8Op64(Exchange, wasmExchange);
})();
function TestCmpExchange(func, buffer, params, size, offset = 0) {
for (let i = 0; i + (offset / size) < buffer.length; i = inc(i)) {
for (let j = 0; j < params.length; j++) {
for (let k = 0; k < params.length; k++) {
buffer[i + (offset / size)] = params[j];
let loaded = func(i * size, params[k], params[j]) >>> 0;
let expected = (params[k] == loaded) ? params[j] : loaded;
assertEquals(loaded, params[j]);
assertEquals(expected, buffer[i + (offset / size)]);
}
}
}
VerifyBoundsCheck(func, size);
}
(function TestAtomicCompareExchange() {
print(arguments.callee.name);
// Offset is big enough to not fit in a 12-bit immediate on arm64, but small
// enough to fit in the maxSize wasm pages.
const offset = 0x1234;
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, offset);
let i32 = new Uint32Array(memory.buffer);
let params = [0x00000001, 0x00000555, 0x00099999, 0xffffffff];
TestCmpExchange(wasmCmpExchange, i32, params, kMemtypeSize32, offset);
})();
(function TestAtomicCompareExchange16U() {
print(arguments.callee.name);
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange16U, 1, 0);
let i16 = new Uint16Array(memory.buffer);
let params = [0x0001, 0x0555, 0x9999];
TestCmpExchange(wasmCmpExchange, i16, params, kMemtypeSize16);
})();
(function TestAtomicCompareExchange8U() {
print(arguments.callee.name);
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange8U, 0, 0);
let i8 = new Uint8Array(memory.buffer);
let params = [0x01, 0x0d, 0xf9];
TestCmpExchange(wasmCmpExchange, i8, params, kMemtypeSize8);
})();
function TestLoad(func, buffer, value, size) {
for (let i = 0; i < buffer.length; i = inc(i)) {
buffer[i] = value;
assertEquals(value, func(i * size) >>> 0);
}
VerifyBoundsCheck(func, size);
}
(function TestAtomicLoad() {
print(arguments.callee.name);
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad, 2, 0);
let i32 = new Uint32Array(memory.buffer);
let value = 0xacedaced;
TestLoad(wasmLoad, i32, value, kMemtypeSize32);
})();
(function TestAtomicLoad16U() {
print(arguments.callee.name);
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
let i16 = new Uint16Array(memory.buffer);
let value = 0xaced;
TestLoad(wasmLoad, i16, value, kMemtypeSize16);
})();
(function TestAtomicLoad8U() {
print(arguments.callee.name);
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad8U, 0, 0);
let i8 = new Uint8Array(memory.buffer);
let value = 0xac;
TestLoad(wasmLoad, i8, value, kMemtypeSize8);
})();
function TestStore(func, buffer, value, size) {
for (let i = 0; i < buffer.length; i = inc(i)) {
func(i * size, value)
assertEquals(value, buffer[i]);
}
VerifyBoundsCheck(func, size);
}
(function TestAtomicStore() {
print(arguments.callee.name);
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
let i32 = new Uint32Array(memory.buffer);
let value = 0xacedaced;
TestStore(wasmStore, i32, value, kMemtypeSize32);
})();
(function TestAtomicStore16U() {
print(arguments.callee.name);
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore16U, 1, 0);
let i16 = new Uint16Array(memory.buffer);
let value = 0xaced;
TestStore(wasmStore, i16, value, kMemtypeSize16);
})();
(function TestAtomicStore8U() {
print(arguments.callee.name);
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore8U, 0, 0);
let i8 = new Uint8Array(memory.buffer);
let value = 0xac;
TestCmpExchange(wasmStore, i8, value, kMemtypeSize8);
})();
(function TestAtomicLoadStoreOffset() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let memory = new WebAssembly.Memory({
initial: 16, maximum: 128, shared: true});
builder.addImportedMemory("m", "imported_mem", 16, 128, "shared");
builder.addFunction("loadStore", kSig_i_v)
.addBody([
kExprI32Const, 16,
kExprI32Const, 20,
kAtomicPrefix,
kExprI32AtomicStore, 2, 0xFC, 0xFF, 0x3a,
kExprI32Const, 16,
kAtomicPrefix,
kExprI32AtomicLoad, 2, 0xFC, 0xFF, 0x3a])
.exportAs("loadStore");
builder.addFunction("storeOob", kSig_v_v)
.addBody([
kExprI32Const, 16,
kExprI32Const, 20,
kAtomicPrefix,
kExprI32AtomicStore, 2, 0xFC, 0xFF, 0xFF, 0x3a])
.exportAs("storeOob");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
let buf = memory.buffer;
assertEquals(20, instance.exports.loadStore());
assertTraps(kTrapMemOutOfBounds, instance.exports.storeOob);
})();
(function TestAtomicOpinLoop() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
let memory = new WebAssembly.Memory({
initial: 16, maximum: 128, shared: true});
builder.addImportedMemory("m", "imported_mem", 16, 128, "shared");
builder.addFunction("main", kSig_i_v)
.addBody([
kExprLoop, kWasmVoid,
kExprI32Const, 16,
kExprI32Const, 20,
kAtomicPrefix,
kExprI32AtomicStore, 2, 0,
kExprI32Const, 16,
kAtomicPrefix,
kExprI32AtomicLoad, 2, 0,
kExprReturn,
kExprEnd,
kExprI32Const, 0
])
.exportFunc();
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
assertEquals(20, instance.exports.main());
})();
(function TestUnalignedAtomicAccesses() {
print(arguments.callee.name);
let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 17);
assertTraps(kTrapUnalignedAccess, () => wasmAdd(4, 1001));
let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
assertTraps(kTrapUnalignedAccess, () => wasmLoad(15));
let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
assertTraps(kTrapUnalignedAccess, () => wasmStore(22, 5));
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0x16);
assertTraps(kTrapUnalignedAccess, () => wasmCmpExchange(11, 6, 5));
// Building functions with bad alignment should fail to compile
assertThrows(() => GetAtomicBinOpFunction(kExprI32AtomicSub16U, 3, 0),
WebAssembly.CompileError);
})();
function CmpExchgLoop(opcode, alignment) {
print("TestI64AtomicCompareExchangeLoop" + alignment);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, 2, "shared");
builder.addFunction("main", makeSig([kWasmI32], []))
.addLocals(kWasmI64, 2)
.addBody([
kExprLoop, kWasmVoid,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kAtomicPrefix, opcode, alignment, 0,
kExprLocalGet, 1,
kExprI64Ne,
kExprBrIf, 0,
kExprEnd
])
.exportFunc();
let mem = new WebAssembly.Memory({initial: 2, maximum: 2, shared: true});
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, {m: {imported_mem: mem}});
}
(function TestAtomicCompareExchgLoop() {
CmpExchgLoop(kExprI64AtomicCompareExchange, 3);
CmpExchgLoop(kExprI64AtomicCompareExchange32U, 2);
CmpExchgLoop(kExprI64AtomicCompareExchange16U, 1);
CmpExchgLoop(kExprI64AtomicCompareExchange8U, 0);
})();
(function TestIllegalAtomicOp() {
// Regression test for https://crbug.com/1381330.
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_v_v).addBody([
kAtomicPrefix, 0x90, 0x0f
]);
assertEquals(false, WebAssembly.validate(builder.toBuffer()));
assertThrows(
() => builder.toModule(), WebAssembly.CompileError,
/invalid atomic opcode: 0xfe790/);
})();
(function TestConsistentErrorReporting() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addMemory(1, 32);
builder.addFunction("atomicStore", makeSig([kWasmI32], []))
.addBody([
kExprLocalGet, 0,
kExprI64Const, 42, // A dummy value to be stored.
kAtomicPrefix, kExprI64AtomicStore, /*align*/ 0x03, /*offset*/ 0x00,
]).exportFunc();
builder.addFunction("atomicLoad", makeSig([kWasmI32], [kWasmI64]))
.addBody([
kExprLocalGet, 0,
kAtomicPrefix, kExprI64AtomicLoad, /*align*/ 0x03, /*offset*/ 0x00,
]).exportFunc();
builder.addFunction("atomicAdd", makeSig([kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprI32Const, 42,
kAtomicPrefix, kExprI32AtomicAdd, /*align*/ 0x02, /*offset*/ 0x00,
]).exportFunc();
builder.addFunction("atomicCompareExchange", makeSig([kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprI32Const, 12,
kExprI32Const, 23,
kAtomicPrefix, kExprI32AtomicCompareExchange,
/*align*/ 0x02, /*offset*/ 0x00,
]).exportFunc();
builder.addFunction("atomicWait", makeSig([kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprI32Const, 12,
kExprI64Const, 23,
kAtomicPrefix, kExprI32AtomicWait, /*align*/ 0x02, /*offset*/ 0x00,
]).exportFunc();
builder.addFunction("atomicNotify", makeSig([kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprI32Const, 12,
kAtomicPrefix, kExprAtomicNotify, /*align*/ 0x02, /*offset*/ 0x00,
]).exportFunc();
const wasm = builder.instantiate().exports;
let unaligned = 1;
let oob = 1024 * 1024;
for (let test of [
"atomicStore", "atomicLoad", "atomicAdd", "atomicCompareExchange",
"atomicWait", "atomicNotify"]) {
print(`- test ${test}`);
let fct = wasm[test];
assertTraps(kTrapUnalignedAccess, () => fct(unaligned));
assertTraps(kTrapMemOutOfBounds, () => fct(oob));
// If an atomic operation is both unaligned and out of bounds, the unaligned
// error is reported.
assertTraps(kTrapUnalignedAccess, () => fct(unaligned + oob));
}
})();

View File

@ -0,0 +1,547 @@
// Copyright 2018 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.
// This test might time out if the search space for a sequential
// interleaving becomes to large. However, it should never fail.
// Note that results of this test are flaky by design. While the test is
// deterministic with a fixed seed, bugs may introduce non-determinism.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const kDebug = false;
const kSequenceLength = 256;
const kNumberOfWorker = 4;
const kNumberOfSteps = 10000000;
const kFirstOpcodeWithInput = 4;
const kFirstOpcodeWithoutOutput = 4;
const kLastOpcodeWithoutOutput = 7;
const opCodes = [
kExprI64AtomicLoad, kExprI64AtomicLoad8U, kExprI64AtomicLoad16U,
kExprI64AtomicLoad32U, kExprI64AtomicStore, kExprI64AtomicStore8U,
kExprI64AtomicStore16U, kExprI64AtomicStore32U, kExprI64AtomicAdd,
kExprI64AtomicAdd8U, kExprI64AtomicAdd16U, kExprI64AtomicAdd32U,
kExprI64AtomicSub, kExprI64AtomicSub8U, kExprI64AtomicSub16U,
kExprI64AtomicSub32U, kExprI64AtomicAnd, kExprI64AtomicAnd8U,
kExprI64AtomicAnd16U, kExprI64AtomicAnd32U, kExprI64AtomicOr,
kExprI64AtomicOr8U, kExprI64AtomicOr16U, kExprI64AtomicOr32U,
kExprI64AtomicXor, kExprI64AtomicXor8U, kExprI64AtomicXor16U,
kExprI64AtomicXor32U, kExprI64AtomicExchange, kExprI64AtomicExchange8U,
kExprI64AtomicExchange16U, kExprI64AtomicExchange32U
];
const opCodeNames = [
'kExprI64AtomicLoad', 'kExprI64AtomicLoad8U',
'kExprI64AtomicLoad16U', 'kExprI64AtomicLoad32U',
'kExprI64AtomicStore', 'kExprI64AtomicStore8U',
'kExprI64AtomicStore16U', 'kExprI64AtomicStore32U',
'kExprI64AtomicAdd', 'kExprI64AtomicAdd8U',
'kExprI64AtomicAdd16U', 'kExprI64AtomicAdd32U',
'kExprI64AtomicSub', 'kExprI64AtomicSub8U',
'kExprI64AtomicSub16U', 'kExprI64AtomicSub32U',
'kExprI64AtomicAnd', 'kExprI64AtomicAnd8U',
'kExprI64AtomicAnd16U', 'kExprI64AtomicAnd32U',
'kExprI64AtomicOr', 'kExprI64AtomicOr8U',
'kExprI64AtomicOr16U', 'kExprI64AtomicOr32U',
'kExprI64AtomicXor', 'kExprI64AtomicXor8U',
'kExprI64AtomicXor16U', 'kExprI64AtomicXor32U',
'kExprI64AtomicExchange', 'kExprI64AtomicExchange8U',
'kExprI64AtomicExchange16U', 'kExprI64AtomicExchange32U'
];
let kMaxMemPages = 10;
let gSharedMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gSharedMemoryView = new Int32Array(gSharedMemory.buffer);
let gPrivateMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gPrivateMemoryView = new Int32Array(gPrivateMemory.buffer);
const kMaxInt32 = (1 << 31) * 2;
class Operation {
constructor(opcode, low_input, high_input, offset) {
this.opcode = opcode != undefined ? opcode : Operation.nextOpcode();
this.size = Operation.opcodeToSize(this.opcode);
if (low_input == undefined) {
[low_input, high_input] = Operation.inputForSize(this.size);
}
this.low_input = low_input;
this.high_input = high_input;
this.offset =
offset != undefined ? offset : Operation.offsetForSize(this.size);
}
static nextOpcode() {
let random = Math.random();
return Math.floor(random * opCodes.length);
}
static opcodeToSize(opcode) {
// Instructions are ordered in 64, 8, 16, 32 bits size
return [64, 8, 16, 32][opcode % 4];
}
static opcodeToAlignment(opcode) {
// Instructions are ordered in 64, 8, 16, 32 bits size
return [3, 0, 1, 2][opcode % 4];
}
static inputForSize(size) {
if (size <= 32) {
let random = Math.random();
// Avoid 32 bit overflow for integer here :(
return [Math.floor(random * (1 << (size - 1)) * 2), 0];
}
return [
Math.floor(Math.random() * kMaxInt32),
Math.floor(Math.random() * kMaxInt32)
];
}
static offsetForSize(size) {
// Pick an offset in bytes between 0 and 8.
let offset = Math.floor(Math.random() * 8);
// Make sure the offset matches the required alignment by masking out the
// lower bits.
let size_in_bytes = size / 8;
let mask = ~(size_in_bytes - 1);
return offset & mask;
}
get wasmOpcode() {
// [opcode, alignment, offset]
return [
opCodes[this.opcode], Operation.opcodeToAlignment(this.opcode),
this.offset
];
}
get hasInput() {
return this.opcode >= kFirstOpcodeWithInput;
}
get hasOutput() {
return this.opcode < kFirstOpcodeWithoutOutput ||
this.opcode > kLastOpcodeWithoutOutput;
}
truncateResultBits(low, high) {
if (this.size == 64)
return [low, high]
// Shift the lower part. For offsets greater four it drops out of the
// visible window.
let shiftedL = this.offset >= 4 ? 0 : low >>> (this.offset * 8);
// The higher part is zero for offset 0, left shifted for [1..3] and right
// shifted for [4..7].
let shiftedH = this.offset == 0 ?
0 :
this.offset >= 4 ? high >>> (this.offset - 4) * 8 :
high << ((4 - this.offset) * 8);
let value = shiftedL | shiftedH;
switch (this.size) {
case 8:
return [value & 0xFF, 0];
case 16:
return [value & 0xFFFF, 0];
case 32:
return [value, 0];
default:
throw 'Unexpected size: ' + this.size;
}
}
static get builder() {
if (!Operation.__builder) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
Operation.__builder = builder;
}
return Operation.__builder;
}
static get exports() {
if (!Operation.__instance) {
return {};
}
return Operation.__instance.exports;
}
static get memory() {
return Operation.exports.mem;
}
static set instance(instance) {
Operation.__instance = instance;
}
compute(state) {
let evalFun = Operation.exports[this.key];
if (!evalFun) {
let builder = Operation.builder;
let body = [
// Load address of low 32 bits.
kExprI32Const, 0,
// Load expected value.
kExprLocalGet, 0, kExprI32StoreMem, 2, 0,
// Load address of high 32 bits.
kExprI32Const, 4,
// Load expected value.
kExprLocalGet, 1, kExprI32StoreMem, 2, 0,
// Load address of where our window starts.
kExprI32Const, 0,
// Load input if there is one.
...(this.hasInput ?
[
kExprLocalGet, 3, kExprI64UConvertI32, kExprI64Const, 32,
kExprI64Shl, kExprLocalGet, 2, kExprI64UConvertI32,
kExprI64Ior
] :
[]),
// Perform operation.
kAtomicPrefix, ...this.wasmOpcode,
// Drop output if it had any.
...(this.hasOutput ? [kExprDrop] : []),
// Return.
kExprReturn
]
builder.addFunction(this.key, kSig_v_iiii)
.addBody(body)
.exportAs(this.key);
// Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer());
Operation.instance =
new WebAssembly.Instance(module, {m: {imported_mem: gPrivateMemory}});
evalFun = Operation.exports[this.key];
}
evalFun(state.low, state.high, this.low_input, this.high_input);
let ta = gPrivateMemoryView;
if (kDebug) {
print(
state.high + ':' + state.low + ' ' + this.toString() + ' -> ' +
ta[1] + ':' + ta[0]);
}
return {low: ta[0], high: ta[1]};
}
toString() {
return opCodeNames[this.opcode] + '[+' + this.offset + '] ' +
this.high_input + ':' + this.low_input;
}
get key() {
return this.opcode + '-' + this.offset;
}
}
class State {
constructor(low, high, indices, count) {
this.low = low;
this.high = high;
this.indices = indices;
this.count = count;
}
isFinal() {
return (this.count == kNumberOfWorker * kSequenceLength);
}
toString() {
return this.high + ':' + this.low + ' @ ' + this.indices;
}
}
function makeSequenceOfOperations(size) {
let result = new Array(size);
for (let i = 0; i < size; i++) {
result[i] = new Operation();
}
return result;
}
function toSLeb128(low, high) {
let result = [];
while (true) {
let v = low & 0x7f;
// For low, fill up with zeros, high will add extra bits.
low = low >>> 7;
if (high != 0) {
let shiftIn = high << (32 - 7);
low = low | shiftIn;
// For high, fill up with ones, so that we keep trailing one.
high = high >> 7;
}
let msbIsSet = (v & 0x40) || false;
if (((low == 0) && (high == 0) && !msbIsSet) ||
((low == -1) && (high == -1) && msbIsSet)) {
result.push(v);
break;
}
result.push(v | 0x80);
}
return result;
}
function generateFunctionBodyForSequence(sequence) {
// We expect the int64* to perform ops on as arg 0 and
// the int64* for our value log as arg1. Argument 2 gives
// an int32* we use to count down spinning workers.
let body = [];
// Initially, we spin until all workers start running.
if (!kDebug) {
body.push(
// Decrement the wait count.
kExprLocalGet, 2, kExprI32Const, 1, kAtomicPrefix, kExprI32AtomicSub, 2,
0,
// Spin until zero.
kExprLoop, kWasmVoid, kExprLocalGet, 2, kAtomicPrefix,
kExprI32AtomicLoad, 2, 0, kExprI32Const, 0, kExprI32GtU, kExprBrIf, 0,
kExprEnd);
}
for (let operation of sequence) {
body.push(
// Pre-load address of results sequence pointer for later.
kExprLocalGet, 1,
// Load address where atomic pointers are stored.
kExprLocalGet, 0,
// Load the second argument if it had any.
...(operation.hasInput ?
[
kExprI64Const,
...toSLeb128(operation.low_input, operation.high_input)
] :
[]),
// Perform operation
kAtomicPrefix, ...operation.wasmOpcode,
// Generate fake output in needed.
...(operation.hasOutput ? [] : [kExprI64Const, 0]),
// Store read intermediate to sequence.
kExprI64StoreMem, 3, 0,
// Increment result sequence pointer.
kExprLocalGet, 1, kExprI32Const, 8, kExprI32Add, kExprLocalSet, 1);
}
// Return end of sequence index.
body.push(kExprLocalGet, 1, kExprReturn);
return body;
}
function getSequence(start, end) {
return new Int32Array(
gSharedMemory.buffer, start,
(end - start) / Int32Array.BYTES_PER_ELEMENT);
}
function spawnWorkers() {
let workers = [];
for (let i = 0; i < kNumberOfWorker; i++) {
let worker = new Worker(
`onmessage = function({data:msg}) {
if (msg.module) {
let module = msg.module;
let mem = msg.mem;
this.instance = new WebAssembly.Instance(module, {m: {imported_mem: mem}});
postMessage({instantiated: true});
} else {
let address = msg.address;
let sequence = msg.sequence;
let index = msg.index;
let spin = msg.spin;
let result = instance.exports["worker" + index](address, sequence, spin);
postMessage({index: index, sequence: sequence, result: result});
}
}`,
{type: 'string'});
workers.push(worker);
}
return workers;
}
function instantiateModuleInWorkers(workers) {
for (let worker of workers) {
worker.postMessage({module: module, mem: gSharedMemory});
let msg = worker.getMessage();
if (!msg.instantiated) throw 'Worker failed to instantiate';
}
}
function executeSequenceInWorkers(workers) {
for (i = 0; i < workers.length; i++) {
let worker = workers[i];
worker.postMessage({
index: i,
address: 0,
spin: 16,
sequence: 32 + ((kSequenceLength * 8) + 32) * i
});
// In debug mode, keep execution sequential.
if (kDebug) {
let msg = worker.getMessage();
results[msg.index] = getSequence(msg.sequence, msg.result);
}
}
}
function selectMatchingWorkers(state) {
let matching = [];
let indices = state.indices;
for (let i = 0; i < indices.length; i++) {
let index = indices[i];
if (index >= kSequenceLength) continue;
// We need to project the expected value to the number of bits this
// operation will read at runtime.
let [expected_low, expected_high] =
sequences[i][index].truncateResultBits(state.low, state.high);
let hasOutput = sequences[i][index].hasOutput;
if (!hasOutput ||
((results[i][index * 2] == expected_low) &&
(results[i][index * 2 + 1] == expected_high))) {
matching.push(i);
}
}
return matching;
}
function computeNextState(state, advanceIdx) {
let newIndices = state.indices.slice();
let sequence = sequences[advanceIdx];
let operation = sequence[state.indices[advanceIdx]];
newIndices[advanceIdx]++;
let {low, high} = operation.compute(state);
return new State(low, high, newIndices, state.count + 1);
}
function findSequentialOrdering() {
let startIndices = new Array(results.length);
let steps = 0;
startIndices.fill(0);
let matchingStates = [new State(0, 0, startIndices, 0)];
while (matchingStates.length > 0) {
let current = matchingStates.pop();
if (kDebug) {
print(current);
}
let matchingResults = selectMatchingWorkers(current);
if (matchingResults.length == 0) {
continue;
}
for (let match of matchingResults) {
let newState = computeNextState(current, match);
if (newState.isFinal()) {
return true;
}
matchingStates.push(newState);
}
if (steps++ > kNumberOfSteps) {
print('Search timed out, aborting...');
return true;
}
}
// We have no options left.
return false;
}
// Helpful for debugging failed tests.
function loadSequencesFromStrings(inputs) {
let reverseOpcodes = {};
for (let i = 0; i < opCodeNames.length; i++) {
reverseOpcodes[opCodeNames[i]] = i;
}
let sequences = [];
let parseRE = /([a-zA-Z0-9]*)\[\+([0-9])\] ([\-0-9]*)/;
for (let input of inputs) {
let parts = input.split(',');
let sequence = [];
for (let part of parts) {
let parsed = parseRE.exec(part);
sequence.push(
new Operation(reverseOpcodes[parsed[1]], parsed[3], parsed[2] | 0));
}
sequences.push(sequence);
}
return sequences;
}
// Helpful for debugging failed tests.
function loadResultsFromStrings(inputs) {
let results = [];
for (let input of inputs) {
let parts = input.split(',');
let result = [];
for (let number of parts) {
result.push(number | 0);
}
results.push(result);
}
return results;
}
let sequences = [];
let results = [];
let builder = new WasmModuleBuilder();
builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
for (let i = 0; i < kNumberOfWorker; i++) {
sequences[i] = makeSequenceOfOperations(kSequenceLength);
builder.addFunction('worker' + i, kSig_i_iii)
.addBody(generateFunctionBodyForSequence(sequences[i]))
.exportAs('worker' + i);
}
// Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer());
let instance =
new WebAssembly.Instance(module, {m: {imported_mem: gSharedMemory}});
// Spawn off the workers and run the sequences.
let workers = spawnWorkers();
// Set spin count.
gSharedMemoryView[4] = kNumberOfWorker;
instantiateModuleInWorkers(workers);
executeSequenceInWorkers(workers);
if (!kDebug) {
// Collect results, d8 style.
for (let worker of workers) {
let msg = worker.getMessage();
results[msg.index] = getSequence(msg.sequence, msg.result);
}
}
// Terminate all workers.
for (let worker of workers) {
worker.terminate();
}
// In debug mode, print sequences and results.
if (kDebug) {
for (let result of results) {
print(result);
}
for (let sequence of sequences) {
print(sequence);
}
}
// Try to reconstruct a sequential ordering.
let passed = findSequentialOrdering();
if (passed) {
print('PASS');
} else {
for (let i = 0; i < kNumberOfWorker; i++) {
print('Worker ' + i);
print(sequences[i]);
print(results[i]);
}
print('FAIL');
quit(-1);
}

View File

@ -0,0 +1,37 @@
// 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestWasmI64ToJSBigIntImportedFunc() {
var builder = new WasmModuleBuilder();
var a_index = builder
.addImport("a", "a", kSig_l_l) // i64 -> i64
builder
.addFunction("fn", kSig_l_v) // () -> i64
.addBody([
kExprI64Const, 0x7,
kExprCallFunction, a_index
])
.exportFunc();
a_was_called = false;
var module = builder.instantiate({
a: {
a(param) {
assertEquals(typeof param, "bigint");
assertEquals(param, 7n);
a_was_called = true;
return 12n;
},
}
});
assertEquals(module.exports.fn(), 12n);
assertTrue(a_was_called);
})();

36
deps/v8/test/mjsunit/wasm/bigint-opt.js vendored Normal file
View File

@ -0,0 +1,36 @@
// Copyright 2022 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: --allow-natives-syntax --turbofan --no-always-turbofan --turbo-inline-js-wasm-calls
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let builder = new WasmModuleBuilder();
builder
.addFunction("f", kSig_v_l) // i64 -> ()
.addBody([])
.exportFunc();
let module = builder.instantiate();
function TestBigIntTruncatedToWord64(x) {
return module.exports.f(x + x);
}
let bi = (2n ** (2n ** 29n + 2n ** 29n - 1n));
// Expect BigIntTooBig for adding bi to itself
assertThrows(() => TestBigIntTruncatedToWord64(bi), RangeError);
%PrepareFunctionForOptimization(TestBigIntTruncatedToWord64);
TestBigIntTruncatedToWord64(1n);
TestBigIntTruncatedToWord64(2n);
%OptimizeFunctionOnNextCall(TestBigIntTruncatedToWord64);
// After optimization, bi should be checked as BigInt and
// truncated to Word64, which is then passed to Int64Add.
// Thus no BigIntTooBig exception is expected.
TestBigIntTruncatedToWord64(bi);
assertOptimized(TestBigIntTruncatedToWord64);

View File

@ -0,0 +1,43 @@
// Copyright 2022 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: --allow-natives-syntax --turbofan --no-always-turbofan --turbo-inline-js-wasm-calls
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let builder = new WasmModuleBuilder();
builder
.addFunction("f", kSig_l_v) // () -> i64
.addBody([
kExprI64Const, 0,
kExprI64Const, 1,
kExprI64Sub, // -1
])
.exportFunc();
let module = builder.instantiate();
function f(x) {
let y = module.exports.f();
try {
return x + y;
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(0n, f(1n));
assertEquals(1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(0n, f(1n));
assertOptimized(f);
// After optimization, the result of the js wasm call is stored in word64 and
// passed to StateValues without conversion. Rematerialization will happen
// in deoptimizer.
assertEquals(-1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}

192
deps/v8/test/mjsunit/wasm/bigint.js vendored Normal file
View File

@ -0,0 +1,192 @@
// Copyright 2018 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestWasmI64ToJSBigInt() {
let builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_l_v) // () -> i64
.addBody([
kExprI64Const, 0x3,
])
.exportFunc();
let module = builder.instantiate();
assertEquals(typeof module.exports.fn(), "bigint");
assertEquals(module.exports.fn(), 3n);
})();
(function TestJSBigIntToWasmI64Global() {
let builder = new WasmModuleBuilder();
let a_global_index = builder
.addImportedGlobal("mod", "a", kWasmI64);
let b_global_index = builder
.addImportedGlobal("mod", "b", kWasmI64);
builder
.addExportOfKind('a', kExternalGlobal, a_global_index)
.addExportOfKind('b', kExternalGlobal, b_global_index)
let module = builder.instantiate({
mod: {
a: 1n,
b: 2n ** 63n,
}
});
assertEquals(module.exports.a.value, 1n);
assertEquals(module.exports.b.value, - (2n ** 63n));
})();
(function TestJSBigIntGlobalImportInvalidType() {
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("mod", "a", kWasmI64);
assertThrows(() => builder.instantiate({mod: { a: {} } }), WebAssembly.LinkError);
})();
(function TestJSBigIntToWasmI64MutableGlobal() {
let builder = new WasmModuleBuilder();
let a_global_index = builder
.addImportedGlobal("mod", "a", kWasmI64, /* mutable = */ true)
builder
.addExportOfKind('a', kExternalGlobal, a_global_index);
// as non object
let fn = () => builder.instantiate({
mod: {
a: 1n,
}
});
assertThrows(fn, WebAssembly.LinkError);
// as WebAssembly.Global object
let module = builder.instantiate({
mod: {
a: new WebAssembly.Global({ value: "i64", mutable: true }, 1n),
}
});
assertEquals(module.exports.a.value, 1n);
})();
(function TestJSBigIntToWasmI64Identity() {
let builder = new WasmModuleBuilder();
builder
.addFunction("f", kSig_l_l) // i64 -> i64
.addBody([
kExprLocalGet, 0,
])
.exportFunc();
let module = builder.instantiate();
let f = module.exports.f;
assertEquals(f(0n), 0n);
assertEquals(f(123n), 123n);
assertEquals(f(-123n), -123n);
assertEquals(f("5"), 5n);
assertThrows(() => f(5), TypeError);
})();
(function TestJSBigIntToWasmI64Projection() {
let builder = new WasmModuleBuilder();
builder
.addFunction("f", kSig_l_ll) // i64 -> i64
.addBody([
kExprLocalGet, 1,
])
.exportFunc();
let module = builder.instantiate();
let f = module.exports.f;
assertEquals(f(1n, 0n), 0n);
assertEquals(f(1n, 123n), 123n);
assertEquals(f(1n, -123n), -123n);
assertEquals(f(1n, "5"), 5n);
assertThrows(() => f(1n, 5), TypeError);
})();
(function TestI64Global() {
let argument = { "value": "i64", "mutable": true };
let global = new WebAssembly.Global(argument);
assertEquals(global.value, 0n); // initial value
global.value = 123n;
assertEquals(global.valueOf(), 123n);
global.value = 2n ** 63n;
assertEquals(global.valueOf(), - (2n ** 63n));
})();
(function TestI64GlobalValueOf() {
let argument = { "value": "i64" };
// as literal
let global = new WebAssembly.Global(argument, {
valueOf() {
return 123n;
}
});
assertEquals(global.value, 123n);
// as string
let global2 = new WebAssembly.Global(argument, {
valueOf() {
return "321";
}
});
assertEquals(global2.value, 321n);
})();
(function TestInvalidValtypeGlobalErrorMessage() {
let argument = { "value": "some string" };
assertThrows(() => new WebAssembly.Global(argument), TypeError);
try {
new WebAssembly.Global(argument);
} catch (e) {
assertContains("'value' must be a WebAssembly type", e.message);
}
})();
(function TestGlobalI64ValueWrongType() {
let argument = { "value": "i64" };
assertThrows(() => new WebAssembly.Global(argument, 666), TypeError);
})();
(function TestGlobalI64SetWrongType() {
let argument = { "value": "i64", "mutable": true };
let global = new WebAssembly.Global(argument);
assertThrows(() => global.value = 1, TypeError);
})();
(function TestFuncParamF64PassingBigInt() {
let builder = new WasmModuleBuilder();
builder
.addFunction("f", kSig_v_d) // f64 -> ()
.addBody([])
.exportFunc();
let module = builder.instantiate();
assertThrows(() => module.exports.f(123n), TypeError);
})();

View File

@ -0,0 +1,126 @@
// Copyright 2022 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: --allow-natives-syntax
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let builder = new WasmModuleBuilder();
let fcts = [
builder.addFunction('i64_shr_u_dynamic',
makeSig([kWasmI64, kWasmI64], [kWasmI64]))
.exportFunc().addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI64ShrU,
]),
builder.addFunction('i64_shr_u_by_negative_48',
makeSig([kWasmI64], [kWasmI64]))
.exportFunc().addBody([
kExprLocalGet, 0,
...wasmI64Const(-48),
kExprI64ShrU,
]),
builder.addFunction('i64_shr_s_dynamic',
makeSig([kWasmI64, kWasmI64], [kWasmI64]))
.exportFunc().addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI64ShrS,
]),
builder.addFunction('i64_shr_s_by_negative_48',
makeSig([kWasmI64], [kWasmI64]))
.exportFunc().addBody([
kExprLocalGet, 0,
...wasmI64Const(-48),
kExprI64ShrS,
]),
builder.addFunction('i32_shr_u_dynamic',
makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.exportFunc().addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI32ShrU,
]),
builder.addFunction('i32_shr_u_by_negative_22',
makeSig([kWasmI32], [kWasmI32]))
.exportFunc().addBody([
kExprLocalGet, 0,
...wasmI32Const(-22),
kExprI32ShrU,
]),
builder.addFunction('i32_shr_s_dynamic',
makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.exportFunc().addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI32ShrS,
]),
builder.addFunction('i32_shr_s_by_negative_22',
makeSig([kWasmI32], [kWasmI32]))
.exportFunc().addBody([
kExprLocalGet, 0,
...wasmI32Const(-22),
kExprI32ShrS,
]),
];
let instance = builder.instantiate();
let wasm = instance.exports;
let testFct = () => {
// i64.shr_u
assertEquals(0n, wasm.i64_shr_u_dynamic(0n, 0n));
assertEquals(1n, wasm.i64_shr_u_dynamic(3n, 1n));
assertEquals(-1n, wasm.i64_shr_u_dynamic(-1n, 64n));
assertEquals(-1n, wasm.i64_shr_u_dynamic(-1n, -64n));
assertEquals(1n, wasm.i64_shr_u_dynamic(-1n, 63n));
assertEquals(1n, wasm.i64_shr_u_dynamic(-1n, -1n));
assertEquals(BigInt(2 ** 32 - 1), wasm.i64_shr_u_dynamic(-1n, -32n));
assertEquals(8n, wasm.i64_shr_u_dynamic(16n, 65n));
assertEquals(8n, wasm.i64_shr_u_dynamic(16n, -127n));
assertEquals(0n, wasm.i64_shr_u_by_negative_48(123n));
assertEquals(BigInt(2 ** 48 - 1), wasm.i64_shr_u_by_negative_48(-1n));
assertEquals(4n, wasm.i64_shr_u_by_negative_48(BigInt(2 ** 18)))
// i64.shr_s
assertEquals(0n, wasm.i64_shr_s_dynamic(0n, 0n));
assertEquals(1n, wasm.i64_shr_s_dynamic(3n, 1n));
assertEquals(-1n, wasm.i64_shr_s_dynamic(-1n, 64n));
assertEquals(-1n, wasm.i64_shr_s_dynamic(-1n, -64n));
assertEquals(-1n, wasm.i64_shr_s_dynamic(-8n, 63n));
assertEquals(-4n, wasm.i64_shr_s_dynamic(-8n, -63n));
assertEquals(-1n, wasm.i64_shr_s_dynamic(-16n, -2n));
assertEquals(0n, wasm.i64_shr_s_by_negative_48(123n));
assertEquals(-BigInt(2 ** 16),
wasm.i64_shr_s_by_negative_48(-BigInt(2 ** 32)));
// i32.shr_u
assertEquals(0, wasm.i32_shr_u_dynamic(0, 0));
assertEquals(1, wasm.i32_shr_u_dynamic(3, 1));
assertEquals(-1, wasm.i32_shr_u_dynamic(-1, 32));
assertEquals(-1, wasm.i32_shr_u_dynamic(-1, -32));
assertEquals((1 << 17) - 1, wasm.i32_shr_u_dynamic(-1, 15));
assertEquals(123, wasm.i32_shr_u_by_negative_22((123 << 10) + 456));
assertEquals((1 << 22) - 123, wasm.i32_shr_u_by_negative_22(-123 << 10));
// i32.shr_s
assertEquals(0, wasm.i32_shr_s_dynamic(0, 0));
assertEquals(1, wasm.i32_shr_s_dynamic(3, 1));
assertEquals(-1, wasm.i32_shr_s_dynamic(-1, 32));
assertEquals(-1, wasm.i32_shr_s_dynamic(-1, -32));
assertEquals(-123, wasm.i32_shr_s_dynamic(-123 << 15, 15));
assertEquals(123, wasm.i32_shr_s_by_negative_22((123 << 10) + 456));
assertEquals(-123, wasm.i32_shr_s_by_negative_22((-123 << 10) + 456));
};
for (let i = 0; i < 20; i++) testFct();
for (let fct of fcts) {
%WasmTierUpFunction(wasm[fct.name]);
}
testFct();

View File

@ -0,0 +1,33 @@
// Copyright 2017 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
builder.addMemory(1, undefined);
builder.addFunction('load', kSig_i_ii)
.addBody([
kExprLocalGet, 0,
kExprI64SConvertI32,
kExprLocalGet, 1,
kExprI64SConvertI32,
kExprI64Shl,
kExprI32ConvertI64,
kExprI32LoadMem, 0, 0])
.exportFunc();
const module = builder.instantiate();
let start = 12;
let address = start;
for (i = 0; i < 64; i++) {
// This is the address which will be accessed in the code. We cannot use
// shifts to calculate the address because JS shifts work on 32-bit integers.
print(`address=${address}`);
if (address < kPageSize) {
assertEquals(0, module.exports.load(start, i));
} else {
assertTraps(kTrapMemOutOfBounds, _ => { module.exports.load(start, i);});
}
address = (address * 2) % 4294967296;
}

View File

@ -0,0 +1,23 @@
// Copyright 2018 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: --allow-natives-syntax
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
builder.addMemory(1, undefined);
builder.addFunction('load', kSig_i_i)
.addBody([
kExprLocalGet, 0,
kExprI32LoadMem, 0, 100])
.exportFunc();
const load = builder.instantiate().exports.load;
%WasmTierUpFunction(load);
// 100 is added as part of the load instruction above
// Last valid address (64k - 100 - 4)
assertEquals(0, load(0x10000 - 100 - 4));
// First invalid address (64k - 100)
assertTraps(kTrapMemOutOfBounds, _ => { load(0x10000 - 100);});

View File

@ -0,0 +1,32 @@
// Copyright 2024 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.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// Test that we don't rewrite the stack types when iterating over the targets
// for a br_table instrunction.
(function BrTable() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let funcT = builder.addType(makeSig([], []));
let table = builder.addTable(
wasmRefNullType(funcT), 1, 1, [kExprRefNull, funcT]);
let returnsFuncT = builder.addType(makeSig([], [wasmRefNullType(funcT)]));
let returnsFuncRef = builder.addType(makeSig([], [kWasmFuncRef]));
builder.addFunction("br_table", makeSig([kWasmI32], [kWasmFuncRef]))
.addBody([
kExprBlock, returnsFuncRef,
kExprBlock, returnsFuncT,
kExprI32Const, 0,
kExprTableGet, table.index,
kExprLocalGet, 0,
kExprBrTable, 2, 1, 1, 0,
kExprEnd,
kExprEnd,
])
.exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(null, wasm.br_table(0));
})();

258
deps/v8/test/mjsunit/wasm/bulk-memory.js vendored Normal file
View File

@ -0,0 +1,258 @@
// Copyright 2018 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.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestPassiveDataSegment() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addMemory(1, 1);
builder.addPassiveDataSegment([0, 1, 2]);
builder.addPassiveDataSegment([3, 4]);
// Should not throw.
builder.instantiate();
})();
(function TestPassiveElementSegment() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('f', kSig_v_v).addBody([]);
builder.setTableBounds(1, 1);
builder.addPassiveElementSegment([0, 0, 0]);
builder.addPassiveElementSegment([0, 0]);
// Should not throw.
builder.instantiate();
})();
function getMemoryInit(mem, segment_data) {
const builder = new WasmModuleBuilder();
builder.addImportedMemory('', 'mem', 0);
builder.addPassiveDataSegment(segment_data);
builder.addFunction('init', kSig_v_iii)
.addBody([
kExprLocalGet, 0, // Dest.
kExprLocalGet, 1, // Source.
kExprLocalGet, 2, // Size in bytes.
kNumericPrefix, kExprMemoryInit,
0, // Data segment index.
0, // Memory index.
])
.exportAs('init');
return builder.instantiate({'': {mem}}).exports.init;
}
(function TestMemoryInitOutOfBoundsGrow() {
print(arguments.callee.name);
const mem = new WebAssembly.Memory({initial: 1});
// Create a data segment that has a length of kPageSize.
const memoryInit = getMemoryInit(mem, new Array(kPageSize));
mem.grow(1);
// Works properly after grow.
memoryInit(kPageSize, 0, 1000);
// Traps at new boundary.
assertTraps(
kTrapMemOutOfBounds, () => memoryInit(kPageSize + 1, 0, kPageSize));
})();
(function TestMemoryInitOnActiveSegment() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addMemory(1);
builder.addPassiveDataSegment([1, 2, 3]);
builder.addActiveDataSegment(0, [kExprI32Const, 0], [4, 5, 6]);
builder.addFunction('init', kSig_v_i)
.addBody([
kExprI32Const, 0, // Dest.
kExprI32Const, 0, // Source.
kExprLocalGet, 0, // Size in bytes.
kNumericPrefix, kExprMemoryInit,
1, // Data segment index.
0, // Memory index.
])
.exportAs('init');
// Instantiation succeeds, because using memory.init with an active segment
// is a trap, not a validation error.
const instance = builder.instantiate();
// Initialization succeeds, because source address and size are 0
// which is valid on a dropped segment.
instance.exports.init(0);
// Initialization fails, because the segment is implicitly dropped.
assertTraps(kTrapMemOutOfBounds, () => instance.exports.init(1));
})();
(function TestDataDropOnActiveSegment() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addMemory(1);
builder.addPassiveDataSegment([1, 2, 3]);
builder.addActiveDataSegment(0, [kExprI32Const, 0], [4, 5, 6]);
builder.addFunction('drop', kSig_v_v)
.addBody([
kNumericPrefix, kExprDataDrop,
1, // Data segment index.
])
.exportAs('drop');
const instance = builder.instantiate();
// Drop on passive segment is equivalent to double drop which is allowed.
instance.exports.drop();
})();
function getMemoryCopy(mem) {
const builder = new WasmModuleBuilder();
builder.addImportedMemory("", "mem", 0);
builder.addFunction("copy", kSig_v_iii).addBody([
kExprLocalGet, 0, // Dest.
kExprLocalGet, 1, // Source.
kExprLocalGet, 2, // Size in bytes.
kNumericPrefix, kExprMemoryCopy, 0, 0,
]).exportAs("copy");
return builder.instantiate({'': {mem}}).exports.copy;
}
(function TestMemoryCopyOutOfBoundsGrow() {
print(arguments.callee.name);
const mem = new WebAssembly.Memory({initial: 1});
const memoryCopy = getMemoryCopy(mem);
mem.grow(1);
// Works properly after grow.
memoryCopy(0, kPageSize, 1000);
// Traps at new boundary.
assertTraps(
kTrapMemOutOfBounds, () => memoryCopy(0, kPageSize + 1, kPageSize));
})();
function getMemoryFill(mem) {
const builder = new WasmModuleBuilder();
builder.addImportedMemory("", "mem", 0);
builder.addFunction("fill", kSig_v_iii).addBody([
kExprLocalGet, 0, // Dest.
kExprLocalGet, 1, // Byte value.
kExprLocalGet, 2, // Size.
kNumericPrefix, kExprMemoryFill, 0,
]).exportAs("fill");
return builder.instantiate({'': {mem}}).exports.fill;
}
(function TestMemoryFillOutOfBoundsGrow() {
print(arguments.callee.name);
const mem = new WebAssembly.Memory({initial: 1});
const memoryFill = getMemoryFill(mem);
const v = 123;
mem.grow(1);
// Works properly after grow.
memoryFill(kPageSize, v, 1000);
// Traps at new boundary.
assertTraps(
kTrapMemOutOfBounds, () => memoryFill(kPageSize + 1, v, kPageSize));
})();
(function TestElemDropActive() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.setTableBounds(5, 5);
builder.addActiveElementSegment(0, wasmI32Const(0), [0, 0, 0]);
builder.addFunction('drop', kSig_v_v)
.addBody([
kNumericPrefix, kExprElemDrop,
0, // Element segment index.
])
.exportAs('drop');
const instance = builder.instantiate();
// Segment already got dropped after initialization and is therefore
// not active anymore.
instance.exports.drop();
})();
(function TestLazyDataSegmentBoundsCheck() {
print(arguments.callee.name);
const memory = new WebAssembly.Memory({initial: 1});
const view = new Uint8Array(memory.buffer);
const builder = new WasmModuleBuilder();
builder.addImportedMemory('m', 'memory', 1);
builder.addActiveDataSegment(0, wasmI32Const(kPageSize - 1), [42, 42]);
builder.addActiveDataSegment(0, [kExprI32Const, 0], [111, 111]);
assertEquals(0, view[kPageSize - 1]);
// Instantiation fails, memory remains unmodified.
assertThrows(
() => builder.instantiate({m: {memory}}), WebAssembly.RuntimeError);
assertEquals(0, view[kPageSize - 1]);
// The second segment is not initialized.
assertEquals(0, view[0]);
})();
(function TestLazyDataAndElementSegments() {
print(arguments.callee.name);
const table = new WebAssembly.Table({initial: 1, element: 'anyfunc'});
const memory = new WebAssembly.Memory({initial: 1});
const view = new Uint8Array(memory.buffer);
const builder = new WasmModuleBuilder();
builder.addImportedMemory('m', 'memory', 1);
builder.addImportedTable('m', 'table', 1);
const f = builder.addFunction('f', kSig_i_v).addBody([kExprI32Const, 42]);
const tableIndex = 0;
builder.addActiveElementSegment(
tableIndex,
wasmI32Const(0),
[f.index, f.index]);
builder.addActiveDataSegment(0, [kExprI32Const, 0], [42]);
// Instantiation fails, but still modifies the table. The memory is not
// modified, since data segments are initialized after element segments.
assertThrows(
() => builder.instantiate({m: {memory, table}}),
WebAssembly.RuntimeError);
assertEquals(0, view[0]);
})();
(function TestPassiveDataSegmentNoMemory() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addPassiveDataSegment([0, 1, 2]);
// Should not throw.
builder.instantiate();
})();
(function TestPassiveElementSegmentNoMemory() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('f', kSig_v_v).addBody([]);
builder.addPassiveElementSegment([0, 0, 0]);
// Should not throw.
builder.instantiate();
})();
(function TestIllegalNumericOpcode() {
// Regression test for https://crbug.com/1382816.
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_v_v).addBody([kNumericPrefix, 0x90, 0x0f]);
assertEquals(false, WebAssembly.validate(builder.toBuffer()));
assertThrows(
() => builder.toModule(), WebAssembly.CompileError,
/invalid numeric opcode: 0xfc790/);
})();

216
deps/v8/test/mjsunit/wasm/call-ref.js vendored Normal file
View File

@ -0,0 +1,216 @@
// Copyright 2020 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: --experimental-wasm-type-reflection
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestImportedRefCall() {
print(arguments.callee.name);
var exporting_instance = (function () {
var builder = new WasmModuleBuilder();
builder.addFunction("addition", kSig_i_ii)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
return builder.instantiate({});
})();
var instance = (function () {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_i_ii);
var imported_js_api_function_index =
builder.addImport("imports", "js_api_mul", sig_index);
var imported_js_function_index =
builder.addImport("imports", "js_add", sig_index);
var imported_wasm_function_index =
builder.addImport("imports", "wasm_add", sig_index);
var locally_defined_function =
builder.addFunction("sub", sig_index)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
.exportFunc();
builder.addFunction("main", makeSig(
[wasmRefType(sig_index), kWasmI32, kWasmI32], [kWasmI32]))
.addBody([kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 0,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_local", kSig_i_v)
.addBody([kExprI32Const, 55, kExprI32Const, 42,
kExprRefFunc, locally_defined_function.index,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_js_import", kSig_i_v)
.addBody([kExprI32Const, 15, kExprI32Const, 42,
kExprRefFunc, imported_js_function_index,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_wasm_import", kSig_i_v)
.addBody([kExprI32Const, 15, kExprI32Const, 42,
kExprRefFunc, imported_wasm_function_index,
kExprCallRef, sig_index])
.exportFunc();
builder.addFunction("test_js_api_import", kSig_i_v)
.addBody([kExprI32Const, 3, kExprI32Const, 7,
kExprRefFunc, imported_js_api_function_index,
kExprCallRef, sig_index])
.exportFunc();
builder.addExport("reexported_js_function", imported_js_function_index);
// Just to make these functions eligible for call_ref.
builder.addDeclarativeElementSegment([imported_wasm_function_index,
imported_js_api_function_index]);
return builder.instantiate({imports: {
js_add: function(a, b) { return a + b; },
wasm_add: exporting_instance.exports.addition,
js_api_mul: new WebAssembly.Function(
{parameters:['i32', 'i32'], results: ['i32']},
function(a, b) { return a * b; })
}});
})();
// Check that the modules exist.
assertTrue(!!exporting_instance);
assertTrue(!!instance);
print("--locally defined func--");
assertEquals(13, instance.exports.test_local());
print("--locally defined exported func--")
assertEquals(5, instance.exports.main(instance.exports.sub, 12, 7));
print("--imported js func--");
assertEquals(57, instance.exports.test_js_import());
print("--imported and reexported js func--")
assertEquals(19, instance.exports.main(
instance.exports.reexported_js_function, 12, 7));
print("--imported function from another module--");
assertEquals(57, instance.exports.test_wasm_import());
print("--not imported function defined in another module--");
assertEquals(19, instance.exports.main(
exporting_instance.exports.addition, 12, 7));
print("--imported WebAssembly.Function--")
assertEquals(21, instance.exports.test_js_api_import());
print("--not imported WebAssembly.Function--")
assertEquals(-5, instance.exports.main(
new WebAssembly.Function(
{parameters:['i32', 'i32'], results: ['i32']},
function(a, b) { return a - b; }),
10, 15));
print("--not imported WebAssembly.Function, arity mismatch--")
assertEquals(100, instance.exports.main(
new WebAssembly.Function(
{parameters:['i32', 'i32'], results: ['i32']},
function(a) { return a * a; }),
10, 15));
})();
(function TestFromJSSlowPath() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_i_i);
builder.addFunction("main", makeSig(
[wasmRefType(sig_index), kWasmI32], [kWasmI32]))
.addBody([kExprLocalGet, 1, kExprLocalGet, 0, kExprCallRef, sig_index])
.exportFunc();
var instance = builder.instantiate({});
var fun = new WebAssembly.Function(
{ parameters: ['i32'], results: ['i32'] }, (a) => undefined);
// {undefined} is converted to 0.
assertEquals(0, instance.exports.main(fun, 1000));
})();
(function TestImportedFunctionSubtyping() {
print(arguments.callee.name);
var exporting_instance = (function () {
var builder = new WasmModuleBuilder();
let super_struct = builder.addStruct([makeField(kWasmI32, true)]);
let sub_struct = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct);
let super_sig = builder.addType(makeSig([wasmRefNullType(sub_struct)],
[kWasmI32]), kNoSuperType, false)
let sub_sig = builder.addType(makeSig([wasmRefNullType(super_struct)],
[kWasmI32]), super_sig)
builder.addFunction("exported_function", sub_sig)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, super_struct, 0])
.exportFunc();
return builder.instantiate({});
})();
var builder = new WasmModuleBuilder();
// These should canonicalize to the same types as the exporting instance.
let super_struct = builder.addStruct([makeField(kWasmI32, true)]);
let sub_struct = builder.addStruct(
[makeField(kWasmI32, true), makeField(kWasmI64, true)], super_struct);
let super_sig = builder.addType(
makeSig([wasmRefNullType(sub_struct)], [kWasmI32]), kNoSuperType, false);
builder.addImport("m", "f", super_sig);
// Import is a function of the declared type.
return builder.instantiate({m: {f:
exporting_instance.exports.exported_function}});
})();
(function TestJSFunctionCanonicallyDifferent() {
print(arguments.callee.name);
let imp = new WebAssembly.Function({parameters: ["i32"], results: ["i32"]},
x => x + 1);
(function () {
var builder = new WasmModuleBuilder();
let sig = builder.addType(kSig_i_i);
builder.addImport("m", "f", sig);
// This succeeds
builder.instantiate({m: {f: imp}});
})();
(function () {
var builder = new WasmModuleBuilder();
let sig = builder.addType(kSig_i_i, kNoSuperType, false);
let sig_sub = builder.addType(kSig_i_i, sig);
builder.addImport("m", "f", sig_sub);
// Import is a function of the declared type.
assertThrows(() => builder.instantiate({m: {f: imp}}),
WebAssembly.LinkError,
/imported function does not match the expected type/);
})();
(function () {
var builder = new WasmModuleBuilder();
builder.startRecGroup();
let sig_in_group = builder.addType(kSig_i_i);
builder.addType(kSig_i_v);
builder.endRecGroup();
builder.addImport("m", "f", sig_in_group);
// Import is a function of the declared type.
assertThrows(() => builder.instantiate({m: {f: imp}}),
WebAssembly.LinkError,
/imported function does not match the expected type/);
})();
})();

View File

@ -0,0 +1,192 @@
// Copyright 2022 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.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestCallIndirectJSFunction() {
print(arguments.callee.name);
let js_function = function(a, b, c) { return c ? a : b; };
let test = function(is_tail_call) {
const builder = new WasmModuleBuilder();
builder.addType(kSig_i_i);
let sig = builder.addType(kSig_i_iii);
let callee = builder.addImport("m", "f", kSig_i_iii);
let table = builder.addTable(kWasmFuncRef, 10, 10);
builder.addActiveElementSegment(table.index, wasmI32Const(0), [callee]);
let left = -2;
let right = 3;
builder.addFunction("main", kSig_i_i)
.addBody([...wasmI32Const(left), ...wasmI32Const(right), kExprLocalGet, 0,
...wasmI32Const(0),
is_tail_call ? kExprReturnCallIndirect : kExprCallIndirect,
sig, table.index])
.exportFunc();
let instance = builder.instantiate({m: {f: js_function}});
assertEquals(left, instance.exports.main(1));
assertEquals(right, instance.exports.main(0));
}
test(true);
test(false);
})();
(function TestCallIndirectIsorecursiveTypeCanonicalization() {
print(arguments.callee.name);
let exporting_instance = (function() {
const builder = new WasmModuleBuilder();
let struct_mistyped = builder.addStruct([]);
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let sig_mistyped = builder.addType(
makeSig([wasmRefNullType(struct_mistyped)], [kWasmI32]));
let sig = builder.addType(makeSig([wasmRefNullType(struct)], [kWasmI32]));
builder.addFunction("export", sig)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
...wasmI32Const(0), kExprI32GeS])
.exportFunc();
builder.addFunction("export_mistyped", sig_mistyped)
.addBody([...wasmI32Const(0)])
.exportFunc();
return builder.instantiate();
})();
const builder = new WasmModuleBuilder();
// Have these in the reverse order than before.
let struct = builder.addStruct([makeField(kWasmI32, true)]);
let struct_mistyped = builder.addStruct([]);
let sig = builder.addType(makeSig([wasmRefNullType(struct)], [kWasmI32]));
let sig_identical = builder.addType(
makeSig([wasmRefNullType(struct)], [kWasmI32]));
let sig_mistyped = builder.addType(
makeSig([wasmRefNullType(struct_mistyped)], [kWasmI32]));
let imported = builder.addImport("m", "imp", sig);
let imported_mistyped = builder.addImport("m", "imp_m", sig_mistyped);
let local1 = builder.addFunction("local1", sig)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
...wasmI32Const(0), kExprI32LtS])
.index;
let local2 = builder.addFunction("local2", sig_identical)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructGet, struct, 0,
...wasmI32Const(0), kExprI32Eq])
.index;
let mistyped = builder.addFunction("mistyped", kSig_i_i)
.addBody([kExprLocalGet, 0])
.index;
let table_size = 10;
let table = builder.addTable(kWasmFuncRef, table_size, table_size);
builder.addActiveElementSegment(
table.index, wasmI32Const(0),
[[kExprRefFunc, imported], [kExprRefFunc, imported_mistyped],
[kExprRefFunc, local1], [kExprRefFunc, local2], [kExprRefFunc, mistyped]],
table.type);
// Params: Struct field, table index
builder.addFunction("main", kSig_i_ii)
.addBody([kExprLocalGet, 0, kGCPrefix, kExprStructNew, struct,
kExprLocalGet, 1, kExprCallIndirect, sig, table.index])
.exportFunc();
let importing_instance =
builder.instantiate(
{m: {imp: exporting_instance.exports.export,
imp_m: exporting_instance.exports.export_mistyped}});
assertEquals(1, importing_instance.exports.main(10, imported))
assertEquals(0, importing_instance.exports.main(-5, imported))
assertEquals(0, importing_instance.exports.main(10, local1))
assertEquals(1, importing_instance.exports.main(-5, local1))
assertEquals(0, importing_instance.exports.main(10, local2))
assertEquals(1, importing_instance.exports.main(0, local2))
// Mistyped entries
assertTraps(kTrapFuncSigMismatch,
() => importing_instance.exports.main(10, mistyped));
assertTraps(kTrapFuncSigMismatch,
() => importing_instance.exports.main(10, imported_mistyped));
// Null entry
assertTraps(kTrapFuncSigMismatch,
() => importing_instance.exports.main(10, table_size - 1));
assertTraps(kTrapTableOutOfBounds,
() => importing_instance.exports.main(10, table_size));
})();
(function TestSubtyping() {
print(arguments.callee.name);
// This test is the same as https://github.com/WebAssembly/gc/pull/526,
// rewritten in WasmModuleBuilder syntax.
const builder = new WasmModuleBuilder();
let t1 = builder.addType(kSig_v_v, kNoSuperType, false);
let t2 = builder.addType(kSig_v_v, t1, false);
let t3 = builder.addType(kSig_v_v, t2, false);
let t4 = builder.addType(kSig_v_v, kNoSuperType, true);
let f2 = builder.addFunction('f2', t2).addBody([]);
let f3 = builder.addFunction('f3', t3).addBody([]);
let tab0 = builder.addTable(wasmRefNullType(t2), 2).index;
builder.addActiveElementSegment(
tab0, wasmI32Const(0),
[[kExprRefFunc, f2.index], [kExprRefFunc, f3.index]], wasmRefType(t2));
builder.addFunction('run', kSig_v_v).exportFunc().addBody([
// The immediate type being a supertype of the table type (and hence
// also of the table elements) is okay.
kExprI32Const, 0,
kExprCallIndirect, t1, tab0,
kExprI32Const, 1,
kExprCallIndirect, t1, tab0,
// The immediate type being equal to the type of the table and its
// elements is okay.
kExprI32Const, 0,
kExprCallIndirect, t2, tab0,
kExprI32Const, 1,
kExprCallIndirect, t2, tab0,
// The immediate type being a subtype of the table type is okay,
// as long as the requested element matches that subtype.
kExprI32Const, 1,
kExprCallIndirect, t3, tab0,
]);
builder.addFunction('fail1', kSig_v_v).exportFunc().addBody([
// The immediate type is a subtype of the table type here, but the
// retrieved element can't be downcast to it.
kExprI32Const, 0,
kExprCallIndirect, t3, tab0,
]);
builder.addFunction('fail2', kSig_v_v).exportFunc().addBody([
// The immediate type is entirely unrelated to the table type here.
// This validates, but traps at runtime.
kExprI32Const, 0,
kExprCallIndirect, t4, tab0,
]);
let instance = builder.instantiate();
instance.exports.run(); // Does not trap.
assertTraps(kTrapFuncSigMismatch, () => instance.exports.fail1());
assertTraps(kTrapFuncSigMismatch, () => instance.exports.fail2());
})();

108
deps/v8/test/mjsunit/wasm/calls.js vendored Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2015 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
function assertModule(module, memsize) {
// Check the module exists.
assertFalse(module === undefined);
assertFalse(module === null);
assertFalse(module === 0);
assertEquals("object", typeof module);
// Check the memory is an ArrayBuffer.
var mem = module.exports.memory;
assertFalse(mem === undefined);
assertFalse(mem === null);
assertFalse(mem === 0);
assertEquals("object", typeof mem);
assertTrue(mem instanceof WebAssembly.Memory);
var buf = mem.buffer;
assertTrue(buf instanceof ArrayBuffer);
assertEquals(memsize, buf.byteLength);
for (var i = 0; i < 4; i++) {
module.exports.memory = 0; // should be ignored
mem.buffer = 0; // should be ignored
assertSame(mem, module.exports.memory);
assertSame(buf, mem.buffer);
}
}
function assertFunction(module, func) {
assertEquals("object", typeof module.exports);
var exp = module.exports[func];
assertFalse(exp === undefined);
assertFalse(exp === null);
assertFalse(exp === 0);
assertEquals("function", typeof exp);
return exp;
}
(function SubTest() {
var builder = new WasmModuleBuilder();
builder.addMemory(1, 1);
builder.exportMemoryAs("memory");
builder.addFunction("sub", kSig_i_ii)
.addBody([
kExprLocalGet, 0, // --
kExprLocalGet, 1, // --
kExprI32Sub, // --
])
.exportFunc()
var module = builder.instantiate();
assertModule(module, kPageSize);
// Check the properties of the sub function.
var sub = assertFunction(module, "sub");
assertEquals(-55, sub(33, 88));
assertEquals(-55555, sub(33333, 88888));
assertEquals(-5555555, sub(3333333, 8888888));
})();
(function NopTest() {
var builder = new WasmModuleBuilder();
var kPages = 2;
builder.addMemory(kPages, kPages);
builder.exportMemoryAs("memory");
builder.addFunction("nop", kSig_v_v)
.addBody([kExprNop])
.exportFunc();
var module = builder.instantiate();
assertModule(module, kPageSize * kPages);
var nop = assertFunction(module, "nop");
assertEquals(undefined, nop());
})();
(function testLt() {
var builder = new WasmModuleBuilder();
var kPages = 3;
builder.addMemory(kPages, kPages);
builder.exportMemoryAs("memory");
builder.addFunction("flt", kSig_i_dd)
.addBody([
kExprLocalGet, 0, // --
kExprLocalGet, 1, // --
kExprF64Lt // --
]) // --
.exportFunc();
var module = builder.instantiate();
assertModule(module, kPageSize * kPages);
var flt = assertFunction(module, "flt");
assertEquals(1, flt(-2, -1));
assertEquals(0, flt(7.3, 7.1));
assertEquals(1, flt(7.1, 7.3));
})();

View File

@ -0,0 +1,40 @@
// Copyright 2023 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: --allow-natives-syntax --wasm-lazy-compilation
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
builder.addFunction('f1', kSig_i_i).addBody([kExprLocalGet, 0]).exportFunc();
builder.addFunction('f2', kSig_i_i).addBody([kExprLocalGet, 0]).exportFunc();
builder.addFunction('f3', kSig_i_i).addBody([kExprLocalGet, 0]).exportFunc();
const exports = builder.instantiate().exports;
exports.f1(1);
exports.f2(2);
exports.f3(3);
assertTrue(%IsLiftoffFunction(exports.f1));
assertTrue(%IsLiftoffFunction(exports.f2));
assertTrue(%IsLiftoffFunction(exports.f3));
assertTrue(%FlushLiftoffCode() > 0);
assertTrue(%IsUncompiledWasmFunction(exports.f1));
assertTrue(%IsUncompiledWasmFunction(exports.f2));
assertTrue(%IsUncompiledWasmFunction(exports.f3));
exports.f1(1);
exports.f2(2);
exports.f3(3);
%WasmTierUpFunction(exports.f3);
assertTrue(%FlushLiftoffCode() > 0);
assertTrue(%IsUncompiledWasmFunction(exports.f1));
assertTrue(%IsUncompiledWasmFunction(exports.f2));
assertTrue(%IsTurboFanFunction(exports.f3));

View File

@ -0,0 +1,32 @@
// Copyright 2024 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: --allow-natives-syntax --wasm-lazy-compilation
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
builder.addFunction('f1', kSig_i_i).addBody([kExprLocalGet, 0]).exportFunc();
builder.addFunction('f2', kSig_i_i).addBody([kExprLocalGet, 0]).exportFunc();
builder.addFunction('f3', kSig_i_i).addBody([kExprLocalGet, 0]).exportFunc();
const exports = builder.instantiate().exports;
exports.f1(1);
exports.f2(2);
exports.f3(3);
%FlushLiftoffCode();
exports.f1(1);
exports.f2(2);
exports.f3(3);
%WasmTierUpFunction(exports.f3);
%FlushLiftoffCode();
exports.f1(1);
exports.f2(2);
exports.f3(3);

View File

@ -0,0 +1,44 @@
// Copyright 2022 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: --wasm-max-code-space-size-mb=1
// Disable lazy compilation, so we actually generate a lot of code at once.
// Flags: --no-wasm-lazy-compilation
// Limit the number of background threads, so each thread generates more code.
// Flags: --wasm-num-compilation-tasks=2
// This is a regression test for https://crbug.com/v8/13436. If a single
// background thread generates more code than fits in a single code space, we
// need to split it into multiple code spaces.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const start = performance.now();
function time(name) {
const ms_since_start = (performance.now() - start).toFixed(1).padStart(7);
print(`[${ms_since_start}] ${name}`);
}
// At the time of writing this test (Nov 2022), this module generated ~20MB of
// code on x64 and ~18MB on arm64.
const builder = new WasmModuleBuilder();
const kNumFunctions = 1500;
// Build a large body. Then append one instruction to get different code per
// function (for the case that we decide to merge identical code objects in the
// future).
time('Build function template.');
let body_template = [kExprLocalGet, 0];
for (let i = 0; i < kNumFunctions; ++i) {
body_template.push(kExprCallFunction, ...wasmSignedLeb(i));
}
time(`Adding ${kNumFunctions} functions`);
for (let i = 0; i < kNumFunctions; ++i) {
if (i != 0 && i % 100 == 0) time(`... added ${i} functions`);
let body = body_template.concat([...wasmI32Const(i), kExprI32Add, kExprEnd]);
builder.addFunction('f' + i, kSig_i_i).addBodyWithEnd(body);
}
time('Building buffer.');
const wasm_buf = builder.toBuffer();
time('Compiling Wasm module.');
new WebAssembly.Module(wasm_buf);

View File

@ -0,0 +1,17 @@
// Copyright 2018 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: --wasm-max-committed-code-mb=1
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// We only have 1 MB code space. This is enough for the code below, but for all
// 1000 modules, it requires several GCs to get rid of the old code.
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_i).addBody([kExprLocalGet, 0]);
const buffer = builder.toBuffer();
for (let i = 0; i < 1000; ++i) {
new WebAssembly.Module(buffer);
}

View File

@ -0,0 +1,211 @@
// Copyright 2018 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const kSequenceLength = 8192;
const kNumberOfWorkers = 4;
const kBitMask = kNumberOfWorkers - 1;
const kMemoryAddress = 0;
const kSequenceStartAddress = 32;
function makeWorkerCodeForOpcode(compareExchangeOpcode, size, functionName,
builder) {
let loadMemOpcode = kTrapUnreachable;
let alignLog2;
switch (size) {
case 32:
loadMemOpcode = kExprI32LoadMem;
alignLog2 = 2;
break;
case 16:
loadMemOpcode = kExprI32LoadMem16U;
alignLog2 = 1;
break;
case 8:
loadMemOpcode = kExprI32LoadMem8U;
alignLog2 = 0;
break;
default:
throw "!";
}
const kArgMemoryCell = 0; // target for atomic ops
const kArgSequencePtr = 1; // address of sequence
const kArgSeqenceLength = 2; // length of sequence
const kArgWorkerId = 3; // id of this worker
const kArgBitMask = 4; // mask to extract worker id from value
const kLocalCurrentOffset = 5; // current position in sequence in bytes
const kLocalExpectedValue = 6; // the value we are waiting for
const kLocalNextValue = 7; // the value to write in the update
let body = [
// Turn sequence length to equivalent in bytes.
kExprLocalGet, kArgSeqenceLength,
kExprI32Const, size / 8,
kExprI32Mul,
kExprLocalSet, kArgSeqenceLength,
// Outer block so we have something to jump for return.
...[kExprBlock, kWasmVoid,
// Set counter to 0.
kExprI32Const, 0,
kExprLocalSet, kLocalCurrentOffset,
// Outer loop until maxcount.
...[kExprLoop, kWasmVoid,
// Find the next value to wait for.
...[kExprLoop, kWasmVoid,
// Check end of sequence.
kExprLocalGet, kLocalCurrentOffset,
kExprLocalGet, kArgSeqenceLength,
kExprI32Eq,
kExprBrIf, 2, // return
...[kExprBlock, kWasmVoid,
// Load next value.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
// Mask off bits.
kExprLocalGet, kArgBitMask,
kExprI32And,
// Compare with worker id.
kExprLocalGet, kArgWorkerId,
kExprI32Eq,
kExprBrIf, 0,
// Not found, increment position.
kExprLocalGet, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprLocalSet, kLocalCurrentOffset,
kExprBr, 1,
kExprEnd
],
// Found, end loop.
kExprEnd
],
// Load expected value to local.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprLocalSet, kLocalExpectedValue,
// Load value after expected one.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
kExprI32Const, size / 8,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprLocalSet, kLocalNextValue,
// Hammer on memory until value found.
...[kExprLoop, kWasmVoid,
// Load address.
kExprLocalGet, kArgMemoryCell,
// Load expected value.
kExprLocalGet, kLocalExpectedValue,
// Load updated value.
kExprLocalGet, kLocalNextValue,
// Try update.
kAtomicPrefix, compareExchangeOpcode, alignLog2, 0,
// Load expected value.
kExprLocalGet, kLocalExpectedValue,
// Spin if not what expected.
kExprI32Ne,
kExprBrIf, 0,
kExprEnd
],
// Next iteration of loop.
kExprLocalGet, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprLocalSet, kLocalCurrentOffset,
kExprBr, 0,
kExprEnd
], // outer loop
kExprEnd
], // the block
kExprReturn
];
builder.addFunction(functionName, makeSig([kWasmI32, kWasmI32, kWasmI32,
kWasmI32, kWasmI32
], []))
.addLocals(kWasmI32, 3)
.addBody(body)
.exportAs(functionName);
}
function generateSequence(typedarray, start, count) {
let end = count + start;
for (let i = start; i < end; i++) {
typedarray[i] = Math.floor(Math.random() * 256);
}
}
function spawnWorker(module, memory, address, sequence) {
let workers = [];
for (let i = 0; i < kNumberOfWorkers; i++) {
let worker = new Worker(
`onmessage = function({data:msg}) {
this.instance = new WebAssembly.Instance(msg.module,
{m: {imported_mem: msg.memory}});
instance.exports.worker(msg.address, msg.sequence,
msg.sequenceLength, msg.workerId, msg.bitMask);
postMessage({workerId: msg.workerId});
}`,
{type: 'string'}
);
workers.push(worker);
worker.postMessage({
module: module,
memory: memory,
address: address,
sequence: sequence,
sequenceLength: kSequenceLength,
workerId: i,
bitMask: kBitMask
});
}
return workers;
}
function waitForWorkers(workers) {
for (let worker of workers) {
worker.getMessage();
worker.terminate();
}
}
function testOpcode(opcode, opcodeSize) {
print("Testing I32AtomicCompareExchange" + opcodeSize);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, 1, "shared");
makeWorkerCodeForOpcode(opcode, opcodeSize, "worker", builder);
let memory = new WebAssembly.Memory({
initial: 1,
maximum: 1,
shared: true
});
let memoryView = new Uint8Array(memory.buffer);
let numBytes = opcodeSize / 8;
generateSequence(
memoryView, kSequenceStartAddress, kSequenceLength * numBytes);
// Write the first element of the sequence to memory, such that the workers
// can start running as soon as they are spawned.
memoryView.copyWithin(
kMemoryAddress, kSequenceStartAddress,
kSequenceStartAddress + numBytes);
let module = new WebAssembly.Module(builder.toBuffer());
let workers =
spawnWorker(module, memory, kMemoryAddress, kSequenceStartAddress);
waitForWorkers(workers);
print("DONE");
}
testOpcode(kExprI32AtomicCompareExchange, 32);
testOpcode(kExprI32AtomicCompareExchange16U, 16);
testOpcode(kExprI32AtomicCompareExchange8U, 8);

View File

@ -0,0 +1,218 @@
// Copyright 2018 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
const kSequenceLength = 8192;
const kNumberOfWorkers = 4;
const kBitMask = kNumberOfWorkers - 1;
const kMemoryAddress = 0;
const kSequenceStartAddress = 32;
function makeWorkerCodeForOpcode(compareExchangeOpcode, size, functionName,
builder) {
let loadMemOpcode = kTrapUnreachable;
let alignLog2;
switch (size) {
case 64:
loadMemOpcode = kExprI64LoadMem;
alignLog2 = 3;
break;
case 32:
loadMemOpcode = kExprI64LoadMem32U;
alignLog2 = 2;
break;
case 16:
loadMemOpcode = kExprI64LoadMem16U;
alignLog2 = 1;
break;
case 8:
loadMemOpcode = kExprI64LoadMem8U;
alignLog2 = 0;
break;
default:
throw "!";
}
const kArgMemoryCell = 0; // target for atomic ops
const kArgSequencePtr = 1; // address of sequence
const kArgSeqenceLength = 2; // length of sequence
const kArgWorkerId = 3; // id of this worker
const kArgBitMask = 4; // mask to extract worker id from value
const kLocalCurrentOffset = 5; // current position in sequence in bytes
const kLocalExpectedValue = 6; // the value we are waiting for
const kLocalNextValue = 7; // the value to write in the update
let body = [
// Turn sequence length to equivalent in bytes.
kExprLocalGet, kArgSeqenceLength,
kExprI32Const, size / 8,
kExprI32Mul,
kExprLocalSet, kArgSeqenceLength,
// Outer block so we have something to jump for return.
...[kExprBlock, kWasmVoid,
// Set counter to 0.
kExprI32Const, 0,
kExprLocalSet, kLocalCurrentOffset,
// Outer loop until maxcount.
...[kExprLoop, kWasmVoid,
// Find the next value to wait for.
...[kExprLoop, kWasmVoid,
// Check end of sequence.
kExprLocalGet, kLocalCurrentOffset,
kExprLocalGet, kArgSeqenceLength,
kExprI32Eq,
kExprBrIf, 2, // return
...[kExprBlock, kWasmVoid,
// Load next value.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
// Mask off bits.
kExprLocalGet, kArgBitMask,
kExprI64UConvertI32,
kExprI64And,
// Compare with worker id.
kExprLocalGet, kArgWorkerId,
kExprI64UConvertI32,
kExprI64Eq,
kExprBrIf, 0,
// Not found, increment position.
kExprLocalGet, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprLocalSet, kLocalCurrentOffset,
kExprBr, 1,
kExprEnd
],
// Found, end loop.
kExprEnd
],
// Load expected value to local.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprLocalSet, kLocalExpectedValue,
// Load value after expected one.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
kExprI32Const, size / 8,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprLocalSet, kLocalNextValue,
// Hammer on memory until value found.
...[kExprLoop, kWasmVoid,
// Load address.
kExprLocalGet, kArgMemoryCell,
// Load expected value.
kExprLocalGet, kLocalExpectedValue,
// Load updated value.
kExprLocalGet, kLocalNextValue,
// Try update.
kAtomicPrefix, compareExchangeOpcode, alignLog2, 0,
// Load expected value.
kExprLocalGet, kLocalExpectedValue,
// Spin if not what expected.
kExprI64Ne,
kExprBrIf, 0,
kExprEnd
],
// Next iteration of loop.
kExprLocalGet, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprLocalSet, kLocalCurrentOffset,
kExprBr, 0,
kExprEnd
], // outer loop
kExprEnd
], // the block
kExprReturn
];
builder.addFunction(functionName, makeSig([kWasmI32, kWasmI32, kWasmI32,
kWasmI32, kWasmI32
], []))
.addLocals(kWasmI32, 1).addLocals(kWasmI64, 2)
.addBody(body)
.exportAs(functionName);
}
function generateSequence(typedarray, start, count) {
let end = count + start;
for (let i = start; i < end; i++) {
typedarray[i] = Math.floor(Math.random() * 256);
}
}
function spawnWorker(module, memory, address, sequence) {
let workers = [];
for (let i = 0; i < kNumberOfWorkers; i++) {
let worker = new Worker(
`onmessage = function({data:msg}) {
this.instance = new WebAssembly.Instance(msg.module,
{m: {imported_mem: msg.memory}});
instance.exports.worker(msg.address, msg.sequence, msg.sequenceLength, msg.workerId,
msg.bitMask);
postMessage({workerId: msg.workerId});
}`,
{type: 'string'}
);
workers.push(worker);
worker.postMessage({
module: module,
memory: memory,
address: address,
sequence: sequence,
sequenceLength: kSequenceLength,
workerId: i,
bitMask: kBitMask
});
}
return workers;
}
function waitForWorkers(workers) {
for (let worker of workers) {
worker.getMessage();
worker.terminate();
}
}
function testOpcode(opcode, opcodeSize) {
print("Testing I64AtomicCompareExchange" + opcodeSize);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, 2, "shared");
makeWorkerCodeForOpcode(opcode, opcodeSize, "worker", builder);
let memory = new WebAssembly.Memory({
initial: 2,
maximum: 2,
shared: true
});
let memoryView = new Uint8Array(memory.buffer);
let numBytes = opcodeSize / 8;
generateSequence(
memoryView, kSequenceStartAddress, kSequenceLength * numBytes);
// Write the first element of the sequence to memory, such that the workers
// can start running as soon as they are spawned.
memoryView.copyWithin(
kMemoryAddress, kSequenceStartAddress,
kSequenceStartAddress + numBytes);
let module = new WebAssembly.Module(builder.toBuffer());
let workers =
spawnWorker(module, memory, kMemoryAddress, kSequenceStartAddress);
waitForWorkers(workers);
print("DONE");
}
testOpcode(kExprI64AtomicCompareExchange, 64);
testOpcode(kExprI64AtomicCompareExchange32U, 32);
testOpcode(kExprI64AtomicCompareExchange16U, 16);
testOpcode(kExprI64AtomicCompareExchange8U, 8);

View File

@ -0,0 +1,76 @@
// 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: --experimental-wasm-compilation-hints
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testCompileWithBadLazyHint() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierOptimized,
kCompilationHintTierBaseline)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.compile(bytes)
.then(assertUnreachable,
error => assertEquals("WebAssembly.compile(): Invalid compilation " +
"hint 0x19 (forbidden downgrade) @+49", error.message)));
})();
(function testCompileWithBadLazyFunctionBody() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_l)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.compile(bytes).then(
assertUnreachable,
error => assertEquals(
'WebAssembly.compile(): Compiling function #0:"id" failed: type ' +
'error in fallthru[0] (expected i32, got i64) @+56',
error.message)));
})();
(function testCompileEmptyModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.compile(bytes));
})();
(function testCompileLazyModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiate(bytes)
.then(({module, instance}) => assertEquals(42, instance.exports.id(42))));
})();
(function testCompileLazyBaselineEagerTopTierModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazyBaselineEagerTopTier,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiate(bytes)
.then(({module, instance}) => assertEquals(42, instance.exports.id(42))));
})();

View File

@ -0,0 +1,162 @@
// 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: --experimental-wasm-compilation-hints
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testDecodeCompilationHintsSectionNoDowngrade() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierOptimized,
kCompilationHintTierBaseline)
.exportFunc();
assertThrows(() => builder.instantiate({mod: {pow: Math.pow}}),
WebAssembly.CompileError,
"WebAssembly.Module(): Invalid compilation hint 0x19 " +
"(forbidden downgrade) @+70");
})();
(function testDecodeCompilationHintsSectionNoTiering() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyDefault,
kCompilationHintTierBaseline,
kCompilationHintTierBaseline)
.exportFunc();
builder.addFunction('upow2', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
builder.addFunction('upow3', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
let instance = builder.instantiate({mod: {pow: Math.pow}});
assertEquals(27, instance.exports.upow(3))
})();
(function testDecodeCompilationHintsSectionUpgrade() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow2', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
builder.addFunction('upow3', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyEager,
kCompilationHintTierBaseline,
kCompilationHintTierOptimized)
.exportFunc();
let instance = builder.instantiate({mod: {pow: Math.pow}});
assertEquals(27, instance.exports.upow(3))
})();
(function testDecodeCompilationHintsSectionNoImport() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('sq', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprI32Mul])
.setCompilationHint(kCompilationHintStrategyEager,
kCompilationHintTierDefault,
kCompilationHintTierOptimized)
.exportFunc();
let instance = builder.instantiate();
assertEquals(9, instance.exports.sq(3))
})();
(function testDecodeCompilationHintsSectionNoExport() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('sq', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprI32Mul])
.setCompilationHint(kCompilationHintStrategyEager,
kCompilationHintTierDefault,
kCompilationHintTierOptimized);
builder.instantiate();
})();
(function testDecodeCompilationHintsSectionTopTierDefault() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('sq', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprI32Mul])
.setCompilationHint(kCompilationHintStrategyEager,
kCompilationHintTierOptimized,
kCompilationHintTierDefault)
.exportFunc();
let instance = builder.instantiate();
assertEquals(9, instance.exports.sq(3))
})();
(function testDecodeCompilationHintsLazyBaselineEagerTopTier() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('sq', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprI32Mul])
.setCompilationHint(kCompilationHintStrategyLazyBaselineEagerTopTier,
kCompilationHintTierOptimized,
kCompilationHintTierDefault)
.exportFunc();
builder.instantiate();
})();
(function testDecodeIllegalCompilationHintBaselineTier() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let kIllegalHintTier = 0x03;
builder.addFunction('func', kSig_i_i)
.addBody([kExprUnreachable])
.setCompilationHint(
kCompilationHintStrategyDefault, kIllegalHintTier,
kCompilationHintTierDefault);
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
new RegExp(
'WebAssembly.Module\\(\\): Invalid compilation hint 0x0c ' +
'\\(invalid tier 0x03\\)'));
})();
(function testDecodeIllegalCompilationHintTopTier() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let kIllegalHintTier = 0x03;
builder.addFunction('func', kSig_i_i)
.addBody([kExprUnreachable])
.setCompilationHint(
kCompilationHintStrategyDefault, kCompilationHintTierDefault,
kIllegalHintTier);
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
new RegExp(
'WebAssembly.Module\\(\\): Invalid compilation hint 0x30 ' +
'\\(invalid tier 0x03\\)'));
})();

View File

@ -0,0 +1,21 @@
// 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.
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testIgnoreCompilationHintsSectionUnlessEnabled() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyDefault,
kCompilationHintTierBaseline,
kCompilationHintTierBaseline)
.exportFunc();
let instance = builder.instantiate({mod: {pow: Math.pow}});
assertEquals(27, instance.exports.upow(3))
})();

View File

@ -0,0 +1,38 @@
// 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: --experimental-wasm-compilation-hints --wasm-lazy-validation
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testInstantiateLazyValidation() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprI64Const, 1,
kExprI32Mul])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierBaseline,
kCompilationHintTierBaseline)
.exportFunc();
let expected_error_msg = "Compiling function #0:\"id\" failed: i32.mul[1] " +
"expected type i32, found i64.const of type i64 " +
"@+56";
let assertCompileErrorOnInvocation = function(instance) {
assertThrows(() => instance.exports.id(3),
WebAssembly.CompileError,
expected_error_msg)
};
// Synchronous case.
let instance = builder.instantiate();
assertCompileErrorOnInvocation(instance);
// Asynchronous case.
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiate(bytes)
.then(p => assertCompileErrorOnInvocation(p.instance)));
})();

View File

@ -0,0 +1,130 @@
// 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: --experimental-wasm-compilation-hints --wasm-test-streaming
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testInstantiateStreamingWithLazyHint() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
builder.addFunction('upow2', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes),
{mod: {pow: Math.pow}})
.then(({module, instance}) => assertEquals(27, instance.exports.upow2(3))));
})();
(function testInstantiateStreamingWithBadLazyHint() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
builder.addFunction('upow2', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierOptimized,
kCompilationHintTierBaseline)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes),
{mod: {pow: Math.pow}})
.then(assertUnreachable,
error => assertEquals("WebAssembly.instantiateStreaming(): Invalid " +
"compilation hint 0x19 (forbidden downgrade) " +
"@+78",
error.message)));
})();
(function testInstantiateStreamingWithBadLazyFunctionBody() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_f_ff);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
builder.addFunction('upow2', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(
WebAssembly
.instantiateStreaming(Promise.resolve(bytes), {mod: {pow: Math.pow}})
.then(
assertUnreachable,
error => assertEquals(
'WebAssembly.instantiateStreaming(): Compiling ' +
'function #1:"upow" failed: call[0] ' +
'expected type f32, found local.get of type ' +
'i32 @+83',
error.message)));
})();
(function testInstantiateStreamingEmptyModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes),
{mod: {pow: Math.pow}}));
})();
(function testInstantiateStreamingLazyModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes),
{mod: {pow: Math.pow}})
.then(({module, instance}) => assertEquals(27, instance.exports.upow(3))));
})();
(function testInstantiateStreamingLazyBaselineModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImport('mod', 'pow', kSig_i_ii);
builder.addFunction('upow', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprLocalGet, 0,
kExprCallFunction, 0])
.setCompilationHint(kCompilationHintStrategyLazyBaselineEagerTopTier,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes),
{mod: {pow: Math.pow}})
.then(({module, instance}) => assertEquals(27, instance.exports.upow(3))));
})();

View File

@ -0,0 +1,33 @@
// 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: --experimental-wasm-compilation-hints --wasm-test-streaming --wasm-lazy-validation
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testInstantiateStreamingLazyValidation() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0,
kExprI64Const, 1,
kExprI32Mul])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
let expected_error_msg = "Compiling function #0:\"id\" failed: i32.mul[1] " +
"expected type i32, found i64.const of type i64 " +
"@+56";
let assertCompileErrorOnInvocation = function(instance) {
assertThrows(() => instance.exports.id(3),
WebAssembly.CompileError,
expected_error_msg)
};
let bytes = builder.toBuffer();
assertPromiseResult(WebAssembly.instantiateStreaming(Promise.resolve(bytes))
.then(({module, instance}) => assertCompileErrorOnInvocation(instance)));
})();

View File

@ -0,0 +1,67 @@
// 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: --experimental-wasm-compilation-hints
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function testCompileWithBadLazyHint() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierOptimized,
kCompilationHintTierBaseline)
.exportFunc();
assertThrows(() => builder.toModule(),
WebAssembly.CompileError,
"WebAssembly.Module(): Invalid compilation hint 0x19 " +
"(forbidden downgrade) @+49");
})();
(function testCompileWithBadLazyFunctionBody() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_l)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
assertThrows(() => builder.toModule(),
WebAssembly.CompileError,
"WebAssembly.Module(): Compiling function #0:\"id\" failed: type error " +
"in fallthru[0] (expected i32, got i64) @+56");
})();
(function testCompileEmptyModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.toModule();
})();
(function testCompileLazyModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazy,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
assertEquals(42, builder.instantiate().exports.id(42));
})();
(function testCompileLazyBaselineEagerTopTierModule() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction('id', kSig_i_i)
.addBody([kExprLocalGet, 0])
.setCompilationHint(kCompilationHintStrategyLazyBaselineEagerTopTier,
kCompilationHintTierDefault,
kCompilationHintTierDefault)
.exportFunc();
assertEquals(42, builder.instantiate().exports.id(42));
})();

View File

@ -0,0 +1,22 @@
// Copyright 2017 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: --allow-natives-syntax --validate-asm
// Compilation limits for wasm are not enforced for asm->wasm.
%SetWasmCompileControls(0, false);
function AsmModule() {
"use asm";
function xxx() { return 43; }
function yyy() { return 43; }
function zzz() { return 43; }
function main() { return 43; }
return {main: main};
}
assertEquals(43, AsmModule(
undefined, undefined, new ArrayBuffer(1024)).main());
assertTrue(%IsAsmWasmCode(AsmModule));

View File

@ -0,0 +1,73 @@
// Copyright 2017 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: --allow-natives-syntax
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
%SetWasmCompileControls(100000, true);
%SetWasmCompileControls(100000, false);
let buffer = (() => {
let builder = new WasmModuleBuilder();
builder.addFunction("f", kSig_i_v)
.addBody([kExprI32Const, 42])
.exportAs("f");
return builder.toBuffer();
})();
let ok_module = new WebAssembly.Module(buffer);
assertTrue(ok_module instanceof WebAssembly.Module);
assertEquals(42, new WebAssembly.Instance(ok_module).exports.f());
failWithMessage = msg => %AbortJS(msg);
async function SuccessfulTest() {
print("SuccessfulTest...");
%SetWasmCompileControls(buffer.byteLength, true);
%SetWasmInstantiateControls();
let m = new WebAssembly.Module(buffer);
let i = new WebAssembly.Instance(m);
assertEquals(i.exports.f(), 42);
}
async function FailSyncCompile() {
print("FailSyncCompile...");
%SetWasmCompileControls(buffer.byteLength - 1, true);
assertThrows(() => new WebAssembly.Module(buffer), RangeError);
print(" wait");
try {
let m = await WebAssembly.compile(buffer);
print(" cont");
assertTrue(m instanceof WebAssembly.Module);
} catch (e) {
print(" catch");
assertUnreachable();
}
}
async function FailSyncInstantiate() {
print("FailSyncInstantiate...");
%SetWasmCompileControls(buffer.byteLength - 1, true);
assertThrows(() => new WebAssembly.Instance(ok_module), RangeError);
print(" wait");
try {
let i = await WebAssembly.instantiate(ok_module);
print(" cont");
assertTrue(i instanceof WebAssembly.Instance);
} catch (e) {
print(" catch: " + e);
assertUnreachable();
}
}
async function TestAll() {
await SuccessfulTest();
await FailSyncCompile();
await FailSyncInstantiate();
}
assertPromiseResult(TestAll());

View File

@ -0,0 +1,95 @@
// 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-gc --allow-natives-syntax
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Use global variables for all values where the test wants to maintain strict
// control over value lifetime. Using local variables would not give sufficient
// guarantees of the value lifetime.
var module;
var instance1;
var instance2;
var instance3;
var instance4;
(async function () {
(function CompiledModuleInstancesInitialize1to3() {
var builder = new WasmModuleBuilder();
builder.addMemory(1, 1);
builder.addImport("", "getValue", kSig_i_v);
builder.addFunction("f", kSig_i_v)
.addBody([
kExprCallFunction, 0
]).exportFunc();
module = new WebAssembly.Module(builder.toBuffer());
print("Initial instances=0");
assertEquals(0, %WasmGetNumberOfInstances(module));
instance1 = new WebAssembly.Instance(module, { "": { getValue: () => 1 } });
print("Initial instances=1");
assertEquals(1, %WasmGetNumberOfInstances(module));
instance2 = new WebAssembly.Instance(module, { "": { getValue: () => 2 } });
print("Initial instances=2");
assertEquals(2, %WasmGetNumberOfInstances(module));
instance3 = new WebAssembly.Instance(module, { "": { getValue: () => 3 } });
print("Initial instances=3");
assertEquals(3, %WasmGetNumberOfInstances(module));
})();
(function CompiledModuleInstancesClear1() {
assertEquals(1, instance1.exports.f());
instance1 = null;
})();
// Here and below, we need to invoke GC asynchronously and wait for it to
// finish, so that it doesn't need to scan the stack. Otherwise, the objects
// may not be reclaimed because of conservative stack scanning and the test
// may not work as intended.
// Note that two GC's are required because weak slots clearing is deferred.
await gc({ type: 'major', execution: 'async' });
await gc({ type: 'major', execution: 'async' });
print("After gc instances=2");
assertEquals(2, %WasmGetNumberOfInstances(module));
(function CompiledModuleInstancesClear3() {
assertEquals(3, instance3.exports.f());
instance3 = null;
})();
// Note that two GC's are required because weak slots clearing is deferred.
await gc({ type: 'major', execution: 'async' });
await gc({ type: 'major', execution: 'async' });
print("After gc instances=1");
assertEquals(1, %WasmGetNumberOfInstances(module));
(function CompiledModuleInstancesClear2() {
assertEquals(2, instance2.exports.f());
instance2 = null;
})();
// Note that two GC's are required because weak slots clearing is deferred.
await gc({ type: 'major', execution: 'async' });
await gc({ type: 'major', execution: 'async' });
print("After gc instances=0");
assertEquals(0, %WasmGetNumberOfInstances(module));
(function CompiledModuleInstancesInitialize4AndClearModule() {
instance4 = new WebAssembly.Instance(module, { "": { getValue: () => 4 } });
assertEquals(4, instance4.exports.f());
module = null;
instance4 = null;
})();
// Note that two GC's are required because weak slots clearing is deferred.
await gc({ type: 'major', execution: 'async' });
await gc({ type: 'major', execution: 'async' });
})();

View File

@ -0,0 +1,408 @@
// Copyright 2015 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.
// Force TurboFan code for serialization.
// Flags: --allow-natives-syntax --expose-gc --no-liftoff
// Flags: --no-wasm-lazy-compilation
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function SerializeAndDeserializeModule() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
builder.addImportedMemory("", "memory", 1);
var kSig_v_i = makeSig([kWasmI32], []);
var signature = builder.addType(kSig_v_i);
builder.addImport("", "some_value", kSig_i_v);
builder.addImport("", "writer", signature);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprLocalGet, 0,
kExprI32LoadMem, 0, 0,
kExprI32Const, 1,
kExprCallIndirect, signature, kTableZero,
kExprLocalGet,0,
kExprI32LoadMem,0, 0,
kExprCallFunction, 0,
kExprI32Add
]).exportFunc();
// writer(mem[i]);
// return mem[i] + some_value();
builder.addFunction("_wrap_writer", signature)
.addBody([
kExprLocalGet, 0,
kExprCallFunction, 1]);
builder.appendToTable([2, 3]);
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var mem_1 = new WebAssembly.Memory({initial: 1});
var view_1 = new Int32Array(mem_1.buffer);
view_1[0] = 42;
var outval_1;
var i1 = new WebAssembly.Instance(module, {"":
{some_value: () => 1,
writer: (x) => outval_1 = x ,
memory: mem_1}
});
assertEquals(43, i1.exports.main(0));
assertEquals(42, outval_1);
var buff = %SerializeWasmModule(module);
module = null;
gc();
module = %DeserializeWasmModule(buff, wire_bytes);
var mem_2 = new WebAssembly.Memory({initial: 2});
var view_2 = new Int32Array(mem_2.buffer);
view_2[0] = 50;
var outval_2;
var i2 = new WebAssembly.Instance(module, {"":
{some_value: () => 1,
writer: (x) => outval_2 = x ,
memory: mem_2}
});
assertEquals(51, i2.exports.main(0));
assertEquals(50, outval_2);
// The instances don't share memory through deserialization.
assertEquals(43, i1.exports.main(0));
})();
(function DeserializeInvalidObject() {
print(arguments.callee.name);
const invalid_buffer = new ArrayBuffer(10);
const invalid_buffer_view = new Uint8Array(10);
module = %DeserializeWasmModule(invalid_buffer, invalid_buffer_view);
assertEquals(module, undefined);
})();
(function RelationBetweenModuleAndClone() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i_v)
.addBody([kExprI32Const, 42])
.exportFunc();
var wire_bytes = builder.toBuffer();
var compiled_module = new WebAssembly.Module(wire_bytes);
var serialized = %SerializeWasmModule(compiled_module);
var clone = %DeserializeWasmModule(serialized, wire_bytes);
assertNotNull(clone);
assertFalse(clone == undefined);
assertFalse(clone == compiled_module);
assertEquals(clone.constructor, compiled_module.constructor);
})();
(function SerializeWrappersWithSameSignature() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i_v)
.addBody([kExprI32Const, 42])
.exportFunc();
builder.addFunction("main_same_signature", kSig_i_v)
.addBody([kExprI32Const, 23])
.exportFunc();
var wire_bytes = builder.toBuffer();
var compiled_module = new WebAssembly.Module(wire_bytes);
var serialized = %SerializeWasmModule(compiled_module);
var clone = %DeserializeWasmModule(serialized, wire_bytes);
assertNotNull(clone);
assertFalse(clone == undefined);
assertFalse(clone == compiled_module);
assertEquals(clone.constructor, compiled_module.constructor);
})();
(function SerializeAfterInstantiation() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i_v)
.addBody([kExprI32Const, 42])
.exportFunc();
var wire_bytes = builder.toBuffer()
var compiled_module = new WebAssembly.Module(wire_bytes);
var instance1 = new WebAssembly.Instance(compiled_module);
var instance2 = new WebAssembly.Instance(compiled_module);
var serialized = %SerializeWasmModule(compiled_module);
var clone = %DeserializeWasmModule(serialized, wire_bytes);
assertNotNull(clone);
assertFalse(clone == undefined);
assertFalse(clone == compiled_module);
assertEquals(clone.constructor, compiled_module.constructor);
var instance3 = new WebAssembly.Instance(clone);
assertFalse(instance3 == undefined);
})();
(function SerializeAfterInstantiationWithMemory() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("", "memory", 1);
builder.addFunction("main", kSig_i_v)
.addBody([kExprI32Const, 42])
.exportFunc();
var wire_bytes = builder.toBuffer()
var compiled_module = new WebAssembly.Module(wire_bytes);
var mem_1 = new WebAssembly.Memory({initial: 1});
var ffi = {"":{memory:mem_1}};
var instance1 = new WebAssembly.Instance(compiled_module, ffi);
var serialized = %SerializeWasmModule(compiled_module);
var clone = %DeserializeWasmModule(serialized, wire_bytes);
assertNotNull(clone);
assertFalse(clone == undefined);
assertFalse(clone == compiled_module);
assertEquals(clone.constructor, compiled_module.constructor);
var instance2 = new WebAssembly.Instance(clone, ffi);
assertFalse(instance2 == undefined);
})();
(function GlobalsArePrivateBetweenClones() {
print(arguments.callee.name);
var builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32, true, false);
builder.addFunction("read", kSig_i_v)
.addBody([
kExprGlobalGet, 0])
.exportFunc();
builder.addFunction("write", kSig_v_i)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, 0])
.exportFunc();
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var i1 = new WebAssembly.Instance(module);
// serialize and replace module
var buff = %SerializeWasmModule(module);
var module_clone = %DeserializeWasmModule(buff, wire_bytes);
var i2 = new WebAssembly.Instance(module_clone);
i1.exports.write(1);
i2.exports.write(2);
assertEquals(1, i1.exports.read());
assertEquals(2, i2.exports.read());
})();
(function SharedTableTest() {
print(arguments.callee.name);
let kTableSize = 3;
var sig_index1;
function MakeTableExportingModule(constant) {
// A module that defines a table and exports it.
var builder = new WasmModuleBuilder();
builder.addType(kSig_i_i);
builder.addType(kSig_i_ii);
sig_index1 = builder.addType(kSig_i_v);
var f1 = builder.addFunction("f1", sig_index1)
.addBody([kExprI32Const, constant]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprLocalGet, 0, // --
kExprCallIndirect, sig_index1, kTableZero]) // --
.exportAs("main");
builder.setTableBounds(kTableSize, kTableSize);
builder.addActiveElementSegment(0, wasmI32Const(0), [f1.index]);
builder.addExportOfKind("table", kExternalTable, 0);
return new WebAssembly.Module(builder.toBuffer());
}
var m1 = MakeTableExportingModule(11);
// 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([
kExprLocalGet, 0, // --
kExprCallIndirect, sig_index2, kTableZero]) // --
.exportAs("main");
builder.addImportedTable("z", "table", kTableSize, kTableSize);
builder.addActiveElementSegment(0, wasmI32Const(1), [f2.index]);
var m2_bytes = builder.toBuffer();
var m2 = new WebAssembly.Module(m2_bytes);
assertFalse(sig_index1 == sig_index2);
var i1 = new WebAssembly.Instance(m1);
var i2 = new WebAssembly.Instance(m2, {z: {table: i1.exports.table}});
var serialized_m2 = %SerializeWasmModule(m2);
var m2_clone = %DeserializeWasmModule(serialized_m2, m2_bytes);
var m3 = MakeTableExportingModule(33);
var i3 = new WebAssembly.Instance(m3);
var i2_prime = new WebAssembly.Instance(m2_clone,
{z: {table: i3.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));
assertEquals(33, i3.exports.main(0));
assertEquals(33, i2_prime.exports.main(0));
assertThrows(() => i1.exports.main(2));
assertThrows(() => i2.exports.main(2));
assertThrows(() => i1.exports.main(3));
assertThrows(() => i2.exports.main(3));
})();
(function StackOverflowAfterSerialization() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
var fun = builder.addFunction('main', kSig_v_v);
fun.addBody([kExprCallFunction, fun.index]);
fun.exportFunc();
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var buffer = %SerializeWasmModule(module);
module = %DeserializeWasmModule(buffer, wire_bytes);
var instance = new WebAssembly.Instance(module);
assertThrows(instance.exports.main, RangeError);
})();
(function TrapAfterDeserialization() {
print(arguments.callee.name);
function GenerateSerializedModule() {
const builder = new WasmModuleBuilder();
builder.addMemory(1, 1);
builder.addFunction('main', kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
.exportFunc();
const wire_bytes = builder.toBuffer();
const module = new WebAssembly.Module(wire_bytes);
const buffer = %SerializeWasmModule(module);
return [wire_bytes, buffer];
}
const [wire_bytes, buffer] = GenerateSerializedModule();
module = %DeserializeWasmModule(buffer, wire_bytes);
const instance = new WebAssembly.Instance(module);
assertEquals(0, instance.exports.main(0));
assertEquals(0, instance.exports.main(kPageSize - 4));
assertTraps(
kTrapMemOutOfBounds, _ => instance.exports.main(kPageSize - 3));
})();
(function DirectCallAfterSerialization() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
var fun1 = builder.addFunction('fun1', kSig_i_v)
.addBody([kExprI32Const, 23]);
var fun2 = builder.addFunction('fun2', kSig_i_v)
.addBody([kExprI32Const, 42]);
builder.addFunction('main', kSig_i_v)
.addBody([kExprCallFunction, fun1.index,
kExprCallFunction, fun2.index,
kExprI32Add])
.exportFunc();
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var buffer = %SerializeWasmModule(module);
module = %DeserializeWasmModule(buffer, wire_bytes);
var instance = new WebAssembly.Instance(module);
assertEquals(65, instance.exports.main());
})();
(function ImportCallAfterSerialization() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
var fun_import = builder.addImport("", "my_import", kSig_i_v);
var fun = builder.addFunction('fun', kSig_i_v)
.addBody([kExprI32Const, 23]);
builder.addFunction('main', kSig_i_v)
.addBody([kExprCallFunction, fun.index,
kExprCallFunction, fun_import,
kExprI32Add])
.exportFunc();
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var buffer = %SerializeWasmModule(module);
module = %DeserializeWasmModule(buffer, wire_bytes);
var instance = new WebAssembly.Instance(module, {"": {my_import: () => 42 }});
assertEquals(65, instance.exports.main());
})();
(function BranchTableAfterSerialization() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_i)
.addBody([kExprBlock, kWasmVoid,
kExprBlock, kWasmVoid,
kExprBlock, kWasmVoid,
kExprBlock, kWasmVoid,
kExprBlock, kWasmVoid,
kExprBlock, kWasmVoid,
kExprBlock, kWasmVoid,
kExprLocalGet, 0,
kExprBrTable, 6, 0, 1, 2, 3, 4, 5, 6,
kExprEnd,
kExprI32Const, 3,
kExprReturn,
kExprEnd,
kExprI32Const, 7,
kExprReturn,
kExprEnd,
kExprI32Const, 9,
kExprReturn,
kExprEnd,
kExprI32Const, 11,
kExprReturn,
kExprEnd,
kExprI32Const, 23,
kExprReturn,
kExprEnd,
kExprI32Const, 35,
kExprReturn,
kExprEnd,
kExprI32Const, 42,
kExprReturn])
.exportFunc();
var wire_bytes = builder.toBuffer();
var module = new WebAssembly.Module(wire_bytes);
var buffer = %SerializeWasmModule(module);
module = %DeserializeWasmModule(buffer, wire_bytes);
var instance = new WebAssembly.Instance(module);
assertEquals(3, instance.exports.main(0));
assertEquals(7, instance.exports.main(1));
assertEquals(9, instance.exports.main(2));
assertEquals(11, instance.exports.main(3));
assertEquals(23, instance.exports.main(4));
assertEquals(35, instance.exports.main(5));
assertEquals(42, instance.exports.main(6));
assertEquals(42, instance.exports.main(9));
})();

View File

@ -0,0 +1,61 @@
// 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.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
var debug = false;
function SimpleDataSegmentTest(offset) {
print("SimpleDataSegmentTest(" + offset + ")...");
var builder = new WasmModuleBuilder();
builder.addMemory(1, 1);
builder.addFunction("load", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
.exportAs("load");
builder.addActiveDataSegment(0, wasmI32Const(offset), [9, 9, 9, 9]);
var buffer = builder.toBuffer(debug);
var instance = new WebAssembly.Instance(new WebAssembly.Module(buffer));
for (var i = offset - 20; i < offset + 20; i += 4) {
if (i < 0) continue;
var expected = (i == offset) ? 151587081 : 0;
assertEquals(expected, instance.exports.load(i));
}
}
SimpleDataSegmentTest(0);
SimpleDataSegmentTest(4);
SimpleDataSegmentTest(12);
SimpleDataSegmentTest(1064);
function GlobalImportedInitTest(pad) {
print("GlobalImportedInitTest(" + pad + ")...");
var builder = new WasmModuleBuilder();
builder.addMemory(1, 1);
var g = builder.addImportedGlobal("mod", "offset", kWasmI32);
while (pad-- > 0) builder.addGlobal(kWasmI32, false, false); // pad
builder.addFunction("load", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0, 0])
.exportAs("load");
builder.addActiveDataSegment(0, [kExprGlobalGet, g], [5, 5, 5, 5]);
var buffer = builder.toBuffer(debug);
var module = new WebAssembly.Module(buffer);
for (var offset of [0, 12, 192, 1024]) {
var instance = new WebAssembly.Instance(module, {mod: {offset: offset}});
for (var i = offset - 20; i < offset + 20; i += 4) {
if (i < 0) continue;
var expected = i == offset ? 84215045 : 0;
assertEquals(expected, instance.exports.load(i));
}
}
}
GlobalImportedInitTest(0);
GlobalImportedInitTest(1);
GlobalImportedInitTest(4);

34
deps/v8/test/mjsunit/wasm/denormals.js vendored Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2025 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: --allow-natives-syntax
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
let builder = new WasmModuleBuilder();
builder.addFunction("flushed", kSig_i_v).exportFunc().addBody([
...wasmF64Const(Number.MIN_VALUE),
...wasmF64Const(2),
kExprF64Mul,
...wasmF64Const(0),
kExprF64Eq,
kExprIf, kWasmI32,
kExprI32Const, 1,
kExprElse,
kExprI32Const, 0,
kExprEnd,
]);
let inst1 = builder.instantiate();
assertEquals(0, inst1.exports.flushed());
d8.test.setFlushDenormals(true);
let inst2 = builder.instantiate();
let result1 = %IsLiftoffFunction(inst1.exports.flushed) ? 1 : 0;
assertEquals(result1, inst1.exports.flushed());
assertEquals(1, inst2.exports.flushed());
d8.test.setFlushDenormals(false);
assertEquals(0, inst1.exports.flushed());
let result2 = %IsLiftoffFunction(inst2.exports.flushed) ? 0 : 1;
assertEquals(result2, inst2.exports.flushed());

View File

@ -0,0 +1,58 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
// Flags: --wasm-inlining-call-indirect
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptCallIndirect() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
let add = builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
let mul = builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let table = builder.addTable(kWasmFuncRef, 2);
builder.addActiveElementSegment(table.index, wasmI32Const(0), [
[kExprRefFunc, add.index],
[kExprRefFunc, mul.index],
], kWasmFuncRef);
let mainSig =
makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallIndirect, funcRefT, table.index,
]).exportFunc();
let wasm = builder.instantiate().exports;
add = 0;
mul = 1;
assertEquals(42, wasm.main(12, 30, add));
%WasmTierUpFunction(wasm.main);
// Tier up.
assertEquals(42, wasm.main(12, 30, add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Deopt.
assertEquals(-360, wasm.main(12, -30, mul));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
assertEquals(42, wasm.main(12, 30, add));
// Re-optimize.
%WasmTierUpFunction(wasm.main);
assertEquals(360, wasm.main(12, 30, mul));
assertEquals(42, wasm.main(12, 30, add));
})();

View File

@ -0,0 +1,55 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptAfterDebuggingRequest() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
let enterDebuggingIndex = builder.addImport("m", 'enterDebugging', kSig_v_v);
let enterDebugging = () => %WasmEnterDebugging();
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
// if (local[0]) %WasmEnterDebugging();
kExprLocalGet, 0,
kExprIf, kWasmVoid,
kExprCallFunction, enterDebuggingIndex,
kExprEnd,
// call_ref(local[0], local[1], local[2])
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate({m: {enterDebugging}}).exports;
assertEquals(10, wasm.main(0, 10, wasm.add));
%WasmTierUpFunction(wasm.main);
// Not yet in debugging mode.
assertFalse(%IsWasmDebugFunction(wasm.main));
// Trigger debugging mode while the optimized "main" function is on the stack,
// followed by a deoptimization.
assertEquals(56, wasm.main(7, 8, wasm.mul));
// False because we deopted and false because we entered debugging.
assertFalse(%IsTurboFanFunction(wasm.main));
// We need to call it a second time, so that the function gets replaced with
// the "for debugging" version. (Same behavior without deopts enabled.)
assertEquals(0, wasm.main(0, 8, wasm.mul));
assertTrue(%IsWasmDebugFunction(wasm.main));
%WasmLeaveDebugging();
})();

View File

@ -0,0 +1,63 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff
// Flags: --wasm-tiering-budget=1000 --wasm-dynamic-tiering
// Flags: --no-predictable
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptTieringBudget() {
// This can be non-zero in certain variants (e.g. `code_serializer`).
let initialDeoptCount = %WasmDeoptsExecutedCount();
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addLocals(kWasmI32, 1)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(initialDeoptCount, %WasmDeoptsExecutedCount());
print("Running until tier up");
while (!%IsTurboFanFunction(wasm.main)) {
assertEquals(42, wasm.main(12, 30, wasm.add));
}
assertEquals(initialDeoptCount, %WasmDeoptsExecutedCount());
print("Tierup reached");
// Cause deopt.
assertEquals(360, wasm.main(12, 30, wasm.mul));
assertEquals(initialDeoptCount + 1, %WasmDeoptsExecutedCount());
print("Deopt reached");
// Run again until tierup.
while (!%IsTurboFanFunction(wasm.main)) {
assertEquals(42, wasm.main(12, 30, wasm.add));
assertEquals(360, wasm.main(12, 30, wasm.mul));
}
print("Tiered up again");
// If we are very unlucky, there can still be compilations with old feedback
// queued in the background that re-trigger new deopts with the wasm.mul
// target. Therefore we can't assert that the deopt count is still 1.
assertTrue(initialDeoptCount + %WasmDeoptsExecutedCount() < 20);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(42, wasm.main(12, 30, wasm.add));
assertEquals(360, wasm.main(12, 30, wasm.mul));
})();

View File

@ -0,0 +1,116 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Test progressing through the different "states" of feedback:
// - monomorphic
// - polymorphic (2-4 targets)
// - megamorphic
(function TestDeoptCallRef() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
builder.addFunction("sub", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
.exportFunc();
builder.addFunction("first", funcRefT)
.addBody([kExprLocalGet, 0])
.exportFunc();
builder.addFunction("second", funcRefT)
.addBody([kExprLocalGet, 1])
.exportFunc();
builder.addFunction("equals", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Eq,])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addLocals(kWasmI32, 1)
.addBody([
// Set the local to some value.
kExprI32Const, 1,
kExprLocalSet, 3,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
// Use the local after the deopt point.
kExprLocalGet, 3,
kExprI32Mul,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(42, wasm.main(12, 30, wasm.add));
%WasmTierUpFunction(wasm.main);
print("tierup");
assertEquals(42, wasm.main(12, 30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
print("non-deopt call succeeded, now causing deopt");
assertEquals(360, wasm.main(12, 30, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
print("deopt happened");
assertEquals(42, wasm.main(12, 30, wasm.add));
print("collect more feedback in liftoff");
assertEquals(-18, wasm.main(12, 30, wasm.sub));
print("re-opt");
%WasmTierUpFunction(wasm.main);
assertEquals(360, wasm.main(12, 30, wasm.mul));
assertEquals(42, wasm.main(12, 30, wasm.add));
assertEquals(-18, wasm.main(12, 30, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(12, wasm.main(12, 30, wasm.first));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
print("deopt happened");
print("re-opt with maximum polymorphism");
%WasmTierUpFunction(wasm.main);
assertEquals(360, wasm.main(12, 30, wasm.mul));
assertEquals(42, wasm.main(12, 30, wasm.add));
assertEquals(-18, wasm.main(12, 30, wasm.sub));
assertEquals(12, wasm.main(12, 30, wasm.first));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(30, wasm.main(12, 30, wasm.second));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
print("deopt happened");
print("reopt again");
%WasmTierUpFunction(wasm.main);
// All previous call targets still succeed.
assertEquals(360, wasm.main(12, 30, wasm.mul));
assertEquals(42, wasm.main(12, 30, wasm.add));
assertEquals(-18, wasm.main(12, 30, wasm.sub));
assertEquals(12, wasm.main(12, 30, wasm.first));
assertEquals(30, wasm.main(12, 30, wasm.second));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Previously unseen call targets do not cause a deopt any more.
assertEquals(1, wasm.main(42, 42, wasm.equals));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,67 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax --liftoff
// Flags: --wasm-inlining --wasm-inlining-ignore-call-counts --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// This test case tests th scenario where the optimized function "main" doesn't
// have any parameter stack slots while the inner-most inlined function
// "innerInlinee" has multiple ones.
(function TestDeoptManyParameterStackSlotsInInlinedFunctionButNotInOuter() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let sigInner = makeSig([wasmRefType(funcRefT), kWasmI32, kWasmI32, kWasmI32,
kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32,
kWasmI32, kWasmI32], [kWasmI32]);
let innerInlinee = builder.addFunction("innerInlinee", sigInner)
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprLocalGet, 0,
kExprCallRef, funcRefT,
]);
let sigCallRef =
makeSig([wasmRefType(funcRefT)], [kWasmI32]);
let outerInlinee = builder.addFunction("outerInlinee", sigCallRef)
.addBody([
kExprLocalGet, 0, kExprI32Const, 30, kExprI32Const, 12, kExprI32Const, 0,
kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 0,
kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 0,
kExprI32Const, 0,
kExprCallFunction, innerInlinee.index,
]);
builder.addFunction("main", sigCallRef)
.addBody([
kExprLocalGet, 0,
kExprCallFunction, outerInlinee.index,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(42, wasm.main(wasm.add));
%WasmTierUpFunction(wasm.main);
// tierup.
assertEquals(42, wasm.main(wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// deopt.
assertEquals(360, wasm.main(wasm.mul));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,92 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff
// Flags: --wasm-inlining-ignore-call-counts --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptInlinedStacktrace() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("div", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32DivS])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
let inlinee = builder.addFunction("inlinee", mainSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
builder.addFunction("main", mainSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallFunction, inlinee.index,
]).exportFunc();
let wasm = builder.instantiate().exports;
// Collect feedback.
assertEquals(42, wasm.main(12, 30, wasm.add));
// Trigger tierup.
%WasmTierUpFunction(wasm.main);
assertEquals(42, wasm.main(12, 30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Trigger deopt which then calls a target that throws, i.e. the stack trace
// contains frames created by the deoptimizer.
try {
wasm.main(10, 0, wasm.div);
assertUnreachable();
} catch (error) {
verifyException(error);
}
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
// Rerun unoptimized which should produce the same result.
try {
wasm.main(10, 0, wasm.div);
assertUnreachable();
} catch (error) {
verifyException(error);
}
%WasmTierUpFunction(wasm.main);
// Rerun optimized which should also produce the same result and not deopt.
try {
wasm.main(10, 0, wasm.div);
assertUnreachable();
} catch (error) {
verifyException(error);
}
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
function verifyException(error) {
assertMatches(/RuntimeError: divide by zero/, error + "");
let stack = error.stack.split("\n");
let expected = [
/RuntimeError: divide by zero/,
/at div .*wasm-function\[1\]:0x59/,
/at inlinee .*wasm-function\[2\]:0x63/,
/at main .*wasm-function\[3\]:0x6e/,
];
for (let i = 0; i < expected.length; ++i) {
assertMatches(expected[i], stack[i]);
}
}
})();

View File

@ -0,0 +1,104 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff
// Flags: --wasm-inlining-ignore-call-counts --no-jit-fuzzing
// Flags: --wasm-inlining-call-indirect
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptInlined() {
let inlineeIndex = 2;
let sig = 1;
// These different tests define the call "kind" for the inlining of the
// function called "inlinee", not the speculative inlining for add / mul.
let tests = [
{name: "callDirect", ops: [kExprCallFunction, inlineeIndex]},
{name: "callRef", ops: [kExprRefFunc, inlineeIndex, kExprCallRef, sig]},
{name: "callIndirect",
ops: [kExprI32Const, 0, kExprCallIndirect, sig, kTableZero]},
{name: "returnCall", ops: [kExprReturnCall, inlineeIndex]},
{name: "returnCallRef",
ops: [kExprRefFunc, inlineeIndex, kExprReturnCallRef, sig]},
{name: "returnCallIndirect",
ops: [kExprI32Const, 0, kExprReturnCallIndirect, sig, kTableZero]},
];
for (let test of tests) {
print(`test ${test.name}`);
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig = builder.addType(makeSig([kWasmI32, kWasmI32,
wasmRefType(funcRefT)], [kWasmI32]));
assertEquals(sig, mainSig);
let inlinee = builder.addFunction("inlinee", mainSig)
.addBody([
kExprLocalGet, 1,
kExprI32Const, 1,
kExprI32Add,
kExprLocalTee, 1,
kExprLocalGet, 0,
kExprI32Const, 1,
kExprI32Add,
kExprLocalTee, 0,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
assertEquals(inlinee.index, inlineeIndex);
builder.addFunction(test.name, mainSig)
.addLocals(kWasmI32, 1)
.addBody([
kExprLocalGet, 0,
kExprI32Const, 1,
kExprI32Add,
kExprLocalGet, 1,
kExprI32Const, 1,
kExprI32Add,
kExprLocalGet, 2,
...test.ops,
]).exportFunc();
let table = builder.addTable(kWasmFuncRef, 1);
builder.addActiveElementSegment(table.index, wasmI32Const(0),
[[kExprRefFunc, inlinee.index]], kWasmFuncRef);
let wasm = builder.instantiate().exports;
let fct = wasm[test.name];
assertEquals(46, fct(12, 30, wasm.add));
// Tier up.
%WasmTierUpFunction(fct);
assertEquals(46, fct(12, 30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(fct));
}
// Cause deopt.
assertEquals(14 * 32, fct(12, 30, wasm.mul));
// Deopt happened.
if (%IsWasmTieringPredictable()) assertFalse(%IsTurboFanFunction(fct));
assertEquals(46, fct(12, 30, wasm.add));
// Trigger re-opt.
%WasmTierUpFunction(fct);
// Both call targets are used in the re-optimized function, so they don't
// trigger new deopts.
assertEquals(46, fct(12, 30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(fct));
}
assertEquals(14 * 32, fct(12, 30, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(fct));
}
}
})();

View File

@ -0,0 +1,124 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
// Test for different types of stack, local and literal values.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Similar to deopt-value-types.js but focused on int64 which requires int64
// lowering.
(function TestDeoptTypesInt64Values() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_v_v);
builder.addFunction("nop1", funcRefT).addBody([]).exportFunc();
builder.addFunction("nop2", funcRefT).addBody([]).exportFunc();
let mainSig = makeSig([kWasmI64, wasmRefType(funcRefT)], [kWasmI64]);
let localConstant = (0x1n << 32n) + 0x2n;
let valueStackConstant = (0x12345n << 32n) + 0x54321n;
let addedValue = 123n;
let subtractedValue = 321n;
let argumentValue = 0x1234567887654321n;
builder.addFunction("main", mainSig)
.addLocals(kWasmI64, 3)
.addBody([
// Add a constant value as a local.
...wasmI64Const(localConstant),
kExprLocalSet, 2,
// Add a non-constant value as a local.
kExprLocalGet, 0,
...wasmI64Const(addedValue),
kExprI64Add,
kExprLocalSet, 3,
// Push a constant value to the value stack.
...wasmI64Const(valueStackConstant),
// Push a non-constant value to the value stack.
kExprLocalGet, 0,
...wasmI64Const(subtractedValue),
kExprI64Sub,
// Perform the call_ref.
kExprLocalGet, 1,
kExprCallRef, funcRefT,
// Use the locals in the now potentially deoptimized execution.
kExprLocalGet, 0, // the argument
kExprLocalGet, 2, // the constant local
kExprLocalGet, 3, // the non-constant local
kExprLocalGet, 4, // an uninitialized local (0)
kExprI64Add,
kExprI64Add,
kExprI64Add,
kExprI64Add,
kExprI64Add,
]).exportFunc();
let wasm = builder.instantiate().exports;
let expected = localConstant + valueStackConstant + addedValue
- subtractedValue + argumentValue * 3n;
assertEquals(expected, wasm.main(argumentValue, wasm.nop1));
%WasmTierUpFunction(wasm.main);
assertEquals(expected, wasm.main(argumentValue, wasm.nop1));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Deopt happened, the result should still be the same.
assertEquals(expected, wasm.main(argumentValue, wasm.nop2));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
})();
(function TestInlinedI64Params() {
let builder = new WasmModuleBuilder();
let funcRefT = builder.addType(makeSig([kWasmI64, kWasmI64], [kWasmI64]));
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI64Add]).exportFunc();
builder.addFunction("sub", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI64Sub]).exportFunc();
let callerSig =
makeSig([kWasmI64, kWasmI64, wasmRefType(funcRefT)], [kWasmI64]);
let inlinee = builder.addFunction("inlinee", callerSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
builder.addFunction("main", callerSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallFunction, inlinee.index,
]).exportFunc();
let wasm = builder.instantiate({}).exports;
assertEquals(43n, wasm.main(1n, 42n, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(43n, wasm.main(1n, 42n, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(-41n, wasm.main(1n, 42n, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
assertEquals(43n, wasm.main(1n, 42n, wasm.add));
assertEquals(-41n, wasm.main(1n, 42n, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,44 @@
// Copyright 2024 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: --allow-natives-syntax --wasm-deopt --liftoff --no-jit-fuzzing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(
makeSig([wasmRefType(kWasmI31Ref)], [kWasmI32]));
builder.addFunction("getS", calleeSig).addBody([
kExprLocalGet, 0, kGCPrefix, kExprI31GetS,
]).exportFunc();
builder.addFunction("getU", calleeSig).addBody([
kExprLocalGet, 0, kGCPrefix, kExprI31GetU,
]).exportFunc();
builder.addFunction("main", makeSig([wasmRefType(calleeSig)], [kWasmI32]))
.addBody([
...wasmI32Const(1689912565),
kGCPrefix, kExprRefI31,
kExprLocalGet, 0,
kExprCallRef, calleeSig,
]).exportFunc();
const instance = builder.instantiate({});
assertEquals(-457571083, instance.exports.main(instance.exports.getS));
%WasmTierUpFunction(instance.exports.main);
// Test value produced by DeoptimizerLiteral constant.
assertEquals(1689912565, instance.exports.main(instance.exports.getU));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instance.exports.main));
}
// Test value produced by Liftoff.
assertEquals(1689912565, instance.exports.main(instance.exports.getU));
%WasmTierUpFunction(instance.exports.main);
// Test value produced by Turboshaft.
assertEquals(1689912565, instance.exports.main(instance.exports.getU));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance.exports.main));
}

View File

@ -0,0 +1,113 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff
// Flags: --wasm-inlining-ignore-call-counts --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestManyParamsManyLocals() {
// x86-64 has 16 XMM registers, so we need a few values to run out of
// registers and end up with stack slots.
let paramCount = 21;
let builder = new WasmModuleBuilder();
let calleeParams = new Array(paramCount).fill(kWasmS128);
let funcRefT = builder.addType(makeSig(calleeParams, [kWasmS128]));
builder.addFunction("add", funcRefT)
.addBody(generateCalleeBody(kExprI32x4Add)).exportFunc();
builder.addFunction("sub", funcRefT)
.addBody(generateCalleeBody(kExprI32x4Sub)).exportFunc();
builder.addFunction("max", funcRefT)
.addBody(generateCalleeBody(kExprI32x4MaxU)).exportFunc();
let deoptingFct = builder.addFunction(
"deopting", makeSig([...calleeParams, wasmRefType(funcRefT)], [kWasmI32]))
.addBody([
// Push the arguments for the second call from the caller.
...pushArgs(paramCount + 1),
// Create new local arguments for the first call.
...createArgs(paramCount),
kExprLocalGet, paramCount,
kExprCallRef, funcRefT,
kExprLocalSet, 2,
kExprCallRef, funcRefT,
// Add both call_ref results and sum up the lanes.
kExprLocalGet, 2,
...SimdInstr(kExprI32x4Add),
kExprLocalTee, 2,
kSimdPrefix, kExprI32x4ExtractLane, 0,
kExprLocalGet, 2,
kSimdPrefix, kExprI32x4ExtractLane, 1,
kExprLocalGet, 2,
kSimdPrefix, kExprI32x4ExtractLane, 2,
kExprLocalGet, 2,
kSimdPrefix, kExprI32x4ExtractLane, 3,
kExprI32Add,
kExprI32Add,
kExprI32Add,
]).exportFunc();
let mainParams = [kWasmI32, wasmRefType(funcRefT)];
builder.addFunction("main", makeSig(mainParams, [kWasmI32]))
.addLocals(kWasmS128, 1)
.addBody([
...createArgs(paramCount),
kExprLocalGet, 1,
kExprCallFunction, deoptingFct.index,
]).exportFunc();
// [0, 1, ..., paramCount - 1]
let values = [...Array(paramCount).keys()];
let expectedSum = values.reduce((a, b) => a + b) * 4 * 2;
let expectedDiff = values.reduce((a, b) => a - b) * 4 * 2;
let expectedMax = (paramCount - 1) * 4 * 2;
assertEquals(expectedSum, -expectedDiff);
let wasm = builder.instantiate().exports;
assertEquals(expectedSum, wasm.main(0, wasm.add));
%WasmTierUpFunction(wasm.deopting);
assertEquals(expectedSum, wasm.main(0, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.deopting));
}
assertEquals(expectedDiff, wasm.main(0, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.deopting));
}
// Repeat the test but this time with an additional layer of inlining.
%WasmTierUpFunction(wasm.main);
assertEquals(expectedSum, wasm.main(0, wasm.add));
assertEquals(expectedDiff, wasm.main(0, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(expectedMax, wasm.main(0, wasm.max));
function generateCalleeBody(binop) {
let result = [kExprLocalGet, 0];
for (let i = 1; i < paramCount; ++i) {
result.push(kExprLocalGet, i, ...SimdInstr(binop));
}
return result;
}
function createArgs(paramCount) {
let result = [];
for (let i = 0; i < paramCount; ++i) {
result.push(kExprI32Const, i, kSimdPrefix, kExprI32x4Splat);
}
return result;
}
function pushArgs(paramCount) {
let result = [];
for (let i = 0; i < paramCount; ++i) {
result.push(kExprLocalGet, i);
}
return result;
}
})();

View File

@ -0,0 +1,144 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --expose-gc
// Flags: --wasm-inlining-ignore-call-counts --wasm-inlining-factor=30
// Flags: --wasm-inlining-budget=100000 --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Test deopt with many params with different types, some tagged, some untagged.
(function TestManyParamsTagged() {
let create = (function() {
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, false)]);
let array = builder.addArray(kWasmI32, true);
builder.addFunction("struct", makeSig([kWasmI32], [wasmRefType(struct)]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct,
]).exportFunc();
builder.addFunction("array", makeSig([kWasmI32], [wasmRefType(array)]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprArrayNewFixed, array, 1,
]).exportFunc();
return builder.instantiate().exports;
})();
let paramCount = 30;
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, false)]);
let array = builder.addArray(kWasmI32, true);
let types = [
{type: kWasmI32, toI32: [], fromI32: (v) => v},
{type: wasmRefType(struct), toI32: [kGCPrefix, kExprStructGet, struct, 0],
fromI32: (v) => create.struct(v)},
{type: kWasmF64, toI32: [kExprI32SConvertF64], fromI32: (v) => v},
{type: wasmRefType(array),
toI32: [kExprI32Const, 0, kGCPrefix, kExprArrayGet, array],
fromI32: (v) => create.array(v)},
{type: kWasmF32, toI32: [kExprI32SConvertF32], fromI32: (v) => v},
{type: kWasmI31Ref, toI32: [kGCPrefix, kExprI31GetS], fromI32: (v) => v},
{type: kWasmI64, toI32: [kExprI32ConvertI64], fromI32: (v) => BigInt(v)},
];
let calleeParams = new Array(paramCount).fill()
.map((_, i) => types[i % types.length].type);
let funcRefT = builder.addType(makeSig(calleeParams, [kWasmI32]));
let gcImport = builder.addImport("i", "gc", makeSig([], []));
builder.addFunction("add", funcRefT)
.addBody(generateCalleeBody(kExprI32Add)).exportFunc();
builder.addFunction("sub", funcRefT)
.addBody(generateCalleeBody(kExprI32Sub)).exportFunc();
builder.addFunction("add2", funcRefT)
.addBody(generateCalleeBody(kExprI32Add)).exportFunc();
builder.addFunction("addGC", funcRefT)
.addBody([
kExprCallFunction, gcImport,
...generateCalleeBody(kExprI32Add)
]).exportFunc();
let mainParams = [...calleeParams, wasmRefType(funcRefT)];
let main = builder.addFunction("main", makeSig(mainParams, [kWasmI32]))
.addBody([
...pushArgs(paramCount + 1),
kExprCallRef, funcRefT,
// Repeat it to make sure that the parameter slots are still valid.
...pushArgs(paramCount + 1),
kExprCallRef, funcRefT,
// Add both results
kExprI32Add,
]).exportFunc();
// The outer params just contain an extra dummy param in the beginning, so
// they don't align with the parameters of the inner function.
let outerParams = [kWasmI32, ...mainParams];
builder.addFunction("outerDirect", makeSig(outerParams, [kWasmI32]))
.addBody([
...pushArgs(paramCount + 2),
kExprCallFunction, main.index,
kExprReturn,
]).exportFunc();
// [0, 1, ..., paramCount - 1]
let values = [...Array(paramCount).keys()];
let valuesTyped = values.map((_, i) => types[i % types.length].fromI32(i));
let expectedSum = 2 * values.reduce((a, b) => a + b);
let expectedDiff = 2 * values.reduce((a, b) => a - b);
assertEquals(expectedSum, -expectedDiff);
let wasm = builder.instantiate({i: {gc}}).exports;
assertEquals(expectedSum, wasm.main(...valuesTyped, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(expectedSum, wasm.main(...valuesTyped, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(expectedDiff, wasm.main(...valuesTyped, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
// Repeat the test but this time with an additional layer of inlining.
assertEquals(expectedSum, wasm.outerDirect(42, ...valuesTyped, wasm.add));
%WasmTierUpFunction(wasm.outerDirect);
assertEquals(expectedSum, wasm.outerDirect(42, ...valuesTyped, wasm.add));
assertEquals(expectedDiff, wasm.outerDirect(42, ...valuesTyped, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.outerDirect));
}
assertEquals(expectedSum, wasm.outerDirect(42, ...valuesTyped, wasm.add2));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.outerDirect));
}
%WasmTierUpFunction(wasm.outerDirect);
assertEquals(expectedSum, wasm.outerDirect(42, ...valuesTyped, wasm.add2));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.outerDirect));
}
assertEquals(expectedSum, wasm.outerDirect(42, ...valuesTyped, wasm.addGC));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.outerDirect));
}
function generateCalleeBody(binop) {
let result = [kExprLocalGet, 0, ...types[0].toI32];
for (let i = 1; i < paramCount; ++i) {
result.push(kExprLocalGet, i, ...types[i % types.length].toI32, binop);
}
return result;
}
function pushArgs(paramCount) {
let result = [];
for (let i = 0; i < paramCount; ++i) {
result.push(kExprLocalGet, i);
}
return result;
}
})();

View File

@ -0,0 +1,63 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff
// Flags: --wasm-inlining-ignore-call-counts --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestManyParams() {
let paramCount = 12;
let builder = new WasmModuleBuilder();
let calleeParams = new Array(paramCount).fill(kWasmI32);
let funcRefT = builder.addType(makeSig(calleeParams, [kWasmI32]));
builder.addFunction("add", funcRefT)
.addBody(generateCalleeBody(kExprI32Add)).exportFunc();
builder.addFunction("sub", funcRefT)
.addBody(generateCalleeBody(kExprI32Sub)).exportFunc();
let mainParams = [...calleeParams, wasmRefType(funcRefT)];
builder.addFunction("main", makeSig(mainParams, [kWasmI32]))
.addBody([
...pushArgs(paramCount + 1),
kExprCallRef, funcRefT,
]).exportFunc();
// [0, 1, ..., paramCount - 1]
let values = [...Array(paramCount).keys()];
let expectedSum = values.reduce((a, b) => a + b);
let expectedDiff = values.reduce((a, b) => a - b);
assertEquals(expectedSum, -expectedDiff);
let wasm = builder.instantiate().exports;
assertEquals(expectedSum, wasm.main(...values, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(expectedSum, wasm.main(...values, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(expectedDiff, wasm.main(...values, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
function generateCalleeBody(binop) {
let result = [kExprLocalGet, 0];
for (let i = 1; i < paramCount; ++i) {
result.push(kExprLocalGet, i, binop);
}
return result;
}
function pushArgs(paramCount) {
let result = [];
for (let i = 0; i < paramCount; ++i) {
result.push(kExprLocalGet, i);
}
return result;
}
})();

View File

@ -0,0 +1,129 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --expose-gc
// Flags: --wasm-inlining-ignore-call-counts --wasm-inlining-factor=30
// Flags: --wasm-inlining-budget=100000 --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Test deopt with many results with different types.
(function TestManyResults() {
let returnCount = 25;
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, false)]);
let array = builder.addArray(kWasmI32, true);
let gcImport = builder.addImport("i", "gc", makeSig([], []));
let createStruct = builder.addFunction("struct",
makeSig([kWasmI32], [wasmRefType(struct)]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct,
]).exportFunc();
let createArray = builder.addFunction("array",
makeSig([kWasmI32], [wasmRefType(array)]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprArrayNewFixed, array, 1,
]).exportFunc();
let types = [
{type: kWasmI32, toI32: [], fromI32: []},
{type: wasmRefType(struct), toI32: [kGCPrefix, kExprStructGet, struct, 0],
fromI32: [kExprCallFunction, createStruct.index]},
{type: kWasmF64, toI32: [kExprI32SConvertF64],
fromI32: [kExprF64SConvertI32]},
{type: wasmRefType(array),
toI32: [kExprI32Const, 0, kGCPrefix, kExprArrayGet, array],
fromI32: [kExprCallFunction, createArray.index]},
{type: kWasmF32, toI32: [kExprI32SConvertF32],
fromI32: [kExprF32SConvertI32]},
{type: kWasmI31Ref, toI32: [kGCPrefix, kExprI31GetS],
fromI32: [kGCPrefix, kExprRefI31]},
{type: kWasmI64, toI32: [kExprI32ConvertI64],
fromI32: [kExprI64SConvertI32]},
];
let calleeReturns = new Array(returnCount).fill()
.map((_, i) => types[i % types.length].type);
let funcRefT = builder.addType(makeSig([], calleeReturns));
builder.addFunction("one", funcRefT)
.addBody(generateCalleeBody(1)).exportFunc();
builder.addFunction("two", funcRefT)
.addBody(generateCalleeBody(2)).exportFunc();
builder.addFunction("threeGC", funcRefT)
.addBody([
kExprCallFunction, gcImport,
...generateCalleeBody(3)
]).exportFunc();
builder.addFunction("four", funcRefT)
.addBody(generateCalleeBody(4)).exportFunc();
let main = builder.addFunction("passThrough",
makeSig([wasmRefType(funcRefT)], calleeReturns))
.addBody([
kExprLocalGet, 0,
kExprCallRef, funcRefT,
]).exportFunc();
builder.addFunction("passThroughExport",
makeSig([wasmRefType(funcRefT)], [kWasmI32]))
.addLocals(kWasmI32, 1)
.addBody([
kExprLocalGet, 0,
kExprCallFunction, main.index,
...combine(),
]).exportFunc();
let expectedOne = returnCount * (returnCount + 1) / 2;
let expectedTwo = expectedOne + returnCount;
let expectedThree = expectedTwo + returnCount;
let expectedFour = expectedThree + returnCount;
let wasm = builder.instantiate({i: {gc}}).exports;
assertEquals(expectedOne, wasm.passThroughExport(wasm.one));
%WasmTierUpFunction(wasm.passThrough);
assertEquals(expectedTwo, wasm.passThroughExport(wasm.two));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.passThrough));
}
%WasmTierUpFunction(wasm.passThrough);
assertEquals(expectedThree, wasm.passThroughExport(wasm.threeGC));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.passThrough));
}
// This time tier up the outer function.
%WasmTierUpFunction(wasm.passThroughExport);
assertEquals(expectedFour, wasm.passThroughExport(wasm.four));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.passThrough));
}
function generateCalleeBody(value) {
let result = [];
for (let i = 0; i < returnCount; ++i) {
result.push(...wasmI32Const(value++), ...types[i % types.length].fromI32);
}
return result;
}
function combine() {
let result = [];
for (let i = 0; i < returnCount; ++i) {
result.push(
...types[(returnCount - i - 1) % types.length].toI32,
kExprLocalGet, 1,
kExprI32Add,
kExprLocalSet, 1
);
}
result.push(kExprLocalGet, 1);
return result;
}
})();

View File

@ -0,0 +1,57 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptMemoryStart() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addMemory(1, 1);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
// Store some value in the memory.
kExprI32Const, 24, // index
...wasmI32Const(0x12345678), // value
kExprI32StoreMem, 0, 0,
// Perform the call_ref with potential deopt.
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
// Load back the value and check that it's the same.
// Note that the cached memory start should have been invalidated by the
// call_ref, so this should reload the memory start.
kExprI32Const, 24,
kExprI32LoadMem, 0, 0,
...wasmI32Const(0x12345678),
kExprI32Ne,
kExprIf, kWasmVoid,
kExprUnreachable,
kExprEnd,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(42, wasm.main(12, 30, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(42, wasm.main(12, 30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(360, wasm.main(12, 30, wasm.mul));
})();

View File

@ -0,0 +1,57 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptMetrics() {
// This can be non-zero in certain variants (e.g. `code_serializer`).
const initialDeoptCount = %WasmDeoptsExecutedCount();
let builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addLocals(kWasmI32, 1)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
const initialDeoptCountMain = %WasmDeoptsExecutedForFunction(wasm.main);
assertEquals(initialDeoptCount, %WasmDeoptsExecutedCount());
assertEquals(42, wasm.main(12, 30, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(initialDeoptCount, %WasmDeoptsExecutedCount());
assertEquals(360, wasm.main(12, 30, wasm.mul));
assertEquals(initialDeoptCount + 1, %WasmDeoptsExecutedCount());
%WasmTierUpFunction(wasm.main);
assertEquals(42, wasm.main(12, 30, wasm.add));
assertEquals(360, wasm.main(12, 30, wasm.mul));
assertEquals(initialDeoptCount + 1, %WasmDeoptsExecutedCount());
assertEquals(0, %WasmDeoptsExecutedForFunction(wasm.add));
assertEquals(0, %WasmDeoptsExecutedForFunction(wasm.mul));
assertEquals(initialDeoptCountMain + 1,
%WasmDeoptsExecutedForFunction(wasm.main));
// A new instance shares the same counters for deopts.
let wasm2 = builder.instantiate().exports;
assertEquals(0, %WasmDeoptsExecutedForFunction(wasm2.add));
assertEquals(0, %WasmDeoptsExecutedForFunction(wasm2.mul));
assertEquals(initialDeoptCountMain + 1,
%WasmDeoptsExecutedForFunction(wasm2.main));
})();

View File

@ -0,0 +1,60 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Minimal test case for a wasm deopt.
// The following flags can help:
// --trace-deopt-verbose (information about the deopt input and output frames)
// --print-wasm-code (prints some information about the generated deopt data)
// --nodebug-code (keeps the generated code small)
// --code-comments (helpful comments in generated code if enabled in gn args)
(function TestDeoptMinimalCallRef() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig =
makeSig([kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
kExprI32Const, 12,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(42, wasm.main(30, wasm.add));
%WasmTierUpFunction(wasm.main);
// tierup.
assertEquals(42, wasm.main(30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Non-deopt call succeeded, now causing deopt.
assertEquals(360, wasm.main(30, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
// Deopt happened, executions are now in liftoff.
assertEquals(42, wasm.main(30, wasm.add));
// Re-opt.
%WasmTierUpFunction(wasm.main);
// There is feedback for both targets, they do not trigger new deopts.
assertEquals(360, wasm.main(30, wasm.mul));
assertEquals(42, wasm.main(30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,86 @@
// Copyright 2024 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: --allow-natives-syntax --wasm-deopt --liftoff
// Flags: --wasm-inlining-call-indirect --wasm-inlining-ignore-call-counts
// See also inlining-multi-instance.js, from which this is adapted.
// This additionally checks that we don't run into a deopt loop.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
function setup() {
const kInitialTableSize = 1;
let sharedTable = new WebAssembly.Table({
element: "anyfunc",
initial: kInitialTableSize,
});
let builder = new WasmModuleBuilder();
let type_i_v = builder.addType(kSig_i_v);
builder.addImportedTable("env", "table", kInitialTableSize);
// We use this global's value to differentiate which instance we are executing.
let importedGlobal = builder.addImportedGlobal("env", "g", kWasmI32);
builder
.addFunction("callee", kSig_i_v)
.addBody([kExprGlobalGet, importedGlobal, ...wasmI32Const(5), kExprI32Add])
.exportFunc();
builder
.addFunction("call_indirect", type_i_v)
.addBody([
...wasmI32Const(/* index of funcref in table */ 0),
kExprCallIndirect, type_i_v, kTableZero,
])
.exportFunc();
let wasmModule = builder.toModule();
// Both instances share the same table, but have different global values to
// distinguish them.
let instance1 = new WebAssembly.Instance(wasmModule, {
env: {
g: 1000,
table: sharedTable,
},
});
let instance2 = new WebAssembly.Instance(wasmModule, {
env: {
g: 2000,
table: sharedTable,
},
});
// And the table contains a reference to the function of the first instance.
sharedTable.set(0, instance1.exports.callee);
return {instance1, instance2};
}
let {instance1, instance2} = setup();
const func1 = instance1.exports.call_indirect;
const func2 = instance2.exports.call_indirect;
// Same-instance call can be inlined.
assertEquals(1005, func1());
%WasmTierUpFunction(func1);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(func1));
// Since we share code between instances, this is optimized as well.
assertTrue(%IsTurboFanFunction(func2));
}
// Cross-instance call triggers a deopt (and returns the correct result).
assertEquals(1005, func2());
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(func1));
assertFalse(%IsTurboFanFunction(func2));
}
// Re-optimizing after deopting due to non-inlineable target, we may not deopt
// again, otherwise we would have a deopt loop.
%WasmTierUpFunction(func2);
assertEquals(1005, func1());
assertEquals(1005, func2());
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(func1));
assertTrue(%IsTurboFanFunction(func2));
}

View File

@ -0,0 +1,106 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax --no-jit-fuzzing --liftoff
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(makeSig([], [kWasmI32]));
let mainSig = builder.addType(makeSig([wasmRefType(calleeSig)], [kWasmI32]));
let global = builder.addGlobal(kWasmI32, true).exportAs("global");
builder.addFunction("a", calleeSig)
.exportFunc()
.addBody([
kExprGlobalGet, global.index,
kExprI32Const, 1,
kExprI32Add,
]);
builder.addFunction("b", calleeSig)
.exportFunc()
.addBody([
kExprGlobalGet, global.index,
kExprI32Const, 2,
kExprI32Add,
]);
builder.addFunction("main", mainSig).exportFunc()
.addBody([
kExprGlobalGet, global.index,
kExprI32Const, 10,
kExprI32Mul,
kExprLocalGet, 0,
kExprCallRef, calleeSig,
kExprI32Add,
]);
const instanceA = builder.instantiate({});
const instanceB = builder.instantiate({});
instanceA.exports.global.value = 10;
instanceB.exports.global.value = 20;
// Expected output:
// main: global * 10 + callee
// callee:
// - a: global + 1
// - b: global + 2
// Therefore we expect a three digit number:
// - first digit: 1 for instanceA.main, 2 for instanceB.main
// - second digit: 1 for instanceA.(a|b), 2 for instanceB.(a|b)
// - third digit: 1 for a, 2 for b
assertEquals(111, instanceA.exports.main(instanceA.exports.a));
%WasmTierUpFunction(instanceA.exports.main);
assertEquals(111, instanceA.exports.main(instanceA.exports.a));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instanceA.exports.main));
}
// This triggers a deopt as the callee is not function a and the optimized code
// is shared with instanceA.
assertEquals(222, instanceB.exports.main(instanceB.exports.b));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instanceB.exports.main));
}
// This re-optimizes the function with the feedback from instanceB.
// To prevent deopt loops, it must also keep the feedback from instanceA.
%WasmTierUpFunction(instanceB.exports.main);
assertEquals(222, instanceB.exports.main(instanceB.exports.b));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instanceA.exports.main));
}
// Calling a or b does not trigger a deopt.
assertEquals(111, instanceA.exports.main(instanceA.exports.a));
assertEquals(112, instanceA.exports.main(instanceA.exports.b));
assertEquals(221, instanceB.exports.main(instanceB.exports.a));
assertEquals(222, instanceB.exports.main(instanceB.exports.b));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instanceA.exports.main));
assertTrue(%IsTurboFanFunction(instanceB.exports.main));
}
// Note that so far no cross-intance calls have been performed.
// Doing so will trigger a deoptimization as cross-instance calls are not
// inlineable.
assertEquals(122, instanceA.exports.main(instanceB.exports.b));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instanceA.exports.main));
assertFalse(%IsTurboFanFunction(instanceB.exports.main));
}
// Tiering up again will not emit a deopt any more as the feedback contains a
// non-inlineable target.
%WasmTierUpFunction(instanceA.exports.main);
assertEquals(121, instanceA.exports.main(instanceB.exports.a));
assertEquals(122, instanceA.exports.main(instanceB.exports.b));
assertEquals(211, instanceB.exports.main(instanceA.exports.a));
assertEquals(212, instanceB.exports.main(instanceA.exports.b));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instanceA.exports.main));
assertTrue(%IsTurboFanFunction(instanceB.exports.main));
}

View File

@ -0,0 +1,113 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax --no-jit-fuzzing --liftoff
// Flags: --wasm-inlining-ignore-call-counts --wasm-inlining-factor=15
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestMultipleModulesUninlineableTargets() {
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(makeSig([], [kWasmI32]));
let mainSig = builder.addType(makeSig([wasmRefType(calleeSig)], [kWasmI32]));
let callee0 = builder.addFunction("callee_0", calleeSig)
.exportFunc()
.addBody([kExprI32Const, 42]);
let inlinee = builder.addFunction("inlinee", mainSig).addBody([
// Trigger the deopt.
kExprLocalGet, 0,
kExprCallRef, calleeSig,
]);
builder.addFunction("main", mainSig).exportFunc()
.addBody([
// Call the inlinee.
kExprLocalGet, 0,
kExprCallFunction, inlinee.index,
// Just performing a call which triggers an update of the feedback vector.
kExprRefFunc, callee0.index,
kExprCallRef, calleeSig,
kExprI32Add,
]);
const instance = builder.instantiate({});
assertEquals(84, instance.exports.main(instance.exports.callee_0));
%WasmTierUpFunction(instance.exports.main);
assertEquals(84, instance.exports.main(instance.exports.callee_0));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance.exports.main));
}
const instance2 = builder.instantiate({});
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
assertEquals(84, instance2.exports.main(instance.exports.callee_0));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instance2.exports.main));
}
// Run it one more time, so that the call count to inlinee is > 0 for
// instance2 as otherwise the feedback doesn't get updated.
assertEquals(84, instance2.exports.main(instance.exports.callee_0));
%WasmTierUpFunction(instance2.exports.main);
assertEquals(84, instance2.exports.main(instance.exports.callee_0));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
})();
(function TestMultipleModulesUninlineableTargetsRecursiveFrames() {
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(makeSig([], [kWasmI32]));
let mainSig =
builder.addType(makeSig([kWasmI32, wasmRefType(calleeSig)], [kWasmI32]));
let callee0 = builder.addFunction("callee_0", calleeSig)
.exportFunc()
.addBody([kExprI32Const, 42]);
let mainIndex = callee0.index + 1;
let main = builder.addFunction("main", mainSig).exportFunc()
.addBody([
// Call itself recursively.
kExprLocalGet, 0,
kExprIf, kWasmVoid,
kExprLocalGet, 0,
kExprI32Const, 1,
kExprI32Sub,
kExprLocalGet, 1,
kExprCallFunction, mainIndex,
kExprDrop,
kExprEnd,
// Perform the deopting call_ref. (Only the inner-most frame will trigger
// it and deopt the outer frames for all inlined frames.)
kExprLocalGet, 1,
kExprCallRef, calleeSig,
]);
assertEquals(mainIndex, main.index);
const instance = builder.instantiate({});
assertEquals(42, instance.exports.main(7, instance.exports.callee_0));
%WasmTierUpFunction(instance.exports.main);
assertEquals(42, instance.exports.main(7, instance.exports.callee_0));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance.exports.main));
}
const instance2 = builder.instantiate({});
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
assertEquals(42, instance2.exports.main(7, instance.exports.callee_0));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instance2.exports.main));
}
%WasmTierUpFunction(instance2.exports.main);
assertEquals(42, instance2.exports.main(7, instance.exports.callee_0));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
})();

View File

@ -0,0 +1,94 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax --no-jit-fuzzing --liftoff
// Flags: --wasm-inlining-call-indirect
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestCallRef() {
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(makeSig([], [kWasmI32]));
let mainSig = builder.addType(makeSig([wasmRefType(calleeSig)], [kWasmI32]));
builder.addFunction("callee_0", calleeSig)
.exportFunc()
.addBody([kExprI32Const, 42]);
builder.addFunction("main", mainSig).exportFunc()
.addBody([
kExprLocalGet, 0,
kExprCallRef, calleeSig,
]);
const instance = builder.instantiate({});
instance.exports.main(instance.exports.callee_0);
%WasmTierUpFunction(instance.exports.main);
instance.exports.main(instance.exports.callee_0);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance.exports.main));
}
const instance2 = builder.instantiate({});
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
instance2.exports.main(instance.exports.callee_0);
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instance2.exports.main));
}
%WasmTierUpFunction(instance2.exports.main);
instance2.exports.main(instance.exports.callee_0);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
})();
(function TestCallIndirect() {
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(makeSig([], [kWasmI32]));
let mainSig = builder.addType(makeSig([kWasmI32], [kWasmI32]));
let callee1 = builder.addFunction("callee1", calleeSig)
.exportFunc()
.addBody([kExprI32Const, 42]);
let callee2 = builder.addFunction("callee2", calleeSig)
.exportFunc()
.addBody([kExprI32Const, 10]);
let table = builder.addTable(kWasmFuncRef, 2);
builder.addActiveElementSegment(table.index, wasmI32Const(0), [
[kExprRefFunc, callee1.index],
[kExprRefFunc, callee2.index],
], kWasmFuncRef);
builder.addFunction("main", mainSig).exportFunc()
.addBody([
kExprLocalGet, 0,
kExprCallIndirect, calleeSig, table.index,
]);
const instance = builder.instantiate({});
instance.exports.main(0);
%WasmTierUpFunction(instance.exports.main);
instance.exports.main(0);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance.exports.main));
}
const instance2 = builder.instantiate({});
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
instance2.exports.main(1);
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(instance2.exports.main));
}
%WasmTierUpFunction(instance2.exports.main);
instance2.exports.main(1);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(instance2.exports.main));
}
})();

View File

@ -0,0 +1,61 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// Simple test containing more than one deopt point.
(function TestMultipleDeoptPoints() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
let mainSig = makeSig(
[kWasmI32, kWasmI32, wasmRefType(funcRefT), kWasmI32,
wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
kExprLocalGet, 3,
kExprLocalGet, 4,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals((1 + 2) * 3, wasm.main(1, 2, wasm.add, 3, wasm.mul));
%WasmTierUpFunction(wasm.main);
assertEquals((1 + 2) * 3, wasm.main(1, 2, wasm.add, 3, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// New target on 2nd call_ref.
assertEquals((1 + 2) + 3, wasm.main(1, 2, wasm.add, 3, wasm.add));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
// New target on 1st call_ref.
assertEquals((1 * 2) * 3, wasm.main(1, 2, wasm.mul, 3, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
// New combination but no new targets.
assertEquals((1 * 2) + 3, wasm.main(1, 2, wasm.mul, 3, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,169 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptSignalingNanFloat32Literal() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(makeSig([kWasmF32], [kWasmI32]));
builder.addFunction("justOne", funcRefT)
.addBody([kExprI32Const, 1])
.exportFunc();
builder.addFunction("reinterpretF32", funcRefT)
.addBody([kExprLocalGet, 0, kExprI32ReinterpretF32])
.exportFunc();
let mainSig =
makeSig([wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
...wasmF32ConstSignalingNaN(),
kExprLocalGet, 0,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(1, wasm.main(wasm.justOne));
%WasmTierUpFunction(wasm.main);
assertEquals(1, wasm.main(wasm.justOne));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(0x7fa7a1b9, wasm.main(wasm.reinterpretF32));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
assertEquals(0x7fa7a1b9, wasm.main(wasm.reinterpretF32));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();
(function TestDeoptSignalingNanFloat32Value() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(makeSig([kWasmF32], [kWasmI32]));
let mem = builder.addMemory(1, 1);
builder.addActiveDataSegment(
mem.index, [kExprI32Const, 0], [0xb9, 0xa1, 0xa7, 0x7f]);
builder.addFunction("justOne", funcRefT)
.addBody([kExprI32Const, 1])
.exportFunc();
builder.addFunction("reinterpretF32", funcRefT)
.addBody([kExprLocalGet, 0, kExprI32ReinterpretF32])
.exportFunc();
let mainSig =
makeSig([wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
kExprI32Const, 0,
kExprF32LoadMem, 0, 0,
kExprLocalGet, 0,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(1, wasm.main(wasm.justOne));
%WasmTierUpFunction(wasm.main);
assertEquals(1, wasm.main(wasm.justOne));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(0x7fa7a1b9, wasm.main(wasm.reinterpretF32));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
assertEquals(0x7fa7a1b9, wasm.main(wasm.reinterpretF32));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();
(function TestDeoptSignalingNanFloat64Literal() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(makeSig([kWasmF64], [kWasmI64]));
builder.addFunction("justOne", funcRefT)
.addBody([kExprI64Const, 1])
.exportFunc();
builder.addFunction("reinterpretF64", funcRefT)
.addBody([kExprLocalGet, 0, kExprI64ReinterpretF64])
.exportFunc();
let mainSig =
makeSig([wasmRefType(funcRefT)], [kWasmI64]);
builder.addFunction("main", mainSig)
.addBody([
...wasmF64ConstSignalingNaN(),
kExprLocalGet, 0,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(1n, wasm.main(wasm.justOne));
%WasmTierUpFunction(wasm.main);
assertEquals(1n, wasm.main(wasm.justOne));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(0x7ff4000000000000n, wasm.main(wasm.reinterpretF64));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
assertEquals(0x7ff4000000000000n, wasm.main(wasm.reinterpretF64));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();
(function TestDeoptSignalingNanFloat64Value() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(makeSig([kWasmF64], [kWasmI64]));
let mem = builder.addMemory(1, 1);
builder.addActiveDataSegment(
mem.index, [kExprI32Const, 0],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x7f]);
builder.addFunction("justOne", funcRefT)
.addBody([kExprI64Const, 1])
.exportFunc();
builder.addFunction("reinterpretF32", funcRefT)
.addBody([kExprLocalGet, 0, kExprI64ReinterpretF64])
.exportFunc();
let mainSig =
makeSig([wasmRefType(funcRefT)], [kWasmI64]);
builder.addFunction("main", mainSig)
.addBody([
kExprI32Const, 0,
kExprF64LoadMem, 0, 0,
kExprLocalGet, 0,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(1n, wasm.main(wasm.justOne));
%WasmTierUpFunction(wasm.main);
assertEquals(1n, wasm.main(wasm.justOne));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertEquals(0x7ff4000000000000n, wasm.main(wasm.reinterpretF32));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
%WasmTierUpFunction(wasm.main);
assertEquals(0x7ff4000000000000n, wasm.main(wasm.reinterpretF32));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,114 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax --liftoff
// Flags: --wasm-inlining --no-jit-fuzzing
// Flags: --wasm-inlining-call-indirect
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestDeoptWithNonInlineableTargetCallRef() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
let importMul = builder.addImport('m', 'mul', funcRefT);
builder.addExport('mul', importMul);
let mul = (a, b) => a * b;
builder.addFunction('add', funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
let mainSig =
makeSig([kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction('main', mainSig)
.addBody([
kExprI32Const, 12,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate({m: {mul}}).exports;
assertEquals(42, wasm.main(30, wasm.add));
%WasmTierUpFunction(wasm.main);
// Tier-up.
assertEquals(42, wasm.main(30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Non-deopt call succeeded, now causing deopt with imported function.
assertEquals(360, wasm.main(30, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
// Deopt happened, executions are now in Liftoff.
assertEquals(42, wasm.main(30, wasm.add));
// Re-opt.
%WasmTierUpFunction(wasm.main);
// There should be feedback for both targets (one of them being
// non-inlineable), they should not trigger new deopts.
assertEquals(360, wasm.main(30, wasm.mul));
assertEquals(42, wasm.main(30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();
(function TestDeoptWithNonInlineableTargetCallIndirect() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
let importMul = builder.addImport('m', 'mul', funcRefT);
builder.addExport('mul', importMul);
let mul = (a, b) => a * b;
let add = builder.addFunction('add', funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
let table = builder.addTable(kWasmFuncRef, 2);
builder.addActiveElementSegment(table.index, wasmI32Const(0), [
[kExprRefFunc, add.index],
[kExprRefFunc, importMul],
], kWasmFuncRef);
let mainSig =
makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
builder.addFunction('main', mainSig)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallIndirect, funcRefT, table.index,
]).exportFunc();
let wasm = builder.instantiate({m: {mul}}).exports;
let addTableIndex = 0;
let mulTableIndex = 1;
assertEquals(42, wasm.main(12, 30, addTableIndex));
%WasmTierUpFunction(wasm.main);
// Tier-up.
assertEquals(42, wasm.main(12, 30, addTableIndex));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Non-deopt call succeeded, now causing deopt with imported function.
assertEquals(360, wasm.main(12, 30, mulTableIndex));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
// Deopt happened, executions are now in Liftoff.
assertEquals(42, wasm.main(12, 30, addTableIndex));
// Re-opt.
%WasmTierUpFunction(wasm.main);
// There should be feedback for both targets (one of them being
// non-inlineable), they should not trigger new deopts.
assertEquals(360, wasm.main(12, 30, mulTableIndex));
assertEquals(42, wasm.main(12, 30, addTableIndex));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,58 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestNonInlinedTarget() {
let builder = new WasmModuleBuilder();
let funcRefT = builder.addType(makeSig([kWasmI32, kWasmI32], [kWasmI32]));
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add]).exportFunc();
builder.addFunction("sub", funcRefT)
.addBody([
// Some useless instructions to make this non-inlined due to size.
...wasmS128Const(100, 200), kExprDrop,
...wasmS128Const(100, 200), kExprDrop,
...wasmS128Const(100, 200), kExprDrop,
...wasmS128Const(100, 200), kExprDrop,
kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub]).exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul]).exportFunc();
let mainParams = [kWasmI32, kWasmI32, wasmRefType(funcRefT)];
builder.addFunction("main", makeSig(mainParams, [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
]).exportFunc();
let wasm = builder.instantiate().exports;
// Add call feedback for add().
assertEquals(30, wasm.main(10, 20, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(30, wasm.main(10, 20, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
// Call with new target causing a deopt.
assertEquals(-10, wasm.main(10, 20, wasm.sub));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
// Re-opt. Due to the size of sub(), the target will not be inlined.
// This causes sub() to take the slow call_ref implementation and no deopt
// point is created.
%WasmTierUpFunction(wasm.main);
// Calling with a new call target therefore doesn't trigger a deopt.
assertEquals(200, wasm.main(10, 20, wasm.mul));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,98 @@
// Copyright 2024 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.
// This test case reproduces a DCHECK failure where outstanding_baseline_units_
// in the CompilationStateImpl was decremented even though it was already 0.
//
// These are the steps required to reproduce which are tested here:
// 1) Instantiate a wasm module, collect feedback and optimize a wasm function
// inside it which includes a speculatively inlined call with a DeoptimizeIf
// for the bailout.
// 2) Serialize the wasm module.
// 3) Deserialize the wasm module, create a new instance for it without using
// the NativeModuleCache. This will mark the optimized function from (1) as
// RequiredBaselineTier = kTurbofan.
// 4) Trigger the deoptimization on this deserialized Turbofan function. The
// execution now goes back to Liftoff.
// 5) Continue running the Liftoff function until it triggers dynamic tier-up.
// (It has to be a background compilation job!)
// 6) When the tier-up finishes, it sees that the compiled tier (Turbofan) is
// the RequiredBaselineTier of that function while the reached tier is below
// the RequiredBaselineTier (the liftoff code created during deoptimization).
// It therefore decreases the outstanding_baseline_units_ for this instance.
// (Note that this counter is meant to be relevant only during the
// instantiation of a module (happening in step 3 above), afterwards it
// should never be modified again.)
// The code simply did not assume that we'd ever "drop below" the required
// baseline tier and then repeat a compilation with the required baseline
// tier, however with deopts this assumption doesn't hold true any more.
// This leads to a DCHECK failure then as the outstanding_baseline_units_
// should never be negative.
// Speculative inlining via deopts required:
// Flags: --wasm-inlining --wasm-deopt
// Dynamic tier-up required:
// Flags: --liftoff --wasm-dynamic-tiering
// The native module cache prevents hitting the bug:
// Flags: --no-wasm-native-module-cache
// Also reduce tiering budget to speed-up the busy wait on dynamic tier up:
// Flags: --wasm-tiering-budget=100 --expose-gc --allow-natives-syntax
// This test busy-waits for tier-up to be complete, hence it does not work in
// predictable more where we only have a single thread.
// Flags: --no-predictable
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptSerialized() {
let wire_bytes = (function createWireBytes() {
let builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
builder.addFunction("sub", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
.exportFunc();
let mainSig =
makeSig([kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addBody([
kExprI32Const, 12,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprCallRef, funcRefT,
]).exportFunc();
return builder.toBuffer();
})();
let module = new WebAssembly.Module(wire_bytes);
{ // Tier-up main function.
let wasm = new WebAssembly.Instance(module).exports;
assertEquals(42, wasm.main(30, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(42, wasm.main(30, wasm.add));
}
{ // Replace module with deserialized variant.
let buff = %SerializeWasmModule(module);
module = null;
gc(); gc();
module = %DeserializeWasmModule(buff, wire_bytes);
}
{
let wasm = new WebAssembly.Instance(module).exports;
assertEquals(360, wasm.main(30, wasm.mul));
// Trigger dynamic (background) tier-up.
while (!%IsTurboFanFunction(wasm.main)) {
assertEquals(360, wasm.main(30, wasm.mul));
}
// Trigger deopt again.
assertEquals(-18, wasm.main(30, wasm.sub));
}
})();

View File

@ -0,0 +1,52 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// The signal handler itself is completely unrelated to deopts. Still, when
// performing a deopt, the g_thread_in_wasm_code needs to be unset when calling
// into the deoptimizer.
(function TestDeoptSignalHandler() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("zero", funcRefT)
.addBody([kExprI32Const, 0])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addLocals(kWasmI32, 1)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
kExprLocalTee, 3,
kExprI32Eqz,
kExprIf, kWasmVoid,
kExprRefNull, kArrayRefCode,
kGCPrefix, kExprArrayLen, // traps,
kExprUnreachable,
kExprEnd,
kExprLocalGet, 3,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(42, wasm.main(12, 30, wasm.add));
%WasmTierUpFunction(wasm.main);
assertEquals(42, wasm.main(12, 30, wasm.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
assertThrows(() => wasm.main(12, 30, wasm.zero));
})();

View File

@ -0,0 +1,57 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
// Flags: --wasm-inlining-ignore-call-counts --wasm-inlining-factor=15
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// This test case covers a bug where a return call would result in not adding
// the parent FrameState to the deopt node. It is correct that the frame with
// the return call should be skipped (due to the nature of tail calls), however,
// any inlined parent frames of the return call (in this case the main function)
// need to be included.
//
// So, in this particular case at the deopt there should be a FrameState for:
// - inlinee1 (just prior to the callRef)
// - main (just behind the call) as a parent of inlinee1's FrameState
// But no FrameState for inlinee2.
(function TestDeoptInlinedReturnCall() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_v_v);
builder.addFunction("c1", funcRefT).addBody([]).exportFunc();
builder.addFunction("c2", funcRefT).addBody([]).exportFunc();
let inlineeSig = makeSig([wasmRefType(funcRefT)], []);
let inlinee1 = builder.addFunction("inlinee1", inlineeSig).addBody([
kExprLocalGet, 0,
kExprCallRef, funcRefT, // the deopt point.
]);
let inlinee2 = builder.addFunction("inlinee2", inlineeSig).addBody([
kExprLocalGet, 0,
kExprReturnCall, inlinee1.index,
]);
let mainSig =
makeSig([wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addLocals(kWasmI32, 1)
.addBody([
...wasmI32Const(1234567),
kExprLocalSet, 1,
kExprLocalGet, 0,
kExprCallFunction, inlinee2.index,
kExprLocalGet, 1,
]).exportFunc();
let wasm = builder.instantiate().exports;
assertEquals(1234567, wasm.main(wasm.c1));
%WasmTierUpFunction(wasm.main);
assertEquals(1234567, wasm.main(wasm.c2));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.main));
}
})();

View File

@ -0,0 +1,207 @@
// Copyright 2024 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: --allow-natives-syntax --wasm-deopt --liftoff --wasm-inlining
// Flags: --no-jit-fuzzing
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let createUniqueModule = (function() {
// By using a different amount of locals on each module, they are going to be
// unique, so different test cases don't interfer with each other.
let localsCount = 0;
return function createModule() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_i_ii);
builder.addFunction("add", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add])
.exportFunc();
builder.addFunction("mul", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul])
.exportFunc();
builder.addFunction("sub", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub])
.exportFunc();
builder.addFunction("first", funcRefT)
.addBody([kExprLocalGet, 0])
.exportFunc();
builder.addFunction("second", funcRefT)
.addBody([kExprLocalGet, 1])
.exportFunc();
builder.addFunction("equals", funcRefT)
.addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Eq,])
.exportFunc();
let mainSig =
makeSig([kWasmI32, kWasmI32, wasmRefType(funcRefT)], [kWasmI32]);
builder.addFunction("main", mainSig)
.addLocals(kWasmI32, ++localsCount)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
kExprLocalTee, 3,
// If the result is 0, repeat the call, so that we have a second deopt
// point.
kExprI32Eqz,
kExprIf, kWasmVoid,
kExprI32Const, 0,
kExprI32Const, 0,
kExprLocalGet, 2,
kExprCallRef, funcRefT,
kExprDrop,
kExprEnd,
kExprLocalGet, 3,
]).exportFunc();
return builder.toModule();
}
})();
// Test a scenario where each instance only has polymorphic call feedback but
// the instsances combined sum up to more feedback slots than the maximum
// polymorphism (4).
(function TestCollectiveFeedbackMoreThanMaxPolymorphism() {
let module = createUniqueModule();
let wasm1 = new WebAssembly.Instance(module).exports;
// Collect polymorphic feedback for wasm1.
assertEquals(42, wasm1.main(30, 12, wasm1.add));
assertEquals(360, wasm1.main(30, 12, wasm1.mul));
assertEquals(18, wasm1.main(30, 12, wasm1.sub));
%WasmTierUpFunction(wasm1.main);
// Trigger deopt and collect polymorphic feedback for wasm2.
let wasm2 = new WebAssembly.Instance(module).exports;
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm2.main));
}
assertEquals(30, wasm2.main(30, 12, wasm2.first));
if (%IsWasmTieringPredictable()) {
// Deopt due to inlined target mismatch.
assertFalse(%IsTurboFanFunction(wasm2.main));
}
// Returning zero here means that the second call_ref in the main function now
// has feedback for "second".
assertEquals(0, wasm2.main(30, 0, wasm2.second));
// We have 5 different call targets, so the call is megamorphic. (The
// per-instance feedback for both instances is polymorphic.)
%WasmTierUpFunction(wasm2.main);
for (let wasm of [wasm1, wasm2]) {
assertEquals(42, wasm.main(30, 12, wasm.add));
assertEquals(360, wasm.main(30, 12, wasm.mul));
assertEquals(18, wasm.main(30, 12, wasm.sub));
assertEquals(30, wasm.main(30, 12, wasm.first));
assertEquals(12, wasm.main(30, 12, wasm.second));
// New targets don't trigger a de-opt either (as long as they don't return
// zero which would cause them executing the second call_ref).
assertEquals(1, wasm.main(30, 30, wasm.equals));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
}
let wasm3 = new WebAssembly.Instance(module).exports;
assertEquals(42, wasm3.main(30, 12, wasm3.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm3.main));
}
// Trigger deoptimization on second call_ref which so far has only seen the
// "second" function.
assertEquals(0, wasm3.main(12, 12, wasm3.sub));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm3.main));
}
// Now that we are back in liftoff, collect feedback for equals on the first
// call_ref as well.
assertEquals(0, wasm3.main(30, 12, wasm3.equals));
%WasmTierUpFunction(wasm3.main);
// The first call is still megamorphic, so we can call all functions without
// triggering a deopt (as long as the result isn't zero).
for (let wasm of [wasm1, wasm2, wasm3]) {
assertEquals(42, wasm.main(30, 12, wasm.add));
assertEquals(360, wasm.main(30, 12, wasm.mul));
assertEquals(18, wasm.main(30, 12, wasm.sub));
assertEquals(30, wasm.main(30, 12, wasm.first));
assertEquals(12, wasm.main(30, 12, wasm.second));
assertEquals(1, wasm.main(30, 30, wasm.equals));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
}
})();
// Test a scenario where the processed feedback is megamorphic based on the
// first module (using the megamorphic symbol in the feedback vector) and we get
// inlineable (in this case monomorphic but would be the same for polymorphic)
// feedback from the second module triggering a tier-up.
// The new tier-up should not overwrite / replace the megamorphic feedback.
(function TestMegamorphicFeedbackStaysMegamorphic() {
let module = createUniqueModule();
let wasm1 = new WebAssembly.Instance(module).exports;
// Collect megamorphic feedback for the first call_ref on wasm1.
assertEquals(42, wasm1.main(30, 12, wasm1.add));
assertEquals(360, wasm1.main(30, 12, wasm1.mul));
assertEquals(18, wasm1.main(30, 12, wasm1.sub));
assertEquals(30, wasm1.main(30, 12, wasm1.first));
assertEquals(0, wasm1.main(30, 0, wasm1.second));
%WasmTierUpFunction(wasm1.main);
// As the feedback is megamorphic, new call targets don't trigger a deopt.
assertEquals(1, wasm1.main(30, 30, wasm1.equals));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm1.main));
}
// Triggering a deopt with the first instance. (Note that for the second
// call_ref we have speculative inlining for wasm.second, so we can trigger a
// deopt on that call_ref.)
assertEquals(0, wasm1.main(0, 0, wasm1.add)); // MARKER_FOR_BELOW
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm1.main));
}
// Create a second instance. Initialize its feedback with a single target for
// both call_refs (sub).
let wasm2 = new WebAssembly.Instance(module).exports;
assertEquals(0, wasm2.main(30, 30, wasm2.sub));
// Tiering up with the feedback from the second instance, the merged feedback
// has to be megamorphic for the first call_ref.
%WasmTierUpFunction(wasm2.main);
for (let wasm of [wasm1, wasm2]) {
assertEquals(42, wasm.main(30, 12, wasm.add));
assertEquals(360, wasm.main(30, 12, wasm.mul));
assertEquals(18, wasm.main(30, 12, wasm.sub));
assertEquals(30, wasm.main(30, 12, wasm.first));
assertEquals(12, wasm.main(30, 12, wasm.second));
assertEquals(1, wasm.main(30, 30, wasm.equals));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.main));
}
}
// Just highlighting a shortcoming of the current implementation, the feedback
// for the second call_ref with wasm.add as a call target that triggered the
// deopt above can still trigger a deopt as we did not tier up with that
// feedback, yet, so the collected processed feedback doesn't know about it.
assertEquals(0, wasm2.main(0, 0, wasm2.add));
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm1.main));
assertFalse(%IsTurboFanFunction(wasm2.main));
}
// Now, if we trigger another tierup on wasm1, the feedback for the add call
// target for the second call_ref will be used. Note that this feedback was
// collected at the line with the "MARKER_FOR_BELOW" comment already, the
// deopt just above only collects feedback on the executing instance wasm2.
%WasmTierUpFunction(wasm1.main);
// No more deopt for add on the second call_ref.
assertEquals(0, wasm1.main(0, 0, wasm1.add));
assertEquals(0, wasm2.main(0, 0, wasm2.add));
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm1.main));
assertTrue(%IsTurboFanFunction(wasm2.main));
}
})();

View File

@ -0,0 +1,85 @@
// Copyright 2024 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: --wasm-staging --wasm-deopt --liftoff
// Flags: --wasm-inlining-factor=15 --allow-natives-syntax --no-jit-fuzzing
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
// This test case covers a bug where the deoptimizer didn't correctly count the
// amount of parameter stack slots if there aren't any tagged stack slots
// present and there is at least one S128 parameter passed via stack slots.
// This test case reproduces this exact situation on x64, so all comments assume
// wasm calling conventions on x64.
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(makeSig([kWasmI32], [kWasmI32]));
let callee0 = builder.addFunction("callee0", calleeSig).addBody([
kExprLocalGet, 0,
]);
let callee1 = builder.addFunction("callee1", calleeSig).addBody([
kExprLocalGet, 0,
]);
let $global0 = builder.addGlobal(kWasmI32, true, false, wasmI32Const(0));
let $table0 = builder.addTable(kWasmFuncRef, 2, 2);
let $segment0 = builder.addActiveElementSegment($table0.index, wasmI32Const(0),
[[kExprRefFunc, callee0.index], [kExprRefFunc, callee1.index]], kWasmFuncRef);
let fctToOptimize = builder.addFunction("fctToOptimize", makeSig(
[ // Parameters
// 6 S128 values passed in registers xmm1 - xmm6 (see wasm-linkage.h).
kWasmS128, kWasmS128, kWasmS128, kWasmS128, kWasmS128, kWasmS128,
// 1 S128 value passed on the stack needing 2! stack slots.
kWasmS128,
// 6 i32 values passed in general purpose registers.
kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32,
// 1 i32 value passed on the stack needing 1 stack slot.
kWasmI32
],
[kWasmI32]))
.addBody([
// Perform a call to either callee0 or callee1 and pass the i32 value from
// the stack to the callee. Both callees will simply return that value.
kExprLocalGet, 13,
kExprGlobalGet, $global0.index,
kExprTableGet, $table0.index,
kGCPrefix, kExprRefCast, calleeSig,
kExprCallRef, calleeSig,
]).exportFunc();
builder.addFunction("main", kSig_i_i).addBody([
kExprLocalGet, 0, // $var0
kExprGlobalSet, $global0.index,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 0,
kSimdPrefix, kExprI8x16Splat,
kExprI32Const, 2,
...wasmI32Const(3),
...wasmI32Const(4),
kExprI32Const, 5,
...wasmI32Const(6),
kExprI32Const, 7,
// Pass the call index as the last parameter which will be passed via a
// stack slot.
kExprLocalGet, 0,
kExprCallFunction, fctToOptimize.index,
]).exportFunc();
const instance = builder.instantiate({});
assertEquals(0, instance.exports.main(0));
%WasmTierUpFunction(instance.exports.fctToOptimize);
assertEquals(1, instance.exports.main(1));

View File

@ -0,0 +1,52 @@
// Copyright 2024 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: --wasm-staging --allow-natives-syntax --wasm-deopt
// Flags: --liftoff
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
(function TestDeoptWithUntaggedStackSlotsOnly() {
const builder = new WasmModuleBuilder();
let calleeSig = builder.addType(kSig_v_v);
let callee0 = builder.addFunction("c0", kSig_v_v).addBody([]);
let callee1 = builder.addFunction("c1", kSig_v_v).addBody([]);
let $table0 = builder.addTable(wasmRefNullType(calleeSig), 2, 2);
builder.addActiveElementSegment($table0.index, wasmI32Const(0),
[[kExprRefFunc, callee0.index], [kExprRefFunc, callee1.index]],
wasmRefNullType(calleeSig));
// The function uses the first argument as the call target in the table and
// then adds up all the parameters. The call_ref with the callee is only used
// to trigger the deopt.
let mainSig = builder.addType(makeSig([
kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32, kWasmI32],
[kWasmI32]));
builder.addFunction("main", mainSig).addBody([
kExprLocalGet, 0,
kExprTableGet, $table0.index,
kExprCallRef, calleeSig,
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprLocalGet, 3,
kExprLocalGet, 4,
kExprLocalGet, 5,
kExprLocalGet, 6,
kExprI32Add,
kExprI32Add,
kExprI32Add,
kExprI32Add,
kExprI32Add,
kExprI32Add,
]).exportFunc();
const instance = builder.instantiate({});
const wasm = instance.exports;
assertEquals(0+1+2+3+4+5+6, wasm.main(0, 1, 2, 3, 4, 5, 6));
%WasmTierUpFunction(wasm.main);
assertEquals(1+1+2+3+4+5+6, wasm.main(1, 1, 2, 3, 4, 5, 6));
})();

View File

@ -0,0 +1,244 @@
// Copyright 2024 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: --wasm-deopt --allow-natives-syntax
// Flags: --wasm-inlining --liftoff --no-jit-fuzzing
// Test for different types of stack, local and literal values.
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDeoptTypesLiteralsInLocals() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_v_v);
builder.addFunction("nop1", funcRefT).addBody([]).exportFunc();
builder.addFunction("nop2", funcRefT).addBody([]).exportFunc();
let mainSig = makeSig([wasmRefType(funcRefT)], [kWasmF64]);
builder.addFunction("literals", mainSig)
.addLocals(kWasmI32, 1)
.addLocals(kWasmI64, 1)
.addLocals(kWasmF32, 1)
.addLocals(kWasmF64, 1)
.addLocals(kWasmI31Ref, 2)
.addLocals(kWasmS128, 1)
.addBody([
// Initialize the locals with non-default literal values.
...wasmI32Const(32),
kExprLocalSet, 1,
...wasmI64Const(64),
kExprLocalSet, 2,
...wasmF32Const(3.2),
kExprLocalSet, 3,
...wasmF64Const(6.4),
kExprLocalSet, 4,
...wasmI32Const(31),
kGCPrefix, kExprRefI31,
kExprLocalSet, 5,
kExprRefNull, kI31RefCode,
kExprLocalSet, 6,
...wasmS128Const(127, 129),
kExprLocalSet, 7,
// Perform the call_ref.
kExprLocalGet, 0,
kExprCallRef, funcRefT,
// Use the locals in the now potentially deoptimized execution.
kExprLocalGet, 1,
kExprF64SConvertI32,
kExprLocalGet, 2,
kExprF64SConvertI64,
kExprLocalGet, 3,
kExprF64ConvertF32,
kExprLocalGet, 4,
kExprLocalGet, 5,
kGCPrefix, kExprI31GetS,
kExprF64SConvertI32,
kExprLocalGet, 6,
kExprRefIsNull,
kExprF64SConvertI32,
kExprLocalGet, 7,
kSimdPrefix, kExprF64x2ExtractLane, 0,
kExprLocalGet, 7,
kSimdPrefix, kExprF64x2ExtractLane, 1,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
]).exportFunc();
let wasm = builder.instantiate().exports;
let expected = 32 + 64 + 3.2 + 6.4 + 31 + 1 + 127 + 129;
let delta = 1e-4;
assertEqualsDelta(expected, wasm.literals(wasm.nop1), delta);
%WasmTierUpFunction(wasm.literals);
assertEqualsDelta(expected, wasm.literals(wasm.nop1), delta);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.literals));
}
// Deopt happened, the result should still be the same.
assertEqualsDelta(expected, wasm.literals(wasm.nop2), delta);
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.literals));
}
})();
(function TestDeoptTypesLiteralsOnValueStack() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_v_v);
builder.addFunction("nop1", funcRefT).addBody([]).exportFunc();
builder.addFunction("nop2", funcRefT).addBody([]).exportFunc();
let mainSig = makeSig([wasmRefType(funcRefT)], [kWasmF64]);
builder.addFunction("literals", mainSig)
.addLocals(kWasmI32, 1)
.addLocals(kWasmI64, 1)
.addLocals(kWasmF32, 1)
.addLocals(kWasmF64, 1)
.addLocals(kWasmI31Ref, 2)
.addLocals(kWasmS128, 1)
.addBody([
...wasmI32Const(32),
...wasmI64Const(64),
...wasmF32Const(3.2),
...wasmF64Const(6.4),
...wasmI32Const(31),
kGCPrefix, kExprRefI31,
kExprRefNull, kI31RefCode,
...wasmS128Const(127, 129),
// Perform the call_ref.
kExprLocalGet, 0,
kExprCallRef, funcRefT,
// Use the values.
kExprLocalSet, 7,
kExprLocalSet, 6,
kExprLocalSet, 5,
kExprLocalSet, 4,
kExprLocalSet, 3,
kExprLocalSet, 2,
kExprF64SConvertI32,
kExprLocalGet, 2,
kExprF64SConvertI64,
kExprLocalGet, 3,
kExprF64ConvertF32,
kExprLocalGet, 4,
kExprLocalGet, 5,
kGCPrefix, kExprI31GetS,
kExprF64SConvertI32,
kExprLocalGet, 6,
kExprRefIsNull,
kExprF64SConvertI32,
kExprLocalGet, 7,
kSimdPrefix, kExprF64x2ExtractLane, 0,
kExprLocalGet, 7,
kSimdPrefix, kExprF64x2ExtractLane, 1,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
]).exportFunc();
let wasm = builder.instantiate().exports;
let expected = 32 + 64 + 3.2 + 6.4 + 31 + 1 + 127 + 129;
let delta = 1e-4;
assertEqualsDelta(expected, wasm.literals(wasm.nop1), delta);
%WasmTierUpFunction(wasm.literals);
assertEqualsDelta(expected, wasm.literals(wasm.nop1), delta);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.literals));
}
// Deopt happened, the result should still be the same.
assertEqualsDelta(expected, wasm.literals(wasm.nop2), delta);
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.literals));
}
})();
(function TestDeoptTypesNonLiterals() {
var builder = new WasmModuleBuilder();
let funcRefT = builder.addType(kSig_v_v);
builder.addFunction("nop1", funcRefT).addBody([]).exportFunc();
builder.addFunction("nop2", funcRefT).addBody([]).exportFunc();
let mainSig = makeSig([kWasmI32, wasmRefType(funcRefT)], [kWasmF64]);
builder.addFunction("locals", mainSig)
.addLocals(kWasmI32, 1)
.addLocals(kWasmI64, 1)
.addLocals(kWasmF32, 1)
.addLocals(kWasmF64, 1)
.addLocals(kWasmI31Ref, 1)
.addLocals(kWasmS128, 1)
.addBody([
// Initialize the locals with non-default non-literal values.
kExprLocalGet, 0,
kExprLocalTee, 2,
kExprI64SConvertI32,
kExprLocalTee, 3,
kExprF32SConvertI64,
kExprLocalTee, 4,
kExprF64ConvertF32,
kExprLocalSet, 5,
kExprLocalGet, 0,
kGCPrefix, kExprRefI31,
kExprLocalSet, 6,
kExprLocalGet, 0,
kExprF64SConvertI32,
kSimdPrefix, kExprF64x2Splat,
kExprLocalSet, 7,
// Perform the call_ref.
kExprLocalGet, 1,
kExprCallRef, funcRefT,
// Use the locals in the now potentially deoptimized stack.
kExprLocalGet, 2,
kExprF64SConvertI32,
kExprLocalGet, 3,
kExprF64SConvertI64,
kExprLocalGet, 4,
kExprF64ConvertF32,
kExprLocalGet, 5,
kExprLocalGet, 6,
kGCPrefix, kExprI31GetS,
kExprF64SConvertI32,
kExprLocalGet, 7,
kSimdPrefix, kExprF64x2ExtractLane, 0,
kExprLocalGet, 7,
kSimdPrefix, kExprF64x2ExtractLane, 1,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
kExprF64Add,
]).exportFunc();
let wasm = builder.instantiate().exports;
let delta = 1e-4;
assertEqualsDelta(42 * 7, wasm.locals(42, wasm.nop1), delta);
%WasmTierUpFunction(wasm.locals);
assertEqualsDelta(42 * 7, wasm.locals(42, wasm.nop1), delta);
if (%IsWasmTieringPredictable()) {
assertTrue(%IsTurboFanFunction(wasm.locals));
}
// Deopt happened, the result should still be the same.
assertEqualsDelta(42 * 7, wasm.locals(42, wasm.nop2), delta);
if (%IsWasmTieringPredictable()) {
assertFalse(%IsTurboFanFunction(wasm.locals));
}
})();

View File

@ -0,0 +1,9 @@
// Copyright 2017 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: --allow-natives-syntax --no-wasm-trap-handler
// This test makes sure that --no-wasm-trap-handler has the correct effect.
assertFalse(%IsWasmTrapHandlerEnabled());

Some files were not shown because too many files have changed in this diff Show More