relaxed-fma.js (16497B)
1 // |jit-test| --setpref=wasm_relaxed_simd=true; skip-if: !wasmRelaxedSimdEnabled() 2 3 // Experimental opcodes. We have no text parsing support for these yet. The 4 // tests will be cleaned up and moved into ad-hack.js if the opcodes are 5 // adopted. 6 7 load(libdir + "wasm-binary.js"); 8 9 function wasmEval(bytes, imports) { 10 return new WebAssembly.Instance(new WebAssembly.Module(bytes), imports); 11 } 12 13 function wasmValidateAndEval(bytes, imports) { 14 assertEq(WebAssembly.validate(bytes), true, "test of WasmValidate.cpp"); 15 return wasmEval(bytes, imports); 16 } 17 18 function get(arr, loc, len) { 19 let res = []; 20 for ( let i=0; i < len; i++ ) { 21 res.push(arr[loc+i]); 22 } 23 return res; 24 } 25 26 function set(arr, loc, vals) { 27 for ( let i=0; i < vals.length; i++ ) { 28 if (arr instanceof BigInt64Array) { 29 arr[loc+i] = BigInt(vals[i]); 30 } else { 31 arr[loc+i] = vals[i]; 32 } 33 } 34 } 35 36 const v2vSig = {args:[], ret:VoidCode}; 37 38 function V128Load(addr) { 39 return [I32ConstCode, varS32(addr), 40 SimdPrefix, V128LoadCode, 4, varU32(0)] 41 } 42 43 function V128StoreExpr(addr, v) { 44 return [I32ConstCode, varS32(addr), 45 ...v, 46 SimdPrefix, V128StoreCode, 4, varU32(0)]; 47 } 48 49 // FMA/FNMA, https://github.com/WebAssembly/relaxed-simd/issues/27 and 50 // https://github.com/WebAssembly/relaxed-simd/pull/81 51 52 function fma(x, y, a) { return (x * y) + a; } 53 function fnma(x, y, a) { return - (x * y) + a; } 54 55 var fxs = [10, 20, 30, 40]; 56 var fys = [-2, -3, -4, -5]; 57 var fas = [0, 100, 500, 700]; 58 var dxs = [10, 20]; 59 var dys = [-2, -3]; 60 var das = [0, 100]; 61 62 for ( let [opcode, xs, ys, as, operator] of [[F32x4RelaxedMaddCode, fxs, fys, fas, fma], 63 [F32x4RelaxedNmaddCode, fxs, fys, fas, fnma], 64 [F64x2RelaxedMaddCode, dxs, dys, das, fma], 65 [F64x2RelaxedNmaddCode, dxs, dys, das, fnma]] ) { 66 var k = xs.length; 67 var ans = iota(k).map((i) => operator(xs[i], ys[i], as[i])) 68 69 var ins = wasmValidateAndEval(moduleWithSections([ 70 sigSection([v2vSig]), 71 declSection([0]), 72 memorySection(1), 73 exportSection([{funcIndex: 0, name: "run"}, 74 {memIndex: 0, name: "mem"}]), 75 bodySection([ 76 funcBody({locals:[], 77 body: [...V128StoreExpr(0, [...V128Load(16), 78 ...V128Load(32), 79 ...V128Load(48), 80 SimdPrefix, ...varU32(opcode)])]})])])); 81 82 var mem = new (k == 4 ? Float32Array : Float64Array)(ins.exports.mem.buffer); 83 set(mem, k, xs); 84 set(mem, 2*k, ys); 85 set(mem, 3*k, as); 86 ins.exports.run(); 87 var result = get(mem, 0, k); 88 assertSame(result, ans); 89 90 assertEq(false, WebAssembly.validate(moduleWithSections([ 91 sigSection([v2vSig]), 92 declSection([0]), 93 memorySection(1), 94 exportSection([{funcIndex: 0, name: "run"}, 95 {memIndex: 0, name: "mem"}]), 96 bodySection([ 97 funcBody({locals:[], 98 body: [...V128StoreExpr(0, [...V128Load(0), 99 ...V128Load(0), 100 SimdPrefix, ...varU32(opcode)])]})])]))); 101 } 102 103 // Relaxed swizzle, https://github.com/WebAssembly/relaxed-simd/issues/22 104 105 var ins = wasmValidateAndEval(moduleWithSections([ 106 sigSection([v2vSig]), 107 declSection([0]), 108 memorySection(1), 109 exportSection([{funcIndex: 0, name: "run"}, 110 {memIndex: 0, name: "mem"}]), 111 bodySection([ 112 funcBody({locals:[], 113 body: [...V128StoreExpr(0, [...V128Load(16), 114 ...V128Load(32), 115 SimdPrefix, ...varU32(I8x16RelaxedSwizzleCode)])]})])])); 116 var mem = new Uint8Array(ins.exports.mem.buffer); 117 var test = [1, 4, 3, 7, 123, 0, 8, 222]; 118 set(mem, 16, test); 119 for (let [i, s] of [[0, 0], [0, 1], [1,1], [1, 3], [7,5]]) { 120 var ans = new Uint8Array(16); 121 for (let j = 0; j < 16; j++) { 122 mem[32 + j] = (j * s + i) & 15; 123 ans[j] = test[(j * s + i) & 15]; 124 } 125 ins.exports.run(); 126 var result = get(mem, 0, 16); 127 assertSame(result, ans); 128 } 129 130 assertEq(false, WebAssembly.validate(moduleWithSections([ 131 sigSection([v2vSig]), 132 declSection([0]), 133 memorySection(1), 134 bodySection([ 135 funcBody({locals:[], 136 body: [...V128StoreExpr(0, [...V128Load(16), 137 SimdPrefix, ...varU32(I8x16RelaxedSwizzleCode)])]})])]))); 138 139 140 // Relaxed MIN/MAX, https://github.com/WebAssembly/relaxed-simd/issues/33 141 142 const Neg0 = -1/Infinity; 143 var minMaxTests = [ 144 {a: 0, b: 0, min: 0, max: 0, }, 145 {a: Neg0, b: Neg0, min: Neg0, max: Neg0, }, 146 {a: 1/3, b: 2/3, min: 1/3, max: 2/3, }, 147 {a: -1/3, b: -2/3, min: -2/3, max: -1/3, }, 148 {a: -1000, b: 1, min: -1000, max: 1, }, 149 {a: 10, b: -2, min: -2, max: 10, }, 150 ]; 151 152 for (let k of [4, 2]) { 153 const minOpcode = k == 4 ? F32x4RelaxedMinCode : F64x2RelaxedMinCode; 154 const maxOpcode = k == 4 ? F32x4RelaxedMaxCode : F64x2RelaxedMaxCode; 155 156 var ins = wasmValidateAndEval(moduleWithSections([ 157 sigSection([v2vSig]), 158 declSection([0, 0]), 159 memorySection(1), 160 exportSection([{funcIndex: 0, name: "min"}, 161 {funcIndex: 1, name: "max"}, 162 {memIndex: 0, name: "mem"}]), 163 bodySection([ 164 funcBody({locals:[], 165 body: [...V128StoreExpr(0, [...V128Load(16), 166 ...V128Load(32), 167 SimdPrefix, ...varU32(minOpcode)])]}), 168 funcBody({locals:[], 169 body: [...V128StoreExpr(0, [...V128Load(16), 170 ...V128Load(32), 171 SimdPrefix, ...varU32(maxOpcode)])]})])])); 172 for (let i = 0; i < minMaxTests.length; i++) { 173 var Ty = k == 4 ? Float32Array : Float64Array; 174 var mem = new Ty(ins.exports.mem.buffer); 175 var minResult = new Ty(k); 176 var maxResult = new Ty(k); 177 for (let j = 0; j < k; j++) { 178 const {a, b, min, max } = minMaxTests[(j + i) % minMaxTests.length]; 179 mem[j + k] = a; 180 mem[j + k * 2] = b; 181 minResult[j] = min; 182 maxResult[j] = max; 183 } 184 ins.exports.min(); 185 var result = get(mem, 0, k); 186 assertSame(result, minResult); 187 ins.exports.max(); 188 var result = get(mem, 0, k); 189 assertSame(result, maxResult); 190 } 191 192 for (let op of [minOpcode, maxOpcode]) { 193 assertEq(false, WebAssembly.validate(moduleWithSections([ 194 sigSection([v2vSig]), 195 declSection([0, 0]), 196 memorySection(1), 197 exportSection([]), 198 bodySection([ 199 funcBody({locals:[], 200 body: [...V128StoreExpr(0, [...V128Load(0), 201 SimdPrefix, ...varU32(op)])]})])]))); 202 } 203 } 204 205 // Relaxed I32x4.TruncFXXX, https://github.com/WebAssembly/relaxed-simd/issues/21 206 207 var ins = wasmValidateAndEval(moduleWithSections([ 208 sigSection([v2vSig]), 209 declSection([0, 0, 0, 0]), 210 memorySection(1), 211 exportSection([{funcIndex: 0, name: "from32s"}, 212 {funcIndex: 1, name: "from32u"}, 213 {funcIndex: 2, name: "from64s"}, 214 {funcIndex: 3, name: "from64u"}, 215 {memIndex: 0, name: "mem"}]), 216 bodySection([ 217 funcBody({locals:[], 218 body: [...V128StoreExpr(0, [...V128Load(16), 219 SimdPrefix, ...varU32(I32x4RelaxedTruncSSatF32x4Code)])]}), 220 funcBody({locals:[], 221 body: [...V128StoreExpr(0, [...V128Load(16), 222 SimdPrefix, ...varU32(I32x4RelaxedTruncUSatF32x4Code)])]}), 223 funcBody({locals:[], 224 body: [...V128StoreExpr(0, [...V128Load(16), 225 SimdPrefix, ...varU32(I32x4RelaxedTruncSatF64x2SZeroCode)])]}), 226 funcBody({locals:[], 227 body: [...V128StoreExpr(0, [...V128Load(16), 228 SimdPrefix, ...varU32(I32x4RelaxedTruncSatF64x2UZeroCode)])]})])])); 229 230 var mem = ins.exports.mem.buffer; 231 set(new Float32Array(mem), 4, [0, 2.3, -3.4, 100000]); 232 ins.exports.from32s(); 233 var result = get(new Int32Array(mem), 0, 4); 234 assertSame(result, [0, 2, -3, 100000]); 235 236 set(new Float32Array(mem), 4, [0, 3.3, 0x80000000, 200000]); 237 ins.exports.from32u(); 238 var result = get(new Uint32Array(mem), 0, 4); 239 assertSame(result, [0, 3, 0x80000000, 200000]); 240 set(new Float32Array(mem), 4, [0, 0x80000100, 0x80000101, 0xFFFFFF00]); 241 ins.exports.from32u(); 242 var result = get(new Uint32Array(mem), 0, 4); 243 assertSame(result, [0, 0x80000100, 0x80000100, 0xFFFFFF00]); 244 245 set(new Float64Array(mem), 2, [200000.3, -3.4]); 246 ins.exports.from64s(); 247 var result = get(new Int32Array(mem), 0, 4); 248 assertSame(result, [200000, -3, 0, 0]); 249 set(new Float64Array(mem), 2, [0x90000000 + 0.1, 0]); 250 ins.exports.from64u(); 251 var result = get(new Uint32Array(mem), 0, 4); 252 assertSame(result, [0x90000000, 0, 0, 0]); 253 254 for (let op of [I32x4RelaxedTruncSSatF32x4Code, I32x4RelaxedTruncUSatF32x4Code, 255 I32x4RelaxedTruncSatF64x2SZeroCode, I32x4RelaxedTruncSatF64x2UZeroCode]) { 256 assertEq(false, WebAssembly.validate(moduleWithSections([ 257 sigSection([v2vSig]), 258 declSection([0]), 259 memorySection(1), 260 exportSection([]), 261 bodySection([ 262 funcBody({locals:[], 263 body: [...V128StoreExpr(0, [SimdPrefix, ...varU32(op)])]})])]))); 264 } 265 266 // Relaxed blend / laneselect, https://github.com/WebAssembly/relaxed-simd/issues/17 267 268 for (let [k, opcode, AT] of [[1, I8x16RelaxedLaneSelectCode, Int8Array], 269 [2, I16x8RelaxedLaneSelectCode, Int16Array], 270 [4, I32x4RelaxedLaneSelectCode, Int32Array], 271 [8, I64x2RelaxedLaneSelectCode, BigInt64Array]]) { 272 273 var ins = wasmValidateAndEval(moduleWithSections([ 274 sigSection([v2vSig]), 275 declSection([0]), 276 memorySection(1), 277 exportSection([{funcIndex: 0, name: "run"}, 278 {memIndex: 0, name: "mem"}]), 279 bodySection([ 280 funcBody({locals:[], 281 body: [...V128StoreExpr(0, [...V128Load(16), 282 ...V128Load(32), 283 ...V128Load(48), 284 SimdPrefix, ...varU32(opcode)])]})])])); 285 286 var mem = ins.exports.mem.buffer; 287 var mem8 = new Uint8Array(mem); 288 set(mem8, 16, [1,2,3,4,0,0,0,0,100,0,102,0,0,250,251,252,253]); 289 set(mem8, 32, [0,0,0,0,5,6,7,8,0,101,0,103,0,254,255,0,1]); 290 var c = new AT(mem, 48, 16 / k); 291 for (let i = 0; i < c.length; i++) { 292 // Use popcnt to randomize 0 and ~0 293 const popcnt_i = i.toString(2).replace(/0/g, "").length; 294 const v = popcnt_i & 1 ? -1 : 0 295 c[i] = k == 8 ? BigInt(v) : v; 296 } 297 ins.exports.run(); 298 for (let i = 0; i < 16; i++) { 299 const r = c[(i / k) | 0] ? mem8[16 + i] : mem8[32 + i]; 300 assertEq(r, mem8[i]); 301 } 302 303 assertEq(false, WebAssembly.validate(moduleWithSections([ 304 sigSection([v2vSig]), 305 declSection([0]), 306 memorySection(1), 307 exportSection([{funcIndex: 0, name: "run"}, 308 {memIndex: 0, name: "mem"}]), 309 bodySection([ 310 funcBody({locals:[], 311 body: [...V128StoreExpr(0, [...V128Load(0), 312 ...V128Load(0), 313 SimdPrefix, ...varU32(opcode)])]})])]))); 314 } 315 316 317 // Relaxed rounding q-format multiplication. 318 var ins = wasmValidateAndEval(moduleWithSections([ 319 sigSection([v2vSig]), 320 declSection([0]), 321 memorySection(1), 322 exportSection([{funcIndex: 0, name: "relaxed_q15mulr_s"}, 323 {memIndex: 0, name: "mem"}]), 324 bodySection([ 325 funcBody({locals:[], 326 body: [...V128StoreExpr(0, [...V128Load(16), 327 ...V128Load(32), 328 SimdPrefix, ...varU32(I16x8RelaxedQ15MulrSCode)])]})])])); 329 330 var mem16 = new Int16Array(ins.exports.mem.buffer); 331 for (let [as, bs] of cross([ 332 [1, -3, 5, -7, 11, -13, -17, 19], 333 [-1, 0, 16, -32, 64, 128, -1024, 0, 1], 334 [1,2,-32768,32767,1,4,-32768,32767]]) ) { 335 set(mem16, 8, as); 336 set(mem16, 16, bs); 337 ins.exports.relaxed_q15mulr_s(); 338 const result = get(mem16, 0, 8); 339 for (let i = 0; i < 8; i++) { 340 const expected = (as[i] * bs[i] + 0x4000) >> 15; 341 if (as[i] == -32768 && bs[i] == -32768) continue; 342 assertEq(expected, result[i], `result of ${as[i]} * ${bs[i]}`); 343 } 344 } 345 346 347 // Check relaxed dot product results. 348 var ins = wasmValidateAndEval(moduleWithSections([ 349 sigSection([v2vSig]), 350 declSection([0]), 351 memorySection(1), 352 exportSection([{funcIndex: 0, name: "dot_i8x16_i7x16_s"}, 353 {memIndex: 0, name: "mem"}]), 354 bodySection([ 355 funcBody({locals:[], 356 body: [...V128StoreExpr(0, [...V128Load(16), 357 ...V128Load(32), 358 SimdPrefix, ...varU32(I16x8RelaxedDotI8x16I7x16SCode)])]})])])); 359 var mem8 = new Int8Array(ins.exports.mem.buffer); 360 var mem16 = new Int16Array(ins.exports.mem.buffer); 361 var test7bit = [1, 2, 3, 4, 5, 64, 65, 127, 127, 0, 0, 362 1, 65, 64, 2, 3, 0, 0, 127, 127, 5, 4]; 363 var testNeg = test7bit.concat(test7bit.map(i => ~i)); 364 for (let ai = 0; ai < testNeg.length - 15; ai++) 365 for (let bi = 0; bi < test7bit.length - 15; bi++) { 366 set(mem8, 16, testNeg.slice(ai, ai + 16)); 367 set(mem8, 32, test7bit.slice(bi, bi + 16)); 368 ins.exports.dot_i8x16_i7x16_s(); 369 const result = get(mem16, 0, 8); 370 for (let i = 0; i < 8; i++) { 371 const expected = ((testNeg[ai + i * 2] * test7bit[bi + i * 2]) + 372 (testNeg[ai + i * 2 + 1] * test7bit[bi + i * 2 + 1])) | 0; 373 assertEq(expected, result[i]); 374 } 375 } 376 377 var ins = wasmValidateAndEval(moduleWithSections([ 378 sigSection([v2vSig]), 379 declSection([0]), 380 memorySection(1), 381 exportSection([{funcIndex: 0, name: "dot_i8x16_i7x16_add_s"}, 382 {memIndex: 0, name: "mem"}]), 383 bodySection([ 384 funcBody({locals:[], 385 body: [...V128StoreExpr(0, [...V128Load(16), 386 ...V128Load(32), 387 ...V128Load(48), 388 SimdPrefix, ...varU32(I32x4RelaxedDotI8x16I7x16AddSCode)])]})])])); 389 var mem8 = new Int8Array(ins.exports.mem.buffer); 390 var mem32 = new Int32Array(ins.exports.mem.buffer); 391 var test7bit = [1, 2, 3, 4, 5, 64, 65, 127, 127, 0, 0, 392 1, 65, 64, 2, 3, 0, 0, 127, 127, 5, 4]; 393 var testNeg = test7bit.concat(test7bit.map(i => ~i)); 394 var testAcc = [0, 12, 65336, -1, 0x10000000, -0xffffff]; 395 for (let ai = 0; ai < testNeg.length - 15; ai++) 396 for (let bi = 0; bi < test7bit.length - 15; bi++) 397 for (let ci = 0; ci < testAcc.length - 3; ci++) { 398 set(mem8, 16, testNeg.slice(ai, ai + 16)); 399 set(mem8, 32, test7bit.slice(bi, bi + 16)); 400 set(mem32, 48/4, testAcc.slice(ci, ci + 4)); 401 ins.exports.dot_i8x16_i7x16_add_s(); 402 const result = get(mem32, 0, 4); 403 for (let i = 0; i < 4; i++) { 404 const a1 = (testNeg[ai + i * 4] * test7bit[bi + i * 4]) + 405 (testNeg[ai + i * 4 + 1] * test7bit[bi + i * 4 + 1]); 406 const a2 = (testNeg[ai + i * 4 + 2] * test7bit[bi + i * 4 + 2]) + 407 (testNeg[ai + i * 4 + 3] * test7bit[bi + i * 4 + 3]); 408 const expected = (testAcc[ci + i] + a1 + a2) | 0; 409 assertEq(expected, result[i]); 410 } 411 }