Source code
Revision control
Copy as Markdown
Other Tools
// |jit-test| skip-if: !wasmSimdEnabled()
// Lane-wise compares against an all-zero constant, with the constant on either
// side and alone or shared, on lanes holding zero, the extremes, and for floats
// -0, the infinities and NaN. Compares against other constants, on either side,
// must still match compares against the same value in a register.
const ints = [
["i8x16", 1, ["eq", "ne", "lt_s", "lt_u", "gt_s", "gt_u", "le_s", "le_u", "ge_s", "ge_u"]],
["i16x8", 2, ["eq", "ne", "lt_s", "lt_u", "gt_s", "gt_u", "le_s", "le_u", "ge_s", "ge_u"]],
["i32x4", 4, ["eq", "ne", "lt_s", "lt_u", "gt_s", "gt_u", "le_s", "le_u", "ge_s", "ge_u"]],
["i64x2", 8, ["eq", "ne", "lt_s", "gt_s", "le_s", "ge_s"]],
];
const floats = [
["f32x4", 4, ["eq", "ne", "lt", "gt", "le", "ge"]],
["f64x2", 8, ["eq", "ne", "lt", "gt", "le", "ge"]],
];
// Every value, split into vectors of 16 / size lanes.
function laneGroups(size, isFloat) {
const bits = BigInt(8 * size);
const values = isFloat
? [0, -0, 1, -1, Infinity, -Infinity, NaN, 2.5]
: [0n, 1n, -1n, (1n << (bits - 1n)) - 1n, -(1n << (bits - 1n)), 2n, -2n,
5n].map(v => BigInt.asUintN(8 * size, v));
const groups = [];
for (let i = 0; i < values.length; i += 16 / size) {
const group = values.slice(i, i + 16 / size);
while (group.length < 16 / size) {
group.push(isFloat ? 0 : 0n);
}
groups.push(group);
}
return groups;
}
function writeLanes(mem, offset, size, lanes, isFloat) {
const view = new DataView(mem.buffer);
lanes.forEach((v, i) => {
const at = offset + i * size;
if (isFloat) {
size == 4 ? view.setFloat32(at, v, true) : view.setFloat64(at, v, true);
} else {
for (let b = 0; b < size; b++) {
view.setUint8(at + b, Number((v >> BigInt(8 * b)) & 0xffn));
}
}
});
}
function readMask(mem, offset, size) {
const out = [];
for (let i = 0; i < 16 / size; i++) {
const lane = Array.from(mem.subarray(offset + i * size, offset + (i + 1) * size));
assertEq(lane.every(b => b == lane[0]) && (lane[0] == 0 || lane[0] == 0xff), true);
out.push(lane[0] == 0xff);
}
return out;
}
function intCompare(op, size, x) {
const s = BigInt.asIntN(8 * size, x);
switch (op) {
case "eq": return x == 0n;
case "ne": return x != 0n;
case "lt_s": return s < 0n;
case "gt_s": return s > 0n;
case "le_s": return s <= 0n;
case "ge_s": return s >= 0n;
case "lt_u": return false;
case "gt_u": return x != 0n;
case "le_u": return x == 0n;
case "ge_u": return true;
}
}
function floatCompare(op, x) {
switch (op) {
case "eq": return x == 0;
case "ne": return x != 0;
case "lt": return x < 0;
case "gt": return x > 0;
case "le": return x <= 0;
case "ge": return x >= 0;
}
}
// x OP y is y SWAPPED(OP) x.
const swapped = {
eq: "eq", ne: "ne",
lt_s: "gt_s", gt_s: "lt_s", le_s: "ge_s", ge_s: "le_s",
lt_u: "gt_u", gt_u: "lt_u", le_u: "ge_u", ge_u: "le_u",
lt: "gt", gt: "lt", le: "ge", ge: "le",
};
const zero = "(v128.const i64x2 0 0)";
const input = "(v128.load (i32.const 0))";
for (const [shape, size, ops] of ints.concat(floats)) {
const isFloat = shape[0] == "f";
for (const op of ops) {
const ins = wasmEvalText(`(module (memory (export "mem") 1)
(func (export "rhs")
(v128.store (i32.const 16) (${shape}.${op} ${input} ${zero})))
(func (export "lhs")
(v128.store (i32.const 16) (${shape}.${op} ${zero} ${input})))
(func (export "shared") (local v128)
(local.set 0 ${zero})
(v128.store (i32.const 16) (${shape}.${op} ${input} (local.get 0)))
(v128.store (i32.const 32) (local.get 0))))`);
const mem = new Uint8Array(ins.exports.mem.buffer);
for (const lanes of laneGroups(size, isFloat)) {
const compare = o => lanes.map(x => isFloat ? floatCompare(o, x) : intCompare(o, size, x));
const want = compare(op);
writeLanes(mem, 0, size, lanes, isFloat);
ins.exports.rhs();
assertSame(readMask(mem, 16, size), want);
ins.exports.lhs();
assertSame(readMask(mem, 16, size), compare(swapped[op]));
mem.fill(0xff, 32, 48);
ins.exports.shared();
assertSame(readMask(mem, 16, size), want);
assertSame(Array.from(mem.subarray(32, 48)), iota(16).map(() => 0));
}
}
}
for (const [shape, size, ops] of ints.concat(floats)) {
const isFloat = shape[0] == "f";
const constant = isFloat
? `(v128.const ${shape} ${Array(16 / size).fill("-1.5").join(" ")})`
: "(v128.const i32x4 0x80000001 0x7ffffffe 0 0xffffffff)";
for (const op of ops) {
const ins = wasmEvalText(`(module (memory (export "mem") 1)
(func (export "init") (v128.store (i32.const 48) ${constant}))
(func (export "constant")
(v128.store (i32.const 16) (${shape}.${op} ${input} ${constant})))
(func (export "register")
(v128.store (i32.const 32)
(${shape}.${op} ${input} (v128.load (i32.const 48)))))
(func (export "constantLhs")
(v128.store (i32.const 16) (${shape}.${op} ${constant} ${input})))
(func (export "registerLhs")
(v128.store (i32.const 32)
(${shape}.${op} (v128.load (i32.const 48)) ${input}))))`);
const mem = new Uint8Array(ins.exports.mem.buffer);
ins.exports.init();
for (const lanes of laneGroups(size, isFloat)) {
writeLanes(mem, 0, size, lanes, isFloat);
ins.exports.constant();
ins.exports.register();
assertSame(readMask(mem, 16, size), readMask(mem, 32, size));
ins.exports.constantLhs();
ins.exports.registerLhs();
assertSame(readMask(mem, 16, size), readMask(mem, 32, size));
}
}
}