|
| 1 | +// Experimental opcodes. We have no text parsing support for these yet. The |
| 2 | +// tests will be cleaned up and moved into ad-hack.js if the opcodes are |
| 3 | +// adopted. |
| 4 | + |
| 5 | +// When simd is enabled by default in release builds we will flip the value of |
| 6 | +// SimdExperimentalEnabled to false in RELEASE_OR_BETA builds. At that point, |
| 7 | +// these tests will start failing in release or beta builds, and a guard |
| 8 | +// asserting !RELEASE_OR_BETA will have to be added above. That is how it |
| 9 | +// should be. |
| 10 | + |
| 11 | +load(libdir + "wasm-binary.js"); |
| 12 | + |
| 13 | +function wasmEval(bytes, imports) { |
| 14 | + return new WebAssembly.Instance(new WebAssembly.Module(bytes), imports); |
| 15 | +} |
| 16 | + |
| 17 | +function get(arr, loc, len) { |
| 18 | + let res = []; |
| 19 | + for ( let i=0; i < len; i++ ) { |
| 20 | + res.push(arr[loc+i]); |
| 21 | + } |
| 22 | + return res; |
| 23 | +} |
| 24 | + |
| 25 | +function set(arr, loc, vals) { |
| 26 | + for ( let i=0; i < vals.length; i++ ) { |
| 27 | + if (arr instanceof BigInt64Array) { |
| 28 | + arr[loc+i] = BigInt(vals[i]); |
| 29 | + } else { |
| 30 | + arr[loc+i] = vals[i]; |
| 31 | + } |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +function assertSame(got, expected) { |
| 36 | + assertEq(got.length, expected.length); |
| 37 | + for ( let i=0; i < got.length; i++ ) { |
| 38 | + let g = got[i]; |
| 39 | + let e = expected[i]; |
| 40 | + if (typeof g != typeof e) { |
| 41 | + if (typeof g == "bigint") |
| 42 | + e = BigInt(e); |
| 43 | + else if (typeof e == "bigint") |
| 44 | + g = BigInt(g); |
| 45 | + } |
| 46 | + assertEq(g, e); |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +function iota(len) { |
| 51 | + let xs = []; |
| 52 | + for ( let i=0 ; i < len ; i++ ) |
| 53 | + xs.push(i); |
| 54 | + return xs; |
| 55 | +} |
| 56 | + |
| 57 | +function pmin(x, y) { return y < x ? y : x } |
| 58 | +function pmax(x, y) { return x < y ? y : x } |
| 59 | + |
| 60 | +function ffloor(x) { return Math.fround(Math.floor(x)) } |
| 61 | +function fceil(x) { return Math.fround(Math.ceil(x)) } |
| 62 | +function ftrunc(x) { return Math.fround(Math.sign(x)*Math.floor(Math.abs(x))) } |
| 63 | +function fnearest(x) { return Math.fround(Math.round(x)) } |
| 64 | + |
| 65 | +function dfloor(x) { return Math.floor(x) } |
| 66 | +function dceil(x) { return Math.ceil(x) } |
| 67 | +function dtrunc(x) { return Math.sign(x)*Math.floor(Math.abs(x)) } |
| 68 | +function dnearest(x) { return Math.round(x) } |
| 69 | + |
| 70 | +const v2vSig = {args:[], ret:VoidCode}; |
| 71 | + |
| 72 | +function V128Load(addr) { |
| 73 | + return [I32ConstCode, varS32(addr), |
| 74 | + SimdPrefix, V128LoadCode, 4, varU32(0)] |
| 75 | +} |
| 76 | + |
| 77 | +function V128StoreExpr(addr, v) { |
| 78 | + return [I32ConstCode, varS32(addr), |
| 79 | + ...v, |
| 80 | + SimdPrefix, V128StoreCode, 4, varU32(0)]; |
| 81 | +} |
| 82 | + |
| 83 | +// Pseudo-min/max, https://github.com/WebAssembly/simd/pull/122 |
| 84 | +var fxs = [5, 1, -4, 2]; |
| 85 | +var fys = [6, 0, -7, 3]; |
| 86 | +var dxs = [5, 1]; |
| 87 | +var dys = [6, 0]; |
| 88 | + |
| 89 | +for ( let [opcode, xs, ys, operator] of [[F32x4PMinCode, fxs, fys, pmin], |
| 90 | + [F32x4PMaxCode, fxs, fys, pmax], |
| 91 | + [F64x2PMinCode, dxs, dys, pmin], |
| 92 | + [F64x2PMaxCode, dxs, dys, pmax]] ) { |
| 93 | + var k = xs.length; |
| 94 | + var ans = iota(k).map((i) => operator(xs[i], ys[i])) |
| 95 | + |
| 96 | + var ins = wasmEval(moduleWithSections([ |
| 97 | + sigSection([v2vSig]), |
| 98 | + declSection([0]), |
| 99 | + memorySection(1), |
| 100 | + exportSection([{funcIndex: 0, name: "run"}, |
| 101 | + {memIndex: 0, name: "mem"}]), |
| 102 | + bodySection([ |
| 103 | + funcBody({locals:[], |
| 104 | + body: [...V128StoreExpr(0, [...V128Load(16), |
| 105 | + ...V128Load(32), |
| 106 | + SimdPrefix, varU32(opcode)])]})])])); |
| 107 | + |
| 108 | + var mem = new (k == 4 ? Float32Array : Float64Array)(ins.exports.mem.buffer); |
| 109 | + set(mem, k, xs); |
| 110 | + set(mem, 2*k, ys); |
| 111 | + ins.exports.run(); |
| 112 | + var result = get(mem, 0, k); |
| 113 | + assertSame(result, ans); |
| 114 | +} |
| 115 | + |
| 116 | +// Widening integer dot product, https://github.com/WebAssembly/simd/pull/127 |
| 117 | + |
| 118 | +var ins = wasmEval(moduleWithSections([ |
| 119 | + sigSection([v2vSig]), |
| 120 | + declSection([0]), |
| 121 | + memorySection(1), |
| 122 | + exportSection([{funcIndex: 0, name: "run"}, |
| 123 | + {memIndex: 0, name: "mem"}]), |
| 124 | + bodySection([ |
| 125 | + funcBody({locals:[], |
| 126 | + body: [...V128StoreExpr(0, [...V128Load(16), |
| 127 | + ...V128Load(32), |
| 128 | + SimdPrefix, varU32(I32x4DotSI16x8Code)])]})])])); |
| 129 | + |
| 130 | +var xs = [5, 1, -4, 2, 20, -15, 12, 3]; |
| 131 | +var ys = [6, 0, -7, 3, 8, -1, -3, 7]; |
| 132 | +var ans = [xs[0]*ys[0] + xs[1]*ys[1], |
| 133 | + xs[2]*ys[2] + xs[3]*ys[3], |
| 134 | + xs[4]*ys[4] + xs[5]*ys[5], |
| 135 | + xs[6]*ys[6] + xs[7]*ys[7]]; |
| 136 | + |
| 137 | +var mem16 = new Int16Array(ins.exports.mem.buffer); |
| 138 | +var mem32 = new Int32Array(ins.exports.mem.buffer); |
| 139 | +set(mem16, 8, xs); |
| 140 | +set(mem16, 16, ys); |
| 141 | +ins.exports.run(); |
| 142 | +var result = get(mem32, 0, 4); |
| 143 | +assertSame(result, ans); |
| 144 | + |
| 145 | +// Rounding, https://github.com/WebAssembly/simd/pull/232 |
| 146 | + |
| 147 | +var fxs = [5.1, -1.1, -4.3, 0]; |
| 148 | +var dxs = [5.1, -1.1]; |
| 149 | + |
| 150 | +for ( let [opcode, xs, operator] of [[F32x4CeilCode, fxs, fceil], |
| 151 | + [F32x4FloorCode, fxs, ffloor], |
| 152 | + [F32x4TruncCode, fxs, ftrunc], |
| 153 | + [F32x4NearestCode, fxs, fnearest], |
| 154 | + [F64x2CeilCode, dxs, dceil], |
| 155 | + [F64x2FloorCode, dxs, dfloor], |
| 156 | + [F64x2TruncCode, dxs, dtrunc], |
| 157 | + [F64x2NearestCode, dxs, dnearest]] ) { |
| 158 | + var k = xs.length; |
| 159 | + var ans = xs.map(operator); |
| 160 | + |
| 161 | + var ins = wasmEval(moduleWithSections([ |
| 162 | + sigSection([v2vSig]), |
| 163 | + declSection([0]), |
| 164 | + memorySection(1), |
| 165 | + exportSection([{funcIndex: 0, name: "run"}, |
| 166 | + {memIndex: 0, name: "mem"}]), |
| 167 | + bodySection([ |
| 168 | + funcBody({locals:[], |
| 169 | + body: [...V128StoreExpr(0, [...V128Load(16), |
| 170 | + SimdPrefix, varU32(opcode)])]})])])); |
| 171 | + |
| 172 | + var mem = new (k == 4 ? Float32Array : Float64Array)(ins.exports.mem.buffer); |
| 173 | + set(mem, k, xs); |
| 174 | + ins.exports.run(); |
| 175 | + var result = get(mem, 0, k); |
| 176 | + assertSame(result, ans); |
| 177 | +} |
| 178 | + |
| 179 | +// Zero-extending SIMD load, https://github.com/WebAssembly/simd/pull/237 |
| 180 | + |
| 181 | +for ( let [opcode, k, log2align, cons, cast] of [[V128Load32ZeroCode, 4, 2, Int32Array, Number], |
| 182 | + [V128Load64ZeroCode, 2, 3, BigInt64Array, BigInt]] ) { |
| 183 | + var ins = wasmEval(moduleWithSections([ |
| 184 | + sigSection([v2vSig]), |
| 185 | + declSection([0]), |
| 186 | + memorySection(1), |
| 187 | + exportSection([{funcIndex: 0, name: "run"}, |
| 188 | + {memIndex: 0, name: "mem"}]), |
| 189 | + bodySection([ |
| 190 | + funcBody({locals:[], |
| 191 | + body: [...V128StoreExpr(0, [I32ConstCode, varU32(16), |
| 192 | + SimdPrefix, varU32(opcode), log2align, varU32(0)])]})])])); |
| 193 | + |
| 194 | + var mem = new cons(ins.exports.mem.buffer); |
| 195 | + mem[k] = cast(37); |
| 196 | + ins.exports.run(); |
| 197 | + var result = get(mem, 0, k); |
| 198 | + assertSame(result, iota(k).map((v) => v == 0 ? 37 : 0)); |
| 199 | +} |
| 200 | + |
0 commit comments