blob: 47aa86de93eacd8385a60d8a037192c326ecde49 [file] [edit]
// Tests that array.new and array.new_default produce correctly initialized
// arrays under OMG compilation. Exercises the fill loop generated in
// B3LowerMacros for WasmArrayNew with initValue.
//
// Each test function is called 100 000 times (OMG threshold: 50 000).
const N = 100000;
function makeInstance(bytes) {
return new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(bytes).buffer));
}
// ── Helper: LEB128 encoders ─────────────────────────────────────────────────
function unsignedLEB128(n) {
const result = [];
do {
let byte = n & 0x7f;
n >>>= 7;
if (n !== 0) byte |= 0x80;
result.push(byte);
} while (n !== 0);
return result;
}
function signedLEB128(n) {
const result = [];
let more = true;
while (more) {
let byte = n & 0x7f;
n >>= 7;
if ((n === 0 && (byte & 0x40) === 0) || (n === -1 && (byte & 0x40) !== 0))
more = false;
else
byte |= 0x80;
result.push(byte);
}
return result;
}
function encodeString(s) {
const bytes = [];
for (let i = 0; i < s.length; i++)
bytes.push(s.charCodeAt(i));
return [...unsignedLEB128(bytes.length), ...bytes];
}
function makeSection(id, content) {
return [id, ...unsignedLEB128(content.length), ...content];
}
// ── i32 array.new test module ───────────────────────────────────────────────
// (module
// (type $arr (array (mut i32)))
// ;; array.new: create array of `size` elements all set to `val`, return arr[idx]
// (func (export "newGet") (param $val i32) (param $size i32) (param $idx i32) (result i32)
// (array.get 0 (array.new 0 (local.get $val) (local.get $size)) (local.get $idx)))
// ;; array.new_default: create array, return arr[idx] (should be 0)
// (func (export "defGet") (param $size i32) (param $idx i32) (result i32)
// (array.get 0 (array.new_default 0 (local.get $size)) (local.get $idx)))
// )
function buildI32Module() {
// Type section: 1 array type + 2 func types
const typeSection = makeSection(0x01, [
3, // 3 types
// type 0: (array (mut i32))
0x5e, 0x7f, 0x01,
// type 1: (func (param i32 i32 i32) (result i32))
0x60, 3, 0x7f, 0x7f, 0x7f, 1, 0x7f,
// type 2: (func (param i32 i32) (result i32))
0x60, 2, 0x7f, 0x7f, 1, 0x7f,
]);
const funcSection = makeSection(0x03, [2, 1, 2]); // 2 funcs, type indices 1, 2
const exportSection = makeSection(0x07, [
2, // 2 exports
...encodeString("newGet"), 0x00, 0x00,
...encodeString("defGet"), 0x00, 0x01,
]);
// Code section
const func0Body = [
0, // no locals
0x20, 0x00, // local.get $val
0x20, 0x01, // local.get $size
0xfb, 0x06, 0x00, // array.new 0
0x20, 0x02, // local.get $idx
0xfb, 0x0b, 0x00, // array.get 0
0x0b,
];
const func1Body = [
0, // no locals
0x20, 0x00, // local.get $size
0xfb, 0x07, 0x00, // array.new_default 0
0x20, 0x01, // local.get $idx
0xfb, 0x0b, 0x00, // array.get 0
0x0b,
];
const codeSection = makeSection(0x0a, [
2, // 2 functions
...unsignedLEB128(func0Body.length), ...func0Body,
...unsignedLEB128(func1Body.length), ...func1Body,
]);
return [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
...typeSection, ...funcSection, ...exportSection, ...codeSection];
}
// ── i64 array.new test module ───────────────────────────────────────────────
function buildI64Module() {
const typeSection = makeSection(0x01, [
3,
// type 0: (array (mut i64))
0x5e, 0x7e, 0x01,
// type 1: (func (param i64 i32 i32) (result i64))
0x60, 3, 0x7e, 0x7f, 0x7f, 1, 0x7e,
// type 2: (func (param i32 i32) (result i64))
0x60, 2, 0x7f, 0x7f, 1, 0x7e,
]);
const funcSection = makeSection(0x03, [2, 1, 2]);
const exportSection = makeSection(0x07, [
2,
...encodeString("newGet"), 0x00, 0x00,
...encodeString("defGet"), 0x00, 0x01,
]);
const func0Body = [
0,
0x20, 0x00, // local.get $val (i64)
0x20, 0x01, // local.get $size (i32)
0xfb, 0x06, 0x00, // array.new 0
0x20, 0x02, // local.get $idx (i32)
0xfb, 0x0b, 0x00, // array.get 0
0x0b,
];
const func1Body = [
0,
0x20, 0x00, // local.get $size
0xfb, 0x07, 0x00, // array.new_default 0
0x20, 0x01, // local.get $idx
0xfb, 0x0b, 0x00, // array.get 0
0x0b,
];
const codeSection = makeSection(0x0a, [
2,
...unsignedLEB128(func0Body.length), ...func0Body,
...unsignedLEB128(func1Body.length), ...func1Body,
]);
return [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
...typeSection, ...funcSection, ...exportSection, ...codeSection];
}
// ── f32 array.new test module ───────────────────────────────────────────────
function buildF32Module() {
const typeSection = makeSection(0x01, [
3,
// type 0: (array (mut f32))
0x5e, 0x7d, 0x01,
// type 1: (func (param f32 i32 i32) (result f32))
0x60, 3, 0x7d, 0x7f, 0x7f, 1, 0x7d,
// type 2: (func (param i32 i32) (result f32))
0x60, 2, 0x7f, 0x7f, 1, 0x7d,
]);
const funcSection = makeSection(0x03, [2, 1, 2]);
const exportSection = makeSection(0x07, [
2,
...encodeString("newGet"), 0x00, 0x00,
...encodeString("defGet"), 0x00, 0x01,
]);
const func0Body = [
0,
0x20, 0x00, 0x20, 0x01, 0xfb, 0x06, 0x00,
0x20, 0x02, 0xfb, 0x0b, 0x00, 0x0b,
];
const func1Body = [
0,
0x20, 0x00, 0xfb, 0x07, 0x00,
0x20, 0x01, 0xfb, 0x0b, 0x00, 0x0b,
];
const codeSection = makeSection(0x0a, [
2,
...unsignedLEB128(func0Body.length), ...func0Body,
...unsignedLEB128(func1Body.length), ...func1Body,
]);
return [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
...typeSection, ...funcSection, ...exportSection, ...codeSection];
}
// ── f64 array.new test module ───────────────────────────────────────────────
function buildF64Module() {
const typeSection = makeSection(0x01, [
3,
// type 0: (array (mut f64))
0x5e, 0x7c, 0x01,
// type 1: (func (param f64 i32 i32) (result f64))
0x60, 3, 0x7c, 0x7f, 0x7f, 1, 0x7c,
// type 2: (func (param i32 i32) (result f64))
0x60, 2, 0x7f, 0x7f, 1, 0x7c,
]);
const funcSection = makeSection(0x03, [2, 1, 2]);
const exportSection = makeSection(0x07, [
2,
...encodeString("newGet"), 0x00, 0x00,
...encodeString("defGet"), 0x00, 0x01,
]);
const func0Body = [
0,
0x20, 0x00, 0x20, 0x01, 0xfb, 0x06, 0x00,
0x20, 0x02, 0xfb, 0x0b, 0x00, 0x0b,
];
const func1Body = [
0,
0x20, 0x00, 0xfb, 0x07, 0x00,
0x20, 0x01, 0xfb, 0x0b, 0x00, 0x0b,
];
const codeSection = makeSection(0x0a, [
2,
...unsignedLEB128(func0Body.length), ...func0Body,
...unsignedLEB128(func1Body.length), ...func1Body,
]);
return [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
...typeSection, ...funcSection, ...exportSection, ...codeSection];
}
// ── Test 1: i32 array.new ───────────────────────────────────────────────────
{
const { newGet, defGet } = makeInstance(buildI32Module()).exports;
for (let i = 0; i < N; i++) {
const r = newGet(42, 4, 0);
if (r !== 42)
throw new Error(`i32 newGet(42,4,0): expected 42, got ${r}`);
}
// Check various indices
for (let idx = 0; idx < 4; idx++) {
const r = newGet(99, 4, idx);
if (r !== 99)
throw new Error(`i32 newGet(99,4,${idx}): expected 99, got ${r}`);
}
// Larger size
for (let i = 0; i < N; i++) {
const r = newGet(7, 10000, 9999);
if (r !== 7)
throw new Error(`i32 newGet(7,10000,9999): expected 7, got ${r}`);
}
// array.new_default
for (let i = 0; i < N; i++) {
const r = defGet(100, 50);
if (r !== 0)
throw new Error(`i32 defGet(100,50): expected 0, got ${r}`);
}
}
// ── Test 2: i64 array.new ───────────────────────────────────────────────────
{
const { newGet, defGet } = makeInstance(buildI64Module()).exports;
for (let i = 0; i < N; i++) {
const r = newGet(123n, 4, 0);
if (r !== 123n)
throw new Error(`i64 newGet(123n,4,0): expected 123n, got ${r}`);
}
for (let i = 0; i < N; i++) {
const r = newGet(0x1_0000_0000n, 100, 99);
if (r !== 0x1_0000_0000n)
throw new Error(`i64 newGet large: expected 0x100000000n, got ${r}`);
}
for (let i = 0; i < N; i++) {
const r = defGet(100, 50);
if (r !== 0n)
throw new Error(`i64 defGet(100,50): expected 0n, got ${r}`);
}
}
// ── Test 3: f32 array.new ───────────────────────────────────────────────────
{
const { newGet, defGet } = makeInstance(buildF32Module()).exports;
for (let i = 0; i < N; i++) {
const r = newGet(3.14, 4, 0);
if (Math.abs(r - 3.14) > 0.001)
throw new Error(`f32 newGet(3.14,4,0): expected ~3.14, got ${r}`);
}
for (let i = 0; i < N; i++) {
const r = defGet(100, 50);
if (r !== 0)
throw new Error(`f32 defGet(100,50): expected 0, got ${r}`);
}
}
// ── Test 4: f64 array.new ───────────────────────────────────────────────────
{
const { newGet, defGet } = makeInstance(buildF64Module()).exports;
for (let i = 0; i < N; i++) {
const r = newGet(2.718281828, 4, 0);
if (Math.abs(r - 2.718281828) > 1e-9)
throw new Error(`f64 newGet(2.718281828,4,0): expected ~2.718, got ${r}`);
}
for (let i = 0; i < N; i++) {
const r = newGet(1.5, 10000, 9999);
if (r !== 1.5)
throw new Error(`f64 newGet(1.5,10000,9999): expected 1.5, got ${r}`);
}
for (let i = 0; i < N; i++) {
const r = defGet(100, 50);
if (r !== 0)
throw new Error(`f64 defGet(100,50): expected 0, got ${r}`);
}
}