Source code

Revision control

Copy as Markdown

Other Tools

// |jit-test| --setpref=wasm_wide_arithmetic=true
let t = `
(module
(func (export "doAddI128")
;; (lhsLo lhsHi rhsLo rhsHi) -> (resultLo resultHi)
(param i64 i64 i64 i64) (result i64 i64)
local.get 0
local.get 1
local.get 2
local.get 3
i64.add128
)
(func (export "doSubI128")
;; (lhsLo lhsHi rhsLo rhsHi) -> (resultLo resultHi)
(param i64 i64 i64 i64) (result i64 i64)
local.get 0
local.get 1
local.get 2
local.get 3
i64.sub128
)
(func (export "doMulI64WideU")
;; (lhs rhs) -> (resultLo resultHi)
(param i64 i64) (result i64 i64)
local.get 0
local.get 1
i64.mul_wide_u
)
(func (export "doMulI64WideS")
;; (lhs rhs) -> (resultLo resultHi)
(param i64 i64) (result i64 i64)
local.get 0
local.get 1
i64.mul_wide_s
)
)`;
let i = wasmEvalText(t);
////////////////////////////////////////////////////////////////////////
// Some simple smoke tests
// 2^63
let x80 = 0x8000000000000000n;
// "normalize" `x` so it falls into uint64_t range (a lame kludge)
function normalize(x) {
if (x < 0n) return x + x80 + x80;
return x;
}
function tryAddI128(a, b, c, d, expected) {
assertEq(expected.length, 2);
let actual = i.exports.doAddI128(a, b, c, d);
assertEq(actual.length, 2);
assertEq(normalize(actual[0]), normalize(expected[0]));
assertEq(normalize(actual[1]), normalize(expected[1]));
}
function trySubI128(a, b, c, d, expected) {
assertEq(expected.length, 2);
let actual = i.exports.doSubI128(a, b, c, d);
assertEq(actual.length, 2);
assertEq(normalize(actual[0]), normalize(expected[0]));
assertEq(normalize(actual[1]), normalize(expected[1]));
}
function tryMulI64WideU(a, b, expected) {
assertEq(expected.length, 2);
let actual = i.exports.doMulI64WideU(a, b);
assertEq(actual.length, 2);
assertEq(normalize(actual[0]), normalize(expected[0]));
assertEq(normalize(actual[1]), normalize(expected[1]));
}
function tryMulI64WideS(a, b, expected) {
assertEq(expected.length, 2);
let actual = i.exports.doMulI64WideS(a, b);
assertEq(actual.length, 2);
assertEq(normalize(actual[0]), normalize(expected[0]));
assertEq(normalize(actual[1]), normalize(expected[1]));
}
// Addition
// format is: --lhs-- --rhs-- result
// lo, hi, lo, hi, [lo, hi]
tryAddI128( 2n, 1n, 4n, 3n, [6n, 4n]); // no carry at midpoint
tryAddI128( x80, 22n, x80, 33n, [0n, 56n]); // carry at midpoint
// carry out of neither
tryAddI128(0x02a74ec1865d36e3n, 0x30fe727e0ffbaf31n,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[0xb6d9c1f89a249549n, 0x7679934c4d6c1dd4n]);
// carry out of lo
tryAddI128(0xcd0e81861923459bn, 0x30fe727e0ffbaf31n,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[0x8140f4bd2ceaa401n, 0x7679934c4d6c1dd5n]);
// carry out of hi
tryAddI128(0x02a74ec1865d36e3n, 0xd74276b90c9d680bn,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[0xb6d9c1f89a249549n, 0x1cbd97874a0dd6aen]);
// carry out of both
tryAddI128(0xcd0e81861923459bn, 0xd74276b90c9d680bn,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[0x8140f4bd2ceaa401n, 0x1cbd97874a0dd6afn]);
// Subtraction
// format is: --lhs-- --rhs-- result
// lo, hi, lo, hi, [lo, hi]
trySubI128(88n, 9999n, 66n, 4444n, [22n, 5555n]); // no borrow at midpoint
trySubI128(88n, 9999n, 99n, 4444n, [-11n, 5554n]); // borrow at midpoint
trySubI128(0x02a74ec1865d36e3n, 0x30fe727e0ffbaf31n,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[5653384819931207805n, -1476246437253922675n]);
trySubI128(0xcd0e81861923459bn, 0x30fe727e0ffbaf31n,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[1791322484341729077n, -1476246437253922674n]);
trySubI128(0x02a74ec1865d36e3n, 0xd74276b90c9d680bn,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[5653384819931207805n, -7942284950858040985n]);
trySubI128(0xcd0e81861923459bn, 0xd74276b90c9d680bn,
0xb432733713c75e66n, 0x457b20ce3d706ea3n,
[1791322484341729077n, -7942284950858040984n]);
// Widening multiplies
// format is: -result-
// operand operand [hi, lo]
// Signed and unsigned produce the same result.
tryMulI64WideU(123n, 456n, [56088n, 0n]);
tryMulI64WideS(123n, 456n, [56088n, 0n]);
// Signed and unsigned produce the same result; but bits[127:64] are now used
tryMulI64WideU(123999888777n, 456777666555n, [8875542349269292115n, 3070n]);
tryMulI64WideS(123999888777n, 456777666555n, [8875542349269292115n, 3070n]);
// Signed and unsigned produce different results
tryMulI64WideU(0x8123ffffeeeeddddn, 456777666555n,
[-6687103016203617617n, 230424036496n]);
tryMulI64WideS(0x8123ffffeeeeddddn, 456777666555n,
[11759641057505933999n, 18446743847355921557n]);
// As above pair, but with operands swapped. Results should be the same
// as above.
tryMulI64WideU(456777666555n, 0x8123ffffeeeeddddn,
[-6687103016203617617n, 230424036496n]);
tryMulI64WideS(456777666555n, 0x8123ffffeeeeddddn,
[11759641057505933999n, 18446743847355921557n]);
////////////////////////////////////////////////////////////////////////
// Testcases from
// f292896bef63408a88d1dc4e56b2ac1cb8c530ab
// simple addition
tryAddI128(0n, 0n, 0n, 0n, [0n, 0n]);
tryAddI128(0n, 1n, 1n, 0n, [1n, 1n]);
tryAddI128(1n, 0n, -1n, 0n, [0n, 1n]);
tryAddI128(1n, 1n, -1n, -1n, [0n, 1n]);
// simple subtraction
trySubI128(0n, 0n, 0n, 0n, [0n, 0n]);
trySubI128(0n, 0n, 1n, 0n, [-1n, -1n]);
trySubI128(0n, 1n, 1n, 1n, [-1n, -1n]);
trySubI128(0n, 0n, 1n, 1n, [-1n, -2n]);
// simple mul_wide
tryMulI64WideS(0n, 0n, [0n, 0n]);
tryMulI64WideU(0n, 0n, [0n, 0n]);
tryMulI64WideS(1n, 1n, [1n, 0n]);
tryMulI64WideU(1n, 1n, [1n, 0n]);
tryMulI64WideS(-1n, -1n, [1n, 0n]);
tryMulI64WideS(-1n, 1n, [-1n, -1n]);
tryMulI64WideU(-1n, 1n, [-1n, 0n]);
// 20 randomly generated test cases for i64.add128
tryAddI128(-2418420703207364752n, -1n,
-1n, -1n,
[-2418420703207364753n, -1n]);
tryAddI128(0n, 0n,
-4579433644172935106n, -1n,
[-4579433644172935106n, -1n]);
tryAddI128(0n, 0n,
1n, -1n,
[1n, -1n]);
tryAddI128(1n, 0n,
1n, 0n,
[2n, 0n]);
tryAddI128(-1n, -1n,
-1n, -1n,
[-2n, -1n]);
tryAddI128(0n, -1n,
1n, 0n,
[1n, -1n]);
tryAddI128(0n, 0n,
0n, -1n,
[0n, -1n]);
tryAddI128(1n, 0n,
-1n, -1n,
[0n, 0n]);
tryAddI128(0n, 6184727276166606191n,
0n, 1n,
[0n, 6184727276166606192n]);
tryAddI128(-8434911321912688222n, -1n,
1n, -1n,
[-8434911321912688221n, -2n]);
tryAddI128(1n, -1n,
0n, -1n,
[1n, -2n]);
tryAddI128(1n, -5148941131328838092n,
0n, 0n,
[1n, -5148941131328838092n]);
tryAddI128(1n, 1n,
1n, 0n,
[2n, 1n]);
tryAddI128(-1n, -1n,
-3636740005180858631n, -1n,
[-3636740005180858632n, -1n]);
tryAddI128(-5529682780229988275n, -1n,
0n, 0n,
[-5529682780229988275n, -1n]);
tryAddI128(1n, -5381447440966559717n,
1020031372481336745n, 1n,
[1020031372481336746n, -5381447440966559716n]);
tryAddI128(1n, 1n,
0n, 0n,
[1n, 1n]);
tryAddI128(-9133888546939907356n, -1n,
1n, 1n,
[-9133888546939907355n, 0n]);
tryAddI128(-4612047512704241719n, -1n,
0n, -1n,
[-4612047512704241719n, -2n]);
tryAddI128(414720966820876428n, -1n,
1n, 0n,
[414720966820876429n, -1n]);
// 20 randomly generated test cases for i64.sub128
trySubI128(0n, -2459085471354756766n,
-9151153060221070927n, -1n,
[9151153060221070927n, -2459085471354756766n]);
trySubI128(4566502638724063423n, -4282658540409485563n,
-6884077310018979971n, -1n,
[-6996164124966508222n, -4282658540409485563n]);
trySubI128(1n, 3118380319444903041n,
0n, 3283115686417695443n,
[1n, -164735366972792402n]);
trySubI128(-7208415241680161810n, -1n,
1n, 0n,
[-7208415241680161811n, -1n]);
trySubI128(0n, 3944850126731328706n,
1n, 1n,
[-1n, 3944850126731328704n]);
trySubI128(1n, -1n,
-1n, -1n,
[2n, -1n]);
trySubI128(-1n, -1n,
4855833073346115923n, -6826437637438999645n,
[-4855833073346115924n, 6826437637438999644n]);
trySubI128(1n, 0n,
-1n, -1n,
[2n, 0n]);
trySubI128(1n, 0n,
1n, 0n,
[0n, 0n]);
trySubI128(-1n, -1n,
0n, 0n,
[-1n, -1n]);
trySubI128(1n, -1n,
-6365475388498096428n, -1n,
[6365475388498096429n, -1n]);
trySubI128(6804238617560992346n, -1n,
0n, -1n,
[6804238617560992346n, 0n]);
trySubI128(0n, 1n,
1n, -7756145513466453619n,
[-1n, 7756145513466453619n]);
trySubI128(1n, -1n,
1n, 1n,
[0n, -2n]);
trySubI128(0n, 1n,
1n, 0n,
[-1n, 0n]);
trySubI128(1n, 5602881641763648953n,
-2110589244314239080n, -1n,
[2110589244314239081n, 5602881641763648953n]);
trySubI128(0n, 1n,
-1n, -1n,
[1n, 1n]);
trySubI128(0n, -1n,
3553816990259121806n, -2105235417856431622n,
[-3553816990259121806n, 2105235417856431620n]);
trySubI128(1861102705894987245n, 1n,
3713781778534059871n, 1n,
[-1852679072639072626n, -1n]);
trySubI128(0n, -1n,
1n, 1832524486821761762n,
[-1n, -1832524486821761764n]);
// 20 randomly generated test cases for i64.mul_wide_s
tryMulI64WideS(1n, 1n,
[1n, 0n]);
tryMulI64WideS(0n, 6287758211025156705n,
[0n, 0n]);
tryMulI64WideS(-6643537319803451357n, 1n,
[-6643537319803451357n, -1n]);
tryMulI64WideS(-2483565146858803428n, 0n,
[0n, 0n]);
tryMulI64WideS(1n, 1n,
[1n, 0n]);
tryMulI64WideS(-3838951433439430085n, 3471602925362676030n,
[5186941893001237834n, -722475195264825124n]);
tryMulI64WideS(-8262495286814853129n, 7883241869666573970n,
[-8557189786755031842n, -3530988912334554469n]);
tryMulI64WideS(4278371902407959701n, 1n,
[4278371902407959701n, 0n]);
tryMulI64WideS(-8852706149487089182n, -1n,
[8852706149487089182n, 0n]);
tryMulI64WideS(1n, -1n,
[-1n, -1n]);
tryMulI64WideS(-1n, -4329244561838653387n,
[4329244561838653387n, 0n]);
tryMulI64WideS(-1n, -1n,
[1n, 0n]);
tryMulI64WideS(697896157315764057n, 1n,
[697896157315764057n, 0n]);
tryMulI64WideS(1n, 1n,
[1n, 0n]);
tryMulI64WideS(-1n, 0n,
[0n, 0n]);
tryMulI64WideS(0n, -3769664482072947073n,
[0n, 0n]);
tryMulI64WideS(1n, 8414291037346403854n,
[8414291037346403854n, 0n]);
tryMulI64WideS(1n, -1n,
[-1n, -1n]);
tryMulI64WideS(5014655679779318485n, -5080037812563681985n,
[2842857627777395563n, -1380983027057486843n]);
tryMulI64WideS(0n, 1n,
[0n, 0n]);
// 20 randomly generated test cases for i64.mul_wide_u
tryMulI64WideU(-4734436040338162711n, 0n,
[0n, 0n]);
tryMulI64WideU(1n, 0n,
[0n, 0n]);
tryMulI64WideU(3270597527173764279n, 6636648075495406358n,
[-5430303818902260550n, 1176674035141685826n]);
tryMulI64WideU(-7771814344630108151n, 1n,
[-7771814344630108151n, 0n]);
tryMulI64WideU(1n, 0n,
[0n, 0n]);
tryMulI64WideU(1n, -7864138787704962081n,
[-7864138787704962081n, 0n]);
tryMulI64WideU(1n, 518555141550256010n,
[518555141550256010n, 0n]);
tryMulI64WideU(1n, -1n,
[-1n, 0n]);
tryMulI64WideU(1118900477321231571n, -1n,
[-1118900477321231571n, 1118900477321231570n]);
tryMulI64WideU(-1n, 0n,
[0n, 0n]);
tryMulI64WideU(-5586890671027490027n, 1n,
[-5586890671027490027n, 0n]);
tryMulI64WideU(0n, 3603850799751152505n,
[0n, 0n]);
tryMulI64WideU(-1n, -1n,
[1n, 18446744073709551614n]);
tryMulI64WideU(0n, 1n,
[0n, 0n]);
tryMulI64WideU(-7344082851774441644n, 3896439839137544024n,
[5738542512914895072n, 2345175459296971666n]);
tryMulI64WideU(0n, 0n,
[0n, 0n]);
tryMulI64WideU(616395976148874061n, 0n,
[0n, 0n]);
tryMulI64WideU(2810729703362889816n, -1n,
[-2810729703362889816n, 2810729703362889815n]);
tryMulI64WideU(1n, -1n,
[-1n, 0n]);
tryMulI64WideU(1n, 0n,
[0n, 0n]);
// assert overlong encodings for each instruction's binary encoding are
// accepted
let wasmBytes = new Uint8Array([
0x00,0x61,0x73,0x6d, 0x01,0x00,0x00,0x00,
0x01,0x11, // type section, 17 bytes
0x02, // 2 count
0x60, // type0 = function
0x04,0x7e,0x7e,0x7e,0x7e, // 4 params - all i64
0x02,0x7e,0x7e, // 2 results - both i64
0x60, // type1 = function
0x02,0x7e,0x7e, // 2 params - both i64
0x02,0x7e,0x7e, // 2 results - both i64
0x03,0x05, // function section, 5 byte
0x04, // 4 count
0x00,0x00,0x01,0x01, // types of each function (0, 0, 1, 1)
0x07,0x3d, // export section 0x3d bytes
0x04, // 4 count
0x0a,0x69,0x36,0x34,0x5f,
0x61,0x64,0x64,0x31,0x32,0x38,0x00,0x00, // i64_add128 which is function 0
0x0a,0x69,0x36,0x34,0x5f,
0x73,0x75,0x62,0x31,0x32,0x38,0x00,0x01, // i64_add128 which is function 1
0x0e,0x69,0x36,0x34,0x5f,
0x6d,0x75,0x6c,0x5f,0x77,0x69,
0x64,0x65,0x5f,0x73,0x00,0x02, // i64_mul_wide_s which is function 2
0x0e,0x69,0x36,0x34,0x5f,
0x6d,0x75,0x6c,0x5f,0x77,0x69,
0x64,0x65,0x5f,0x75,0x00,0x03, // i64_mul_wide_u which is function 3
0x0a,0x37, // code section + byte length
0x04, // 4 count
0x0e, // byte length
0x00, // no locals
0x20,0x00, // local.get 0
0x20,0x01, // local.get 1
0x20,0x02, // local.get 2
0x20,0x03, // local.get 3
0xfc,0x93,0x80,0x00, // i64.add128 (overlong)
0x0b, // end
0x0d, // byte length
0x00, // no locals
0x20,0x00, // local.get 0
0x20,0x01, // local.get 1
0x20,0x02, // local.get 2
0x20,0x03, // local.get 3
0xfc,0x94,0x00, // i64.sub128 (overlong)
0x0b, // end
0x0c, // byte length
0x00, // no locals
0x20,0x00, // local.get 0
0x20,0x01, // local.get 1
0xfc,0x95,0x80,0x80,0x80,0x00, // i64.mul_wide_s (overlong)
0x0b, // end
0x0b, // byte length
0x00, // no locals
0x20,0x00, // local.get 0
0x20,0x01, // local.get 1
0xfc,0x96,0x80,0x80,0x00, // i64.mul_wide_u (overlong)
0x0b // end
]);
let wasmModule = new WebAssembly.Module(wasmBytes);
let wasmInstance = new WebAssembly.Instance(wasmModule, {});
let res = wasmInstance.exports.i64_add128(1n, 2n, 3n, 4n);
assertEq(res[0], 4n);
assertEq(res[1], 6n);
res = wasmInstance.exports.i64_sub128(2n, 5n, 1n, 2n);
assertEq(res[0], 1n);
assertEq(res[1], 3n);
res = wasmInstance.exports.i64_mul_wide_s(1n, -2n);
assertEq(res[0], -2n);
assertEq(res[1], -1n);
res = wasmInstance.exports.i64_mul_wide_u(3n, 2n);
assertEq(res[0], 6n);
assertEq(res[1], 0n);
// some invalid types for these instructions
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64 i64 i64 i64) (result i64)
local.get 0
local.get 1
local.get 2
local.get 3
i64.add128)
)`), Error, /unused values not explicitly dropped by end of block/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64 i64 i64) (result i64 i64)
local.get 0
local.get 1
local.get 2
i64.add128)
)`), Error, /popping value from empty stack/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64 i64 i64 i64) (result i64)
local.get 0
local.get 1
local.get 2
local.get 3
i64.sub128)
)`), Error, /unused values not explicitly dropped by end of block/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64 i64 i64) (result i64 i64)
local.get 0
local.get 1
local.get 2
i64.sub128)
)`), Error, /popping value from empty stack/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64 i64) (result i64)
local.get 0
local.get 1
i64.mul_wide_s)
)`), Error, /unused values not explicitly dropped by end of block/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64) (result i64 i64)
local.get 0
i64.mul_wide_s)
)`), Error, /popping value from empty stack/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64 i64) (result i64)
local.get 0
local.get 1
i64.mul_wide_u)
)`), Error, /unused values not explicitly dropped by end of block/);
assertErrorMessage(() => wasmEvalText(`
(module
(func (param i64) (result i64 i64)
local.get 0
i64.mul_wide_u)
)`), Error, /popping value from empty stack/);