v8/test/mjsunit/wasm/externref-globals.js
Andreas Haas 3bd1efd5e6 Reland "[wasm][liftoff] Support for most externref globals" on ia32
Global.set requires a write barrier. This write barrier was missing in
the original CL. The reland only adds the write barrier for ia32, and
bails out on the other platforms.

Original message:

With this CL we add support for all externref globals except for
imported mutable globals.

R=thibaudm@chromium.org, ulan@chromium.org

Bug: v8:7581
Change-Id: I86328a17200d1edc505f4c4357bdf795d95cf0c8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404777
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69912}
2020-09-15 13:46:42 +00:00

619 lines
20 KiB
JavaScript

// 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: --experimental-wasm-reftypes --expose-gc
load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestDefaultValue() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g_null = builder.addGlobal(kWasmExternRef, true).index;
const g_nullfunc = builder.addGlobal(kWasmAnyFunc, true).index;
builder.addFunction("get_externref_global", kSig_r_v)
.addBody([kExprGlobalGet, g_null])
.exportAs("get_externref_global");
builder.addFunction("get_anyfunc_global", kSig_a_v)
.addBody([kExprGlobalGet, g_nullfunc])
.exportAs("get_anyfunc_global");
const instance = builder.instantiate();
assertEquals(null, instance.exports.get_externref_global());
assertEquals(null, instance.exports.get_anyfunc_global());
})();
(function TestDefaultValueSecondGlobal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g_setref = builder.addGlobal(kWasmExternRef, true);
const g_setfunc = builder.addGlobal(kWasmAnyFunc, true);
const g_null = builder.addGlobal(kWasmExternRef, true);
const g_nullfunc = builder.addGlobal(kWasmAnyFunc, true);
builder.addFunction("get_externref_global", kSig_r_r)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g_setref.index,
kExprGlobalGet, g_null.index
])
.exportAs("get_externref_global");
builder.addFunction("get_anyfunc_global", kSig_a_a)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g_setfunc.index,
kExprGlobalGet, g_nullfunc.index
])
.exportAs("get_anyfunc_global");
const instance = builder.instantiate();
assertEquals(null, instance.exports.get_externref_global({}));
assertEquals(null, instance.exports.get_anyfunc_global(
instance.exports.get_externref_global));
})();
(function TestExternRefGlobalChangeValue() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
// Dummy global for offset.
builder.addGlobal(kWasmExternRef, true);
const g = builder.addGlobal(kWasmExternRef, true);
builder.addFunction("main", kSig_r_r)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g.index,
kExprGlobalGet, g.index
])
.exportAs("main");
const instance = builder.instantiate();
const test_value = { hello: 'world' };
assertSame(test_value, instance.exports.main(test_value));
})();
(function TestAnyFuncGlobalChangeValue() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
// Dummy global for offset.
builder.addGlobal(kWasmAnyFunc, true);
const g = builder.addGlobal(kWasmAnyFunc, true);
builder.addFunction("main", kSig_a_a)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g.index,
kExprGlobalGet, g.index
])
.exportAs("main");
const instance = builder.instantiate();
const test_value = instance.exports.main;
assertSame(test_value, instance.exports.main(test_value));
})();
(function TestGlobalChangeValueWithGC() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const gc_index = builder.addImport("q", "gc", kSig_v_v);
// Dummy global for offset.
builder.addGlobal(kWasmExternRef, true);
const g = builder.addGlobal(kWasmExternRef, true);
builder.addFunction("main", kSig_r_r)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g.index,
kExprCallFunction, gc_index, // call gc
kExprGlobalGet, g.index
])
.exportAs("main");
const instance = builder.instantiate({ q: { gc: gc } });
const test_value = { hello: 'world' };
assertSame(test_value, instance.exports.main(test_value));
assertSame(5, instance.exports.main(5));
assertSame("Hello", instance.exports.main("Hello"));
})();
(function TestGlobalAsRoot() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g = builder.addGlobal(kWasmExternRef, true);
builder.addFunction("get_global", kSig_r_v)
.addBody([
kExprGlobalGet, g.index
])
.exportAs("get_global");
builder.addFunction("set_global", kSig_v_r)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g.index
])
.exportAs("set_global");
const instance = builder.instantiate();
let test_value = { hello: 'world' };
instance.exports.set_global(test_value);
test_value = null;
gc();
const result = instance.exports.get_global();
assertEquals('world', result.hello);
})();
(function TestImportedExternRef() {
print(arguments.callee.name);
function Test(obj) {
let builder = new WasmModuleBuilder();
const g = builder.addImportedGlobal('m', 'val', kWasmExternRef);
builder.addFunction('main', kSig_r_v)
.addBody([kExprGlobalGet, g])
.exportAs('main');
const instance = builder.instantiate({ m: { val: obj } });
assertSame(obj, instance.exports.main());
}
Test(null);
Test(undefined);
Test(1653);
Test("mystring");
Test({ q: 14 });
Test(print);
})();
function dummy_func() {
let builder = new WasmModuleBuilder();
builder.addFunction("dummy", kSig_i_v)
.addBody([kExprI32Const, 12])
.exportAs("dummy");
return builder.instantiate().exports.dummy;
}
(function TestImportedAnyFunc() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g = builder.addImportedGlobal('m', 'val', kWasmAnyFunc);
builder.addFunction('main', kSig_a_v)
.addBody([kExprGlobalGet, g])
.exportAs('main');
const module = builder.toModule();
const instance = new WebAssembly.Instance(module, { m: { val: null } });
assertSame(null, instance.exports.main());
const instance2 = new WebAssembly.Instance(
module, { m: { val: instance.exports.main } });
assertSame(instance.exports.main, instance2.exports.main());
assertThrows(() => new WebAssembly.Instance(module, { m: { val: {} } }),
WebAssembly.LinkError);
})();
(function TestExternRefGlobalObjectDefaultValue() {
print(arguments.callee.name);
let default_init = new WebAssembly.Global({ value: 'externref', mutable: true });
assertSame(null, default_init.value);
assertSame(null, default_init.valueOf());
})();
(function TestAnyFuncGlobalObjectDefaultValue() {
print(arguments.callee.name);
let default_init = new WebAssembly.Global({ value: 'anyfunc', mutable: true });
assertSame(null, default_init.value);
assertSame(null, default_init.valueOf());
})();
(function TestExternRefGlobalObject() {
print(arguments.callee.name);
function TestGlobal(obj) {
const global = new WebAssembly.Global({ value: 'externref' }, obj);
assertSame(obj, global.value);
assertSame(obj, global.valueOf());
}
TestGlobal(null);
TestGlobal(undefined);
TestGlobal(1663);
TestGlobal("testmyglobal");
TestGlobal({ a: 11 });
TestGlobal(print);
})();
(function TestAnyFuncGlobalObject() {
print(arguments.callee.name);
const dummy = dummy_func();
const global = new WebAssembly.Global({ value: 'anyfunc' }, dummy);
assertSame(dummy, global.value);
assertSame(dummy, global.valueOf());
const global_null = new WebAssembly.Global({ value: 'anyfunc' }, null);
assertSame(null, global_null.value);
assertSame(null, global_null.valueOf());
assertThrows(() => new WebAssembly.Global({ value: 'anyfunc' }, {}), TypeError);
})();
(function TestExternRefGlobalObjectSetValue() {
print(arguments.callee.name);
let global = new WebAssembly.Global({ value: 'externref', mutable: true });
function TestGlobal(obj) {
global.value = obj;
assertSame(obj, global.value);
assertSame(obj, global.valueOf());
}
TestGlobal(null);
assertThrows(() => TestGlobal(undefined), TypeError);
TestGlobal(1663);
TestGlobal("testmyglobal");
TestGlobal({ a: 11 });
TestGlobal(print);
})();
(function TestAnyFuncGlobalObjectSetValue() {
print(arguments.callee.name);
let global = new WebAssembly.Global({ value: 'anyfunc', mutable: true });
const dummy = dummy_func();
global.value = dummy;
assertSame(dummy, global.value);
assertSame(dummy, global.valueOf());
global.value = null;
assertSame(null, global.value);
assertSame(null, global.valueOf());
assertThrows(() => global.value = {}, TypeError);
})();
(function TestExportMutableRefGlobal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g1 = builder.addGlobal(kWasmExternRef, true).exportAs("global1");
const g2 = builder.addGlobal(kWasmAnyFunc, true).exportAs("global2");
builder.addGlobal(kWasmI32, true); // Dummy.
builder.addGlobal(kWasmExternRef, true); // Dummy.
const g3 = builder.addGlobal(kWasmExternRef, true).exportAs("global3");
const g4 = builder.addGlobal(kWasmAnyFunc, true).exportAs("global4");
builder.addFunction("main",
makeSig([kWasmExternRef, kWasmAnyFunc, kWasmExternRef, kWasmAnyFunc], []))
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g1.index,
kExprLocalGet, 1,
kExprGlobalSet, g2.index,
kExprLocalGet, 2,
kExprGlobalSet, g3.index,
kExprLocalGet, 3,
kExprGlobalSet, g4.index
])
.exportAs("main");
const instance = builder.instantiate();
const obj1 = { x: 221 };
const func2 = instance.exports.main;
const obj3 = print;
const func4 = dummy_func();
instance.exports.main(obj1, func2, obj3, func4);
assertSame(obj1, instance.exports.global1.value);
assertSame(func2, instance.exports.global2.value);
assertSame(obj3, instance.exports.global3.value);
assertSame(func4, instance.exports.global4.value);
})();
(function TestImportMutableExternRefGlobal() {
print(arguments.callee.name);
function Test(obj) {
let builder = new WasmModuleBuilder();
const g = builder.addImportedGlobal('m', 'val', kWasmExternRef, true);
builder.addFunction('main', kSig_r_v)
.addBody([kExprGlobalGet, g])
.exportAs('main');
const global = new WebAssembly.Global({ value: 'externref', mutable: 'true' }, obj);
const instance = builder.instantiate({ m: { val: global } });
assertSame(obj, instance.exports.main());
}
Test(null);
Test(undefined);
Test(1653);
Test("mystring");
Test({ q: 14 });
Test(print);
})();
(function TestImportMutableAnyFuncGlobal() {
print(arguments.callee.name);
function Test(obj) {
let builder = new WasmModuleBuilder();
const g = builder.addImportedGlobal('m', 'val', kWasmAnyFunc, true);
builder.addFunction('main', kSig_a_v)
.addBody([kExprGlobalGet, g])
.exportAs('main');
const global = new WebAssembly.Global({ value: 'anyfunc', mutable: 'true' }, obj);
const instance = builder.instantiate({ m: { val: global } });
assertSame(obj, instance.exports.main());
}
Test(dummy_func());
Test(null);
})();
(function TestImportMutableExternRefGlobalFromOtherInstance() {
print(arguments.callee.name);
// Create an instance which exports globals.
let builder1 = new WasmModuleBuilder();
const g3 = builder1.addGlobal(kWasmExternRef, true).exportAs("e3");
builder1.addGlobal(kWasmI32, true).exportAs("e1"); // Dummy.
builder1.addGlobal(kWasmExternRef, true).exportAs("e4"); // Dummy.
const g2 = builder1.addGlobal(kWasmExternRef, true).exportAs("e2");
builder1.addFunction("set_globals", kSig_v_rr)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g2.index,
kExprLocalGet, 1,
kExprGlobalSet, g3.index,
])
.exportAs("set_globals");
builder1.addFunction('get_global2', kSig_r_v)
.addBody([kExprGlobalGet, g2.index])
.exportAs('get_global2');
builder1.addFunction('get_global3', kSig_r_v)
.addBody([kExprGlobalGet, g3.index])
.exportAs('get_global3');
const instance1 = builder1.instantiate();
const obj2 = { x: 221 };
const obj3 = print;
instance1.exports.set_globals(obj2, obj3);
// Create an instance which imports the globals of the other instance.
let builder2 = new WasmModuleBuilder();
const i1 = builder2.addImportedGlobal('exports', 'e1', kWasmI32, true);
const i2 = builder2.addImportedGlobal('exports', 'e2', kWasmExternRef, true);
const i3 = builder2.addImportedGlobal('exports', 'e3', kWasmExternRef, true);
const i4 = builder2.addImportedGlobal('exports', 'e4', kWasmExternRef, true);
builder2.addExportOfKind("reexport1", kExternalGlobal, i1);
builder2.addExportOfKind("reexport2", kExternalGlobal, i2);
builder2.addExportOfKind("reexport3", kExternalGlobal, i3);
builder2.addExportOfKind("reexport4", kExternalGlobal, i4);
builder2.addFunction("set_globals", kSig_v_rr)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, i2,
kExprLocalGet, 1,
kExprGlobalSet, i3,
])
.exportAs("set_globals");
builder2.addFunction('get_global2', kSig_r_v)
.addBody([kExprGlobalGet, i2])
.exportAs('get_global2');
builder2.addFunction('get_global3', kSig_r_v)
.addBody([kExprGlobalGet, i3])
.exportAs('get_global3');
const instance2 = builder2.instantiate(instance1);
// Check if the globals were imported correctly.
assertSame(obj2, instance2.exports.get_global2());
assertSame(obj3, instance2.exports.get_global3());
assertSame(obj2, instance2.exports.reexport2.value);
assertSame(obj3, instance2.exports.reexport3.value);
// Check if instance2 can make changes visible for instance1.
instance2.exports.set_globals(null, undefined);
assertEquals(null, instance1.exports.get_global2());
assertEquals(undefined, instance1.exports.get_global3());
assertEquals(null, instance2.exports.reexport2.value);
assertEquals(undefined, instance2.exports.reexport3.value);
// Check if instance1 can make changes visible for instance2.
instance1.exports.set_globals("foo", 66343);
assertEquals("foo", instance2.exports.get_global2());
assertEquals(66343, instance2.exports.get_global3());
assertEquals("foo", instance2.exports.reexport2.value);
assertEquals(66343, instance2.exports.reexport3.value);
const bar2 = { f: "oo" };
const bar3 = { b: "ar" };
instance2.exports.reexport2.value = bar2;
instance2.exports.reexport3.value = bar3;
assertSame(bar2, instance1.exports.get_global2());
assertSame(bar3, instance1.exports.get_global3());
assertSame(bar2, instance2.exports.get_global2());
assertSame(bar3, instance2.exports.get_global3());
})();
(function TestImportMutableAnyFuncGlobalFromOtherInstance() {
print(arguments.callee.name);
// Create an instance which exports globals.
let builder1 = new WasmModuleBuilder();
const g3 = builder1.addGlobal(kWasmAnyFunc, true).exportAs("e3");
builder1.addGlobal(kWasmI32, true).exportAs("e1"); // Dummy.
builder1.addGlobal(kWasmAnyFunc, true).exportAs("e4"); // Dummy.
const g2 = builder1.addGlobal(kWasmAnyFunc, true).exportAs("e2");
builder1.addFunction("set_globals", kSig_v_aa)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, g2.index,
kExprLocalGet, 1,
kExprGlobalSet, g3.index,
])
.exportAs("set_globals");
builder1.addFunction('get_global2', kSig_a_v)
.addBody([kExprGlobalGet, g2.index])
.exportAs('get_global2');
builder1.addFunction('get_global3', kSig_a_v)
.addBody([kExprGlobalGet, g3.index])
.exportAs('get_global3');
const instance1 = builder1.instantiate();
const obj2 = dummy_func();
const obj3 = instance1.exports.set_globals;
const obj4 = instance1.exports.get_global3;
instance1.exports.set_globals(obj2, obj3);
// Create an instance which imports the globals of the other instance.
let builder2 = new WasmModuleBuilder();
const i1 = builder2.addImportedGlobal('exports', 'e1', kWasmI32, true);
const i2 = builder2.addImportedGlobal('exports', 'e2', kWasmAnyFunc, true);
const i3 = builder2.addImportedGlobal('exports', 'e3', kWasmAnyFunc, true);
const i4 = builder2.addImportedGlobal('exports', 'e4', kWasmAnyFunc, true);
builder2.addExportOfKind("reexport1", kExternalGlobal, i1);
builder2.addExportOfKind("reexport2", kExternalGlobal, i2);
builder2.addExportOfKind("reexport3", kExternalGlobal, i3);
builder2.addExportOfKind("reexport4", kExternalGlobal, i4);
builder2.addFunction("set_globals", kSig_v_aa)
.addBody([
kExprLocalGet, 0,
kExprGlobalSet, i2,
kExprLocalGet, 1,
kExprGlobalSet, i3,
])
.exportAs("set_globals");
builder2.addFunction('get_global2', kSig_a_v)
.addBody([kExprGlobalGet, i2])
.exportAs('get_global2');
builder2.addFunction('get_global3', kSig_a_v)
.addBody([kExprGlobalGet, i3])
.exportAs('get_global3');
const instance2 = builder2.instantiate(instance1);
// Check if the globals were imported correctly.
assertSame(obj2, instance2.exports.get_global2());
assertSame(obj3, instance2.exports.get_global3());
assertSame(obj2, instance2.exports.reexport2.value);
assertSame(obj3, instance2.exports.reexport3.value);
// Check if instance2 can make changes visible for instance1.
instance2.exports.set_globals(null, obj4);
assertEquals(null, instance1.exports.get_global2());
assertEquals(obj4, instance1.exports.get_global3());
assertEquals(null, instance2.exports.reexport2.value);
assertEquals(obj4, instance2.exports.reexport3.value);
// Check if instance1 can make changes visible for instance2.
instance1.exports.set_globals(obj2, obj3);
assertEquals(obj2, instance2.exports.get_global2());
assertEquals(obj3, instance2.exports.get_global3());
assertEquals(obj2, instance2.exports.reexport2.value);
assertEquals(obj3, instance2.exports.reexport3.value);
})();
(function TestImportMutableAnyFuncGlobalAsExternRefFails() {
print(arguments.callee.name);
let builder1 = new WasmModuleBuilder();
const g3 = builder1.addGlobal(kWasmAnyFunc, true).exportAs("e3");
builder1.addGlobal(kWasmExternRef, true).exportAs("e1"); // Dummy.
builder1.addGlobal(kWasmAnyFunc, true).exportAs("e2"); // Dummy.
const instance1 = builder1.instantiate();
let builder2 = new WasmModuleBuilder();
const i1 = builder2.addImportedGlobal('exports', 'e1', kWasmExternRef, true);
const i2 = builder2.addImportedGlobal('exports', 'e2', kWasmExternRef, true);
assertThrows(() => builder2.instantiate(instance1), WebAssembly.LinkError);
})();
(function TestRefFuncGlobalInit() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g_func = builder.addGlobal(kWasmAnyFunc, true);
const f_func = builder.addFunction('get_anyfunc_global', kSig_a_v)
.addBody([kExprGlobalGet, g_func.index])
.exportAs('get_anyfunc_global');
builder.addDeclarativeElementSegment([f_func.index]);
g_func.function_index = f_func.index;
const instance = builder.instantiate();
assertEquals(
instance.exports.get_anyfunc_global,
instance.exports.get_anyfunc_global());
})();
(function TestRefFuncGlobalInitWithImport() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_i_v);
const import_wasm = builder.addImport('m', 'wasm', sig_index);
const import_js = builder.addImport('m', 'js', sig_index);
const g_wasm = builder.addGlobal(kWasmAnyFunc, true);
const g_js = builder.addGlobal(kWasmAnyFunc, true);
builder.addDeclarativeElementSegment([import_wasm, import_js]);
g_wasm.function_index = import_wasm;
g_js.function_index = import_js;
builder.addFunction('get_global_wasm', kSig_a_v)
.addBody([kExprGlobalGet, g_wasm.index])
.exportFunc();
builder.addFunction('get_global_js', kSig_a_v)
.addBody([kExprGlobalGet, g_js.index])
.exportFunc();
const expected_wasm = dummy_func();
const expected_val = 27;
// I want to test here that imported JS functions get wrapped by wasm-to-js
// and js-to-wasm wrappers. That's why {expected_js} does not return an
// integer directly but an object with a {valueOf} function.
function expected_js() {
const result = {};
result.valueOf = () => expected_val;
return result;
};
const instance =
builder.instantiate({m: {wasm: expected_wasm, js: expected_js}});
assertSame(expected_wasm, instance.exports.get_global_wasm());
assertSame(expected_val, instance.exports.get_global_js()());
})();
(function TestSetGlobalWriteBarrier() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const global = builder.addGlobal(kWasmExternRef, true).index;
builder.addFunction("set_global", kSig_v_r)
.addBody([kExprLocalGet, 0, kExprGlobalSet, global])
.exportFunc();
builder.addFunction("get_global", kSig_r_v)
.addBody([kExprGlobalGet, global])
.exportFunc();
const instance = builder.instantiate();
// Trigger GC twice to make sure the instance is moved to mature space.
gc();
gc();
const test_value = { hello: 'world' };
instance.exports.set_global(test_value);
// Run another GC to test if the writebarrier existed.
gc();
assertSame(test_value, instance.exports.get_global());
})();