mirror of
https://github.com/nodejs/node.git
synced 2025-05-15 19:48:12 +00:00

PR-URL: https://github.com/nodejs/node/pull/54077 Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
253 lines
9.7 KiB
JavaScript
253 lines
9.7 KiB
JavaScript
// 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: --experimental-wasm-multi-memory --experimental-wasm-memory64
|
|
|
|
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
|
|
|
function assertMemoryEquals(expected, memory) {
|
|
assertInstanceof(memory, WebAssembly.Memory);
|
|
assertInstanceof(expected, Uint8Array);
|
|
const buf = new Uint8Array(memory.buffer);
|
|
// For better output, check the first 50 bytes separately first.
|
|
assertEquals(expected.slice(0, 50), buf.slice(0, 50));
|
|
// Now also check the full memory content.
|
|
assertEquals(expected, buf);
|
|
}
|
|
|
|
(function testBasicMultiMemory() {
|
|
print(arguments.callee.name);
|
|
// Test two memories: One 32-bit and one 64-bit, in both orders.
|
|
for (let mem64_idx of [0, 1]) {
|
|
const builder = new WasmModuleBuilder();
|
|
const mem32_idx = 1 - mem64_idx;
|
|
|
|
const mem32_pages = 1;
|
|
const mem32_size = mem32_pages * kPageSize;
|
|
const mem64_pages = 3;
|
|
const mem64_size = mem64_pages * kPageSize;
|
|
if (mem32_idx == 0) {
|
|
builder.addMemory(mem32_pages, mem32_pages);
|
|
builder.addMemory64(mem64_pages, mem64_pages);
|
|
} else {
|
|
builder.addMemory64(mem64_pages, mem64_pages);
|
|
builder.addMemory(mem32_pages, mem32_pages);
|
|
}
|
|
builder.exportMemoryAs('mem32', mem32_idx);
|
|
builder.exportMemoryAs('mem64', mem64_idx);
|
|
|
|
builder.addFunction('load32', kSig_i_i)
|
|
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0x40, mem32_idx, 0])
|
|
.exportFunc();
|
|
builder.addFunction('load64', kSig_i_l)
|
|
.addBody([kExprLocalGet, 0, kExprI32LoadMem, 0x40, mem64_idx, 0])
|
|
.exportFunc();
|
|
|
|
const instance = builder.instantiate();
|
|
const mem32_offset = 48;
|
|
const mem64_offset = 16;
|
|
const value = 13;
|
|
const view32 = new DataView(instance.exports.mem32.buffer);
|
|
const view64 = new DataView(instance.exports.mem64.buffer);
|
|
view32.setInt32(mem32_offset, value, true);
|
|
view64.setInt32(mem64_offset, value, true);
|
|
|
|
const {load32, load64} = instance.exports;
|
|
|
|
assertEquals(value, load32(mem32_offset));
|
|
assertEquals(0, load32(mem64_offset));
|
|
assertEquals(0, load64(BigInt(mem32_offset)));
|
|
assertEquals(value, load64(BigInt(mem64_offset)));
|
|
|
|
assertEquals(0, load32(mem32_size - 4));
|
|
assertEquals(0, load64(BigInt(mem64_size - 4)));
|
|
assertTraps(kTrapMemOutOfBounds, () => load32(mem32_size - 3));
|
|
assertTraps(kTrapMemOutOfBounds, () => load64(BigInt(mem64_size - 3)));
|
|
}
|
|
})();
|
|
|
|
(function testMultiMemoryInit() {
|
|
print(arguments.callee.name);
|
|
// Test two memories: One 32-bit and one 64-bit, in both orders.
|
|
for (let mem64_idx of [0, 1]) {
|
|
const builder = new WasmModuleBuilder();
|
|
const mem32_idx = 1 - mem64_idx;
|
|
|
|
const mem32_pages = 1;
|
|
const mem32_size = mem32_pages * kPageSize;
|
|
const mem64_pages = 3;
|
|
const mem64_size = mem64_pages * kPageSize;
|
|
if (mem32_idx == 0) {
|
|
builder.addMemory(mem32_pages, mem32_pages);
|
|
builder.addMemory64(mem64_pages, mem64_pages);
|
|
} else {
|
|
builder.addMemory64(mem64_pages, mem64_pages);
|
|
builder.addMemory(mem32_pages, mem32_pages);
|
|
}
|
|
builder.exportMemoryAs('mem32', mem32_idx);
|
|
builder.exportMemoryAs('mem64', mem64_idx);
|
|
|
|
const data = [1, 2, 3, 4, 5];
|
|
const seg_id = builder.addPassiveDataSegment(data);
|
|
|
|
builder.addFunction('init32', kSig_v_iii)
|
|
.addBody([
|
|
kExprLocalGet, 0, // dst
|
|
kExprLocalGet, 1, // offset
|
|
kExprLocalGet, 2, // size
|
|
kNumericPrefix, kExprMemoryInit, seg_id, mem32_idx
|
|
])
|
|
.exportFunc();
|
|
builder.addFunction('init64', kSig_v_lii)
|
|
.addBody([
|
|
kExprLocalGet, 0, // dst
|
|
kExprLocalGet, 1, // offset
|
|
kExprLocalGet, 2, // size
|
|
kNumericPrefix, kExprMemoryInit, seg_id, mem64_idx
|
|
])
|
|
.exportFunc();
|
|
|
|
const instance = builder.instantiate();
|
|
const {init32, init64} = instance.exports;
|
|
const expected_mem32 = new Uint8Array(mem32_size);
|
|
const expected_mem64 = new Uint8Array(mem64_size);
|
|
|
|
assertMemoryEquals(expected_mem32, instance.exports.mem32);
|
|
assertMemoryEquals(expected_mem64, instance.exports.mem64);
|
|
|
|
init32(7, 1, 3); // dst, (data) offset, size
|
|
expected_mem32.set(data.slice(1, 4), 7);
|
|
assertMemoryEquals(expected_mem32, instance.exports.mem32);
|
|
assertMemoryEquals(expected_mem64, instance.exports.mem64);
|
|
|
|
init64(11n, 3, 2); // dst, (data) offset, size
|
|
expected_mem64.set(data.slice(3, 5), 11);
|
|
assertMemoryEquals(expected_mem32, instance.exports.mem32);
|
|
assertMemoryEquals(expected_mem64, instance.exports.mem64);
|
|
|
|
// Test bounds checks.
|
|
init32(mem32_size - 1, 0, 1); // dst, (data) offset, size
|
|
assertTraps(kTrapMemOutOfBounds, () => init32(mem32_size - 1, 0, 2));
|
|
init64(BigInt(mem64_size - 1), 0, 1); // dst, (data) offset, size
|
|
assertTraps(
|
|
kTrapMemOutOfBounds, () => init64(BigInt(mem64_size - 1), 0, 2));
|
|
}
|
|
})();
|
|
|
|
(function testTypingForCopyBetween32And64Bit() {
|
|
print(arguments.callee.name);
|
|
for (let [src, dst, src_type, dst_type, size_type, expect_valid] of [
|
|
// Copy from 32 to 64 bit with correct types.
|
|
[32, 64, kWasmI32, kWasmI64, kWasmI32, true],
|
|
// Copy from 64 to 32 bit with correct types.
|
|
[64, 32, kWasmI64, kWasmI32, kWasmI32, true],
|
|
// Copy from 32 to 64 bit with always one type wrong.
|
|
[32, 64, kWasmI64, kWasmI64, kWasmI32, false],
|
|
[32, 64, kWasmI32, kWasmI32, kWasmI32, false],
|
|
[32, 64, kWasmI32, kWasmI64, kWasmI64, false],
|
|
// Copy from 64 to 32 bit with always one type wrong.
|
|
[64, 32, kWasmI32, kWasmI32, kWasmI32, false],
|
|
[64, 32, kWasmI64, kWasmI64, kWasmI32, false],
|
|
[64, 32, kWasmI64, kWasmI32, kWasmI64, false],
|
|
]) {
|
|
let type_str = type => type == kWasmI32 ? 'i32' : 'i64';
|
|
print(`- copy from ${src} to ${dst} using types src=${
|
|
type_str(src_type)}, dst=${type_str(dst_type)}, size=${
|
|
type_str(size_type)}`);
|
|
let builder = new WasmModuleBuilder();
|
|
const kMemSizeInPages = 10;
|
|
const kMemSize = kMemSizeInPages * kPageSize;
|
|
let mem64_index = builder.addMemory64(kMemSizeInPages, kMemSizeInPages);
|
|
let mem32_index = builder.addMemory(kMemSizeInPages, kMemSizeInPages);
|
|
builder.exportMemoryAs('mem64', mem64_index);
|
|
builder.exportMemoryAs('mem32', mem32_index);
|
|
|
|
let src_index = src == 32 ? mem32_index : mem64_index;
|
|
let dst_index = dst == 32 ? mem32_index : mem64_index;
|
|
|
|
builder.addFunction('copy', makeSig([dst_type, src_type, size_type], []))
|
|
.addBody([
|
|
kExprLocalGet, 0, // dst
|
|
kExprLocalGet, 1, // src
|
|
kExprLocalGet, 2, // size
|
|
kNumericPrefix, kExprMemoryCopy, dst_index, src_index // memcpy
|
|
])
|
|
.exportFunc();
|
|
|
|
if (expect_valid) {
|
|
builder.toModule();
|
|
} else {
|
|
assertThrows(
|
|
() => builder.toModule(), WebAssembly.CompileError,
|
|
/expected type i(32|64), found local.get of type i(32|64)/);
|
|
}
|
|
}
|
|
})();
|
|
|
|
(function testCopyBetween32And64Bit() {
|
|
print(arguments.callee.name);
|
|
let builder = new WasmModuleBuilder();
|
|
const kMemSizeInPages = 10;
|
|
const kMemSize = kMemSizeInPages * kPageSize;
|
|
let mem64_index = builder.addMemory64(kMemSizeInPages, kMemSizeInPages);
|
|
let mem32_index = builder.addMemory(kMemSizeInPages, kMemSizeInPages);
|
|
builder.exportMemoryAs('mem64', mem64_index);
|
|
builder.exportMemoryAs('mem32', mem32_index);
|
|
|
|
builder
|
|
.addFunction('copy_32_to_64', makeSig([kWasmI64, kWasmI32, kWasmI32], []))
|
|
.addBody([
|
|
kExprLocalGet, 0, // dst
|
|
kExprLocalGet, 1, // src
|
|
kExprLocalGet, 2, // size
|
|
kNumericPrefix, kExprMemoryCopy, mem64_index, mem32_index // memcpy
|
|
])
|
|
.exportFunc();
|
|
builder
|
|
.addFunction('copy_64_to_32', makeSig([kWasmI32, kWasmI64, kWasmI32], []))
|
|
.addBody([
|
|
kExprLocalGet, 0, // dst
|
|
kExprLocalGet, 1, // src
|
|
kExprLocalGet, 2, // size
|
|
kNumericPrefix, kExprMemoryCopy, mem32_index, mem64_index // memcpy
|
|
])
|
|
.exportFunc();
|
|
|
|
let instance = builder.instantiate();
|
|
let {mem32, mem64, copy_32_to_64, copy_64_to_32} = instance.exports;
|
|
|
|
// These helpers extract the memory at [offset, offset+size)] into an Array.
|
|
let memory32 = (offset, size) =>
|
|
Array.from(new Uint8Array(mem32.buffer.slice(offset, offset + size)));
|
|
let memory64 = (offset, size) =>
|
|
Array.from(new Uint8Array(mem64.buffer.slice(offset, offset + size)));
|
|
|
|
// Init mem32[3] to 11.
|
|
new Uint8Array(mem32.buffer)[3] = 11;
|
|
// Copy mem32[2..4] to mem64[1..3].
|
|
copy_32_to_64(1n, 2, 3);
|
|
assertEquals([0, 0, 0, 11, 0], memory32(0, 5));
|
|
assertEquals([0, 0, 11, 0, 0], memory64(0, 5));
|
|
// Copy mem64[2..3] to mem32[1..2].
|
|
copy_64_to_32(1, 2n, 2);
|
|
assertEquals([0, 11, 0, 11, 0], memory32(0, 5));
|
|
assertEquals([0, 0, 11, 0, 0], memory64(0, 5));
|
|
|
|
// Just before OOB.
|
|
copy_32_to_64(BigInt(kMemSize), 0, 0);
|
|
copy_64_to_32(kMemSize, 0n, 0);
|
|
copy_32_to_64(BigInt(kMemSize - 3), 0, 3);
|
|
copy_64_to_32(kMemSize - 3, 0n, 3);
|
|
assertEquals([0, 11, 0], memory64(kMemSize - 3, 3));
|
|
// OOB.
|
|
assertTraps(
|
|
kTrapMemOutOfBounds, () => copy_32_to_64(BigInt(kMemSize + 1), 0, 0));
|
|
assertTraps(
|
|
kTrapMemOutOfBounds, () => copy_64_to_32(kMemSize + 1, 0n, 0));
|
|
assertTraps(
|
|
kTrapMemOutOfBounds, () => copy_32_to_64(BigInt(kMemSize - 2), 0, 3));
|
|
assertTraps(
|
|
kTrapMemOutOfBounds, () => copy_64_to_32(kMemSize - 2, 0n, 3));
|
|
})();
|