wa-lang.org/wazero@v1.0.2/internal/engine/compiler/compiler_vec_test.go (about) 1 package compiler 2 3 import ( 4 "encoding/binary" 5 "math" 6 "testing" 7 8 "wa-lang.org/wazero/internal/moremath" 9 "wa-lang.org/wazero/internal/testing/require" 10 "wa-lang.org/wazero/internal/wasm" 11 "wa-lang.org/wazero/internal/wazeroir" 12 ) 13 14 func TestCompiler_compileV128Add(t *testing.T) { 15 tests := []struct { 16 name string 17 shape wazeroir.Shape 18 x1, x2, exp [16]byte 19 }{ 20 { 21 name: "i8x16", 22 shape: wazeroir.ShapeI8x16, 23 x1: [16]byte{0: 1, 2: 10, 10: 10}, 24 x2: [16]byte{0: 10, 4: 5, 10: 5}, 25 exp: [16]byte{0: 11, 2: 10, 4: 5, 10: 15}, 26 }, 27 { 28 name: "i16x8", 29 shape: wazeroir.ShapeI16x8, 30 x1: i16x8(1123, 0, 123, 1, 1, 5, 8, 1), 31 x2: i16x8(0, 123, 123, 0, 1, 5, 9, 1), 32 exp: i16x8(1123, 123, 246, 1, 2, 10, 17, 2), 33 }, 34 { 35 name: "i32x4", 36 shape: wazeroir.ShapeI32x4, 37 x1: i32x4(i32ToU32(-123), 5, 4, math.MaxUint32), 38 x2: i32x4(i32ToU32(-10), 1, i32ToU32(-104), math.MaxUint32), 39 exp: i32x4(i32ToU32(-133), 6, i32ToU32(-100), math.MaxUint32-1), 40 }, 41 { 42 name: "i64x2", 43 shape: wazeroir.ShapeI64x2, 44 x1: i64x2(i64ToU64(math.MinInt64), 12345), 45 x2: i64x2(i64ToU64(-1), i64ToU64(-12345)), 46 exp: i64x2(i64ToU64(math.MinInt64)+i64ToU64(-1), 0), 47 }, 48 { 49 name: "f32x4", 50 shape: wazeroir.ShapeF32x4, 51 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))), 52 x2: f32x4(51234.12341, 123, math.MaxFloat32, -123), 53 exp: f32x4(51235.12341, 246, float32(math.Inf(1)), float32(math.Inf(-1))), 54 }, 55 { 56 name: "f64x2", 57 shape: wazeroir.ShapeF64x2, 58 x1: f64x2(1.123, math.Inf(1)), 59 x2: f64x2(1.123, math.MinInt64), 60 exp: f64x2(2.246, math.Inf(1)), 61 }, 62 } 63 64 for _, tc := range tests { 65 tc := tc 66 t.Run(tc.name, func(t *testing.T) { 67 env := newCompilerEnvironment() 68 compiler := env.requireNewCompiler(t, newCompiler, 69 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 70 71 err := compiler.compilePreamble() 72 require.NoError(t, err) 73 74 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 75 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 76 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 77 }) 78 require.NoError(t, err) 79 80 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 81 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 82 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 83 }) 84 require.NoError(t, err) 85 86 err = compiler.compileV128Add(&wazeroir.OperationV128Add{Shape: tc.shape}) 87 require.NoError(t, err) 88 89 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 90 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 91 92 err = compiler.compileReturnFunction() 93 require.NoError(t, err) 94 95 // Generate and run the code under test. 96 code, _, err := compiler.compile() 97 require.NoError(t, err) 98 env.exec(code) 99 100 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 101 102 lo, hi := env.stackTopAsV128() 103 var actual [16]byte 104 binary.LittleEndian.PutUint64(actual[:8], lo) 105 binary.LittleEndian.PutUint64(actual[8:], hi) 106 require.Equal(t, tc.exp, actual) 107 }) 108 } 109 } 110 111 func TestCompiler_compileV128Sub(t *testing.T) { 112 tests := []struct { 113 name string 114 shape wazeroir.Shape 115 x1, x2, exp [16]byte 116 }{ 117 { 118 name: "i8x16", 119 shape: wazeroir.ShapeI8x16, 120 x1: [16]byte{0: 1, 2: 10, 10: 10}, 121 x2: [16]byte{0: 10, 4: 5, 10: 5}, 122 exp: [16]byte{0: i8ToU8(-9), 2: 10, 4: i8ToU8(-5), 10: 5}, 123 }, 124 { 125 name: "i16x8", 126 shape: wazeroir.ShapeI16x8, 127 x1: i16x8(1123, 0, 123, 1, 1, 5, 8, 1), 128 x2: i16x8(0, 123, 123, 0, 1, 5, 9, 1), 129 exp: i16x8(1123, i16ToU16(-123), 0, 1, 0, 0, i16ToU16(-1), 0), 130 }, 131 { 132 name: "i32x4", 133 shape: wazeroir.ShapeI32x4, 134 x1: i32x4(i32ToU32(-123), 5, 4, math.MaxUint32), 135 x2: i32x4(i32ToU32(-10), 1, i32ToU32(-104), math.MaxUint32), 136 exp: i32x4(i32ToU32(-113), 4, 108, 0), 137 }, 138 { 139 name: "i64x2", 140 shape: wazeroir.ShapeI64x2, 141 x1: i64x2(i64ToU64(math.MinInt64), 12345), 142 x2: i64x2(i64ToU64(-1), i64ToU64(-12345)), 143 exp: i64x2(i64ToU64(math.MinInt64+1), 12345*2), 144 }, 145 { 146 name: "f32x4", 147 shape: wazeroir.ShapeF32x4, 148 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))), 149 x2: f32x4(51234.12341, 123, math.MaxFloat32, -123), 150 exp: f32x4(-51233.12341, 0, float32(math.Inf(1)), float32(math.Inf(-1))), 151 }, 152 { 153 name: "f64x2", 154 shape: wazeroir.ShapeF64x2, 155 x1: f64x2(1.123, math.Inf(1)), 156 x2: f64x2(1.123, math.MinInt64), 157 exp: f64x2(0, math.Inf(1)), 158 }, 159 } 160 161 for _, tc := range tests { 162 tc := tc 163 t.Run(tc.name, func(t *testing.T) { 164 env := newCompilerEnvironment() 165 compiler := env.requireNewCompiler(t, newCompiler, 166 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 167 168 err := compiler.compilePreamble() 169 require.NoError(t, err) 170 171 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 172 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 173 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 174 }) 175 require.NoError(t, err) 176 177 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 178 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 179 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 180 }) 181 require.NoError(t, err) 182 183 err = compiler.compileV128Sub(&wazeroir.OperationV128Sub{Shape: tc.shape}) 184 require.NoError(t, err) 185 186 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 187 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 188 189 err = compiler.compileReturnFunction() 190 require.NoError(t, err) 191 192 // Generate and run the code under test. 193 code, _, err := compiler.compile() 194 require.NoError(t, err) 195 env.exec(code) 196 197 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 198 199 lo, hi := env.stackTopAsV128() 200 var actual [16]byte 201 binary.LittleEndian.PutUint64(actual[:8], lo) 202 binary.LittleEndian.PutUint64(actual[8:], hi) 203 require.Equal(t, tc.exp, actual) 204 }) 205 } 206 } 207 208 func TestCompiler_compileV128Load(t *testing.T) { 209 tests := []struct { 210 name string 211 memSetupFn func(buf []byte) 212 loadType wazeroir.V128LoadType 213 offset uint32 214 exp [16]byte 215 }{ 216 { 217 name: "v128 offset=0", loadType: wazeroir.V128LoadType128, offset: 0, 218 memSetupFn: func(buf []byte) { 219 copy(buf, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}) 220 }, 221 exp: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 222 }, 223 { 224 name: "v128 offset=2", loadType: wazeroir.V128LoadType128, offset: 2, 225 memSetupFn: func(buf []byte) { 226 copy(buf, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}) 227 }, 228 exp: [16]byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, 229 }, 230 { 231 name: "8x8s offset=0", loadType: wazeroir.V128LoadType8x8s, offset: 0, 232 memSetupFn: func(buf []byte) { 233 copy(buf, []byte{ 234 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, 235 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 236 }) 237 }, 238 exp: [16]byte{ 239 1, 0, 0xff, 0xff, 3, 0, 0xff, 0xff, 5, 0, 0xff, 0xff, 7, 0, 0xff, 0xff, 240 }, 241 }, 242 { 243 name: "8x8s offset=3", loadType: wazeroir.V128LoadType8x8s, offset: 3, 244 memSetupFn: func(buf []byte) { 245 copy(buf, []byte{ 246 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, 247 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 248 }) 249 }, 250 exp: [16]byte{ 251 0xff, 0xff, 5, 0, 0xff, 0xff, 7, 0, 0xff, 0xff, 9, 0, 10, 0, 11, 0, 252 }, 253 }, 254 { 255 name: "8x8u offset=0", loadType: wazeroir.V128LoadType8x8u, offset: 0, 256 memSetupFn: func(buf []byte) { 257 copy(buf, []byte{ 258 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, 259 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 260 }) 261 }, 262 exp: [16]byte{ 263 1, 0, 0xff, 0, 3, 0, 0xff, 0, 5, 0, 0xff, 0, 7, 0, 0xff, 0, 264 }, 265 }, 266 { 267 name: "8x8i offset=3", loadType: wazeroir.V128LoadType8x8u, offset: 3, 268 memSetupFn: func(buf []byte) { 269 copy(buf, []byte{ 270 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, 271 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 272 }) 273 }, 274 exp: [16]byte{ 275 0xff, 0, 5, 0, 0xff, 0, 7, 0, 0xff, 0, 9, 0, 10, 0, 11, 0, 276 }, 277 }, 278 { 279 name: "16x4s offset=0", loadType: wazeroir.V128LoadType16x4s, offset: 0, 280 memSetupFn: func(buf []byte) { 281 copy(buf, []byte{ 282 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, 283 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 284 }) 285 }, 286 exp: [16]byte{ 287 1, 0xff, 0xff, 0xff, 288 3, 0xff, 0xff, 0xff, 289 5, 0xff, 0xff, 0xff, 290 7, 0xff, 0xff, 0xff, 291 }, 292 }, 293 { 294 name: "16x4s offset=3", loadType: wazeroir.V128LoadType16x4s, offset: 3, 295 memSetupFn: func(buf []byte) { 296 copy(buf, []byte{ 297 1, 0xff, 3, 0xff, 5, 6, 0xff, 0xff, 9, 10, 298 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 299 }) 300 }, 301 exp: [16]byte{ 302 0xff, 5, 0, 0, 303 6, 0xff, 0xff, 0xff, 304 0xff, 9, 0, 0, 305 10, 11, 0, 0, 306 }, 307 }, 308 { 309 name: "16x4u offset=0", loadType: wazeroir.V128LoadType16x4u, offset: 0, 310 memSetupFn: func(buf []byte) { 311 copy(buf, []byte{ 312 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10, 313 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 314 }) 315 }, 316 exp: [16]byte{ 317 1, 0xff, 0, 0, 318 3, 0xff, 0, 0, 319 5, 0xff, 0, 0, 320 7, 0xff, 0, 0, 321 }, 322 }, 323 { 324 name: "16x4u offset=3", loadType: wazeroir.V128LoadType16x4u, offset: 3, 325 memSetupFn: func(buf []byte) { 326 copy(buf, []byte{ 327 1, 0xff, 3, 0xff, 5, 6, 0xff, 0xff, 9, 10, 328 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 329 }) 330 }, 331 exp: [16]byte{ 332 0xff, 5, 0, 0, 333 6, 0xff, 0, 0, 334 0xff, 9, 0, 0, 335 10, 11, 0, 0, 336 }, 337 }, 338 { 339 name: "32x2s offset=0", loadType: wazeroir.V128LoadType32x2s, offset: 0, 340 memSetupFn: func(buf []byte) { 341 copy(buf, []byte{ 342 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 10, 343 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 344 }) 345 }, 346 exp: [16]byte{ 347 1, 0xff, 3, 0xff, 0xff, 0xff, 0xff, 0xff, 348 5, 6, 7, 0xff, 0xff, 0xff, 0xff, 0xff, 349 }, 350 }, 351 { 352 name: "32x2s offset=2", loadType: wazeroir.V128LoadType32x2s, offset: 2, 353 memSetupFn: func(buf []byte) { 354 copy(buf, []byte{ 355 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 356 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 357 }) 358 }, 359 exp: [16]byte{ 360 3, 0xff, 5, 6, 0, 0, 0, 0, 361 7, 0xff, 9, 0xff, 0xff, 0xff, 0xff, 0xff, 362 }, 363 }, 364 { 365 name: "32x2u offset=0", loadType: wazeroir.V128LoadType32x2u, offset: 0, 366 memSetupFn: func(buf []byte) { 367 copy(buf, []byte{ 368 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 10, 369 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 370 }) 371 }, 372 exp: [16]byte{ 373 1, 0xff, 3, 0xff, 0, 0, 0, 0, 374 5, 6, 7, 0xff, 0, 0, 0, 0, 375 }, 376 }, 377 { 378 name: "32x2u offset=2", loadType: wazeroir.V128LoadType32x2u, offset: 2, 379 memSetupFn: func(buf []byte) { 380 copy(buf, []byte{ 381 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 382 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 383 }) 384 }, 385 exp: [16]byte{ 386 3, 0xff, 5, 6, 0, 0, 0, 0, 387 7, 0xff, 9, 0xff, 0, 0, 0, 0, 388 }, 389 }, 390 { 391 name: "32zero offset=0", loadType: wazeroir.V128LoadType32zero, offset: 0, 392 memSetupFn: func(buf []byte) { 393 copy(buf, []byte{ 394 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 395 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 396 }) 397 }, 398 exp: [16]byte{ 399 1, 0xff, 3, 0xff, 0, 0, 0, 0, 400 0, 0, 0, 0, 0, 0, 0, 0, 401 }, 402 }, 403 { 404 name: "32zero offset=3", loadType: wazeroir.V128LoadType32zero, offset: 3, 405 memSetupFn: func(buf []byte) { 406 copy(buf, []byte{ 407 1, 0xff, 3, 0xff, 5, 6, 0xff, 8, 9, 0xff, 408 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 409 }) 410 }, 411 exp: [16]byte{ 412 0xff, 5, 6, 0xff, 0, 0, 0, 0, 413 0, 0, 0, 0, 0, 0, 0, 0, 414 }, 415 }, 416 { 417 name: "32zero on ceil", loadType: wazeroir.V128LoadType32zero, 418 offset: wasm.MemoryPageSize - 4, 419 memSetupFn: func(buf []byte) { 420 copy(buf[wasm.MemoryPageSize-8:], []byte{ 421 1, 0xff, 3, 0xff, 422 5, 6, 0xff, 8, 423 }) 424 }, 425 exp: [16]byte{5, 6, 0xff, 8}, 426 }, 427 { 428 name: "64zero offset=0", loadType: wazeroir.V128LoadType64zero, offset: 0, 429 memSetupFn: func(buf []byte) { 430 copy(buf, []byte{ 431 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 432 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 433 }) 434 }, 435 exp: [16]byte{ 436 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 437 0, 0, 0, 0, 0, 0, 0, 0, 438 }, 439 }, 440 { 441 name: "64zero offset=2", loadType: wazeroir.V128LoadType64zero, offset: 2, 442 memSetupFn: func(buf []byte) { 443 copy(buf, []byte{ 444 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 445 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 446 }) 447 }, 448 exp: [16]byte{ 449 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 450 0, 0, 0, 0, 0, 0, 0, 0, 451 }, 452 }, 453 { 454 name: "64zero on ceil", loadType: wazeroir.V128LoadType64zero, 455 offset: wasm.MemoryPageSize - 8, 456 memSetupFn: func(buf []byte) { 457 copy(buf[wasm.MemoryPageSize-16:], []byte{ 458 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 459 9, 0xff, 11, 12, 13, 14, 15, 460 }) 461 }, 462 exp: [16]byte{9, 0xff, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0}, 463 }, 464 { 465 name: "8splat offset=0", loadType: wazeroir.V128LoadType8Splat, offset: 0, 466 memSetupFn: func(buf []byte) { 467 copy(buf, []byte{ 468 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 469 }) 470 }, 471 exp: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 472 }, 473 { 474 name: "8splat offset=1", loadType: wazeroir.V128LoadType8Splat, offset: 1, 475 memSetupFn: func(buf []byte) { 476 copy(buf, []byte{ 477 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 478 }) 479 }, 480 exp: [16]byte{ 481 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 482 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 483 }, 484 }, 485 { 486 name: "16splat offset=0", loadType: wazeroir.V128LoadType16Splat, offset: 0, 487 memSetupFn: func(buf []byte) { 488 copy(buf, []byte{ 489 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 490 }) 491 }, 492 exp: [16]byte{1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff}, 493 }, 494 { 495 name: "16splat offset=5", loadType: wazeroir.V128LoadType16Splat, offset: 5, 496 memSetupFn: func(buf []byte) { 497 copy(buf, []byte{ 498 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 499 }) 500 }, 501 exp: [16]byte{6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7}, 502 }, 503 { 504 name: "32splat offset=0", loadType: wazeroir.V128LoadType32Splat, offset: 0, 505 memSetupFn: func(buf []byte) { 506 copy(buf, []byte{ 507 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 508 }) 509 }, 510 exp: [16]byte{1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff}, 511 }, 512 { 513 name: "32splat offset=1", loadType: wazeroir.V128LoadType32Splat, offset: 1, 514 memSetupFn: func(buf []byte) { 515 copy(buf, []byte{ 516 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 517 }) 518 }, 519 exp: [16]byte{0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5}, 520 }, 521 { 522 name: "64splat offset=0", loadType: wazeroir.V128LoadType64Splat, offset: 0, 523 memSetupFn: func(buf []byte) { 524 copy(buf, []byte{ 525 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 526 }) 527 }, 528 exp: [16]byte{1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 1, 0xff, 3, 0xff, 5, 6, 7, 0xff}, 529 }, 530 { 531 name: "64splat offset=1", loadType: wazeroir.V128LoadType64Splat, offset: 1, 532 memSetupFn: func(buf []byte) { 533 copy(buf, []byte{ 534 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 535 }) 536 }, 537 exp: [16]byte{0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9}, 538 }, 539 } 540 541 for _, tc := range tests { 542 tc := tc 543 t.Run(tc.name, func(t *testing.T) { 544 env := newCompilerEnvironment() 545 tc.memSetupFn(env.memory()) 546 547 compiler := env.requireNewCompiler(t, newCompiler, 548 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 549 550 err := compiler.compilePreamble() 551 require.NoError(t, err) 552 553 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) 554 require.NoError(t, err) 555 556 err = compiler.compileV128Load(&wazeroir.OperationV128Load{ 557 Type: tc.loadType, Arg: &wazeroir.MemoryArg{}, 558 }) 559 require.NoError(t, err) 560 561 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 562 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 563 loadedLocation := compiler.runtimeValueLocationStack().peek() 564 require.True(t, loadedLocation.onRegister()) 565 566 err = compiler.compileReturnFunction() 567 require.NoError(t, err) 568 569 // Generate and run the code under test. 570 code, _, err := compiler.compile() 571 require.NoError(t, err) 572 env.exec(code) 573 574 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 575 576 require.Equal(t, uint64(2), env.stackPointer()) 577 lo, hi := env.stackTopAsV128() 578 579 var actual [16]byte 580 binary.LittleEndian.PutUint64(actual[:8], lo) 581 binary.LittleEndian.PutUint64(actual[8:], hi) 582 require.Equal(t, tc.exp, actual) 583 }) 584 } 585 } 586 587 func TestCompiler_compileV128LoadLane(t *testing.T) { 588 originalVecLo, originalVecHi := uint64(0), uint64(0) 589 tests := []struct { 590 name string 591 memSetupFn func(buf []byte) 592 laneIndex, laneSize byte 593 offset uint32 594 exp [16]byte 595 }{ 596 { 597 name: "8_lane offset=0 laneIndex=0", 598 memSetupFn: func(buf []byte) { 599 copy(buf, []byte{ 600 1, 0xff, 601 }) 602 }, 603 laneSize: 8, 604 laneIndex: 0, 605 offset: 0, 606 exp: [16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 607 }, 608 { 609 name: "8_lane offset=1 laneIndex=0", 610 memSetupFn: func(buf []byte) { 611 copy(buf, []byte{ 612 1, 0xff, 613 }) 614 }, 615 laneSize: 8, 616 laneIndex: 0, 617 offset: 1, 618 exp: [16]byte{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 619 }, 620 { 621 name: "8_lane offset=1 laneIndex=5", 622 memSetupFn: func(buf []byte) { 623 copy(buf, []byte{ 624 1, 0xff, 625 }) 626 }, 627 laneSize: 8, 628 laneIndex: 5, 629 offset: 1, 630 exp: [16]byte{0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 631 }, 632 { 633 name: "16_lane offset=0 laneIndex=0", 634 memSetupFn: func(buf []byte) { 635 copy(buf, []byte{ 636 1, 0xff, 1, 0xa, 637 }) 638 }, 639 laneSize: 16, 640 laneIndex: 0, 641 offset: 0, 642 exp: [16]byte{1, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 643 }, 644 { 645 name: "16_lane offset=1 laneIndex=0", 646 memSetupFn: func(buf []byte) { 647 copy(buf, []byte{ 648 1, 0xff, 1, 0xa, 649 }) 650 }, 651 laneSize: 16, 652 laneIndex: 0, 653 offset: 1, 654 exp: [16]byte{0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 655 }, 656 { 657 name: "16_lane offset=1 laneIndex=5", 658 memSetupFn: func(buf []byte) { 659 copy(buf, []byte{ 660 1, 0xff, 1, 0xa, 661 }) 662 }, 663 laneSize: 16, 664 laneIndex: 5, 665 offset: 1, 666 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 1, 0, 0, 0, 0}, 667 }, 668 { 669 name: "32_lane offset=0 laneIndex=0", 670 memSetupFn: func(buf []byte) { 671 copy(buf, []byte{ 672 1, 0xff, 1, 0xa, 0x9, 0x8, 673 }) 674 }, 675 laneSize: 32, 676 laneIndex: 0, 677 offset: 0, 678 exp: [16]byte{1, 0xff, 1, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 679 }, 680 { 681 name: "32_lane offset=1 laneIndex=0", 682 memSetupFn: func(buf []byte) { 683 copy(buf, []byte{ 684 1, 0xff, 1, 0xa, 0x9, 0x8, 685 }) 686 }, 687 laneSize: 32, 688 laneIndex: 0, 689 offset: 1, 690 exp: [16]byte{0xff, 1, 0xa, 0x9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 691 }, 692 { 693 name: "32_lane offset=1 laneIndex=3", 694 memSetupFn: func(buf []byte) { 695 copy(buf, []byte{ 696 1, 0xff, 1, 0xa, 0x9, 0x8, 697 }) 698 }, 699 laneSize: 32, 700 laneIndex: 3, 701 offset: 1, 702 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 1, 0xa, 0x9}, 703 }, 704 705 { 706 name: "64_lane offset=0 laneIndex=0", 707 memSetupFn: func(buf []byte) { 708 copy(buf, []byte{ 709 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 710 }) 711 }, 712 laneSize: 64, 713 laneIndex: 0, 714 offset: 0, 715 exp: [16]byte{1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0, 0, 0, 0, 0, 0, 0, 0}, 716 }, 717 { 718 name: "64_lane offset=1 laneIndex=0", 719 memSetupFn: func(buf []byte) { 720 copy(buf, []byte{ 721 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 722 }) 723 }, 724 laneSize: 64, 725 laneIndex: 0, 726 offset: 1, 727 exp: [16]byte{0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0, 0, 0, 0, 0, 0, 0, 0}, 728 }, 729 { 730 name: "64_lane offset=3 laneIndex=1", 731 memSetupFn: func(buf []byte) { 732 copy(buf, []byte{ 733 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 0xa, 734 }) 735 }, 736 laneSize: 64, 737 laneIndex: 1, 738 offset: 3, 739 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 0xa}, 740 }, 741 } 742 743 for _, tc := range tests { 744 tc := tc 745 t.Run(tc.name, func(t *testing.T) { 746 env := newCompilerEnvironment() 747 tc.memSetupFn(env.memory()) 748 749 compiler := env.requireNewCompiler(t, newCompiler, 750 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 751 752 err := compiler.compilePreamble() 753 require.NoError(t, err) 754 755 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) 756 require.NoError(t, err) 757 758 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 759 Lo: originalVecLo, 760 Hi: originalVecHi, 761 }) 762 require.NoError(t, err) 763 764 err = compiler.compileV128LoadLane(&wazeroir.OperationV128LoadLane{ 765 LaneIndex: tc.laneIndex, LaneSize: tc.laneSize, Arg: &wazeroir.MemoryArg{}, 766 }) 767 require.NoError(t, err) 768 769 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 770 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 771 loadedLocation := compiler.runtimeValueLocationStack().peek() 772 require.True(t, loadedLocation.onRegister()) 773 774 err = compiler.compileReturnFunction() 775 require.NoError(t, err) 776 777 // Generate and run the code under test. 778 code, _, err := compiler.compile() 779 require.NoError(t, err) 780 env.exec(code) 781 782 require.Equal(t, uint64(2), env.stackPointer()) 783 lo, hi := env.stackTopAsV128() 784 785 var actual [16]byte 786 binary.LittleEndian.PutUint64(actual[:8], lo) 787 binary.LittleEndian.PutUint64(actual[8:], hi) 788 require.Equal(t, tc.exp, actual) 789 }) 790 } 791 } 792 793 func TestCompiler_compileV128Store(t *testing.T) { 794 tests := []struct { 795 name string 796 offset uint32 797 }{ 798 {name: "offset=1", offset: 1}, 799 {name: "offset=5", offset: 5}, 800 {name: "offset=10", offset: 10}, 801 } 802 803 for _, tc := range tests { 804 tc := tc 805 t.Run(tc.name, func(t *testing.T) { 806 env := newCompilerEnvironment() 807 808 compiler := env.requireNewCompiler(t, newCompiler, 809 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 810 811 err := compiler.compilePreamble() 812 require.NoError(t, err) 813 814 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) 815 require.NoError(t, err) 816 817 err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: ^uint64(0), Hi: ^uint64(0)}) 818 require.NoError(t, err) 819 820 err = compiler.compileV128Store(&wazeroir.OperationV128Store{Arg: &wazeroir.MemoryArg{}}) 821 require.NoError(t, err) 822 823 requireRuntimeLocationStackPointerEqual(t, uint64(0), compiler) 824 require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters)) 825 826 err = compiler.compileReturnFunction() 827 require.NoError(t, err) 828 829 // Generate and run the code under test. 830 code, _, err := compiler.compile() 831 require.NoError(t, err) 832 env.exec(code) 833 834 require.Equal(t, uint64(0), env.stackPointer()) 835 836 mem := env.memory() 837 require.Equal(t, []byte{ 838 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 839 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 840 }, 841 mem[tc.offset:tc.offset+16]) 842 }) 843 } 844 } 845 846 func TestCompiler_compileV128StoreLane(t *testing.T) { 847 vecBytes := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 848 tests := []struct { 849 name string 850 laneIndex, laneSize byte 851 offset uint32 852 exp [16]byte 853 }{ 854 { 855 name: "8_lane offset=0 laneIndex=0", 856 laneSize: 8, 857 laneIndex: 0, 858 offset: 0, 859 exp: [16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 860 }, 861 { 862 name: "8_lane offset=1 laneIndex=0", 863 laneSize: 8, 864 laneIndex: 0, 865 offset: 1, 866 exp: [16]byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 867 }, 868 { 869 name: "8_lane offset=3 laneIndex=5", 870 laneSize: 8, 871 laneIndex: 5, 872 offset: 3, 873 exp: [16]byte{0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 874 }, 875 { 876 name: "16_lane offset=0 laneIndex=0", 877 laneSize: 16, 878 laneIndex: 0, 879 offset: 0, 880 exp: [16]byte{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 881 }, 882 { 883 name: "16_lane offset=1 laneIndex=0", 884 laneSize: 16, 885 laneIndex: 0, 886 offset: 1, 887 exp: [16]byte{0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 888 }, 889 { 890 name: "16_lane offset=5 laneIndex=7", 891 laneSize: 16, 892 laneIndex: 7, 893 offset: 5, 894 exp: [16]byte{0, 0, 0, 0, 0, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 895 }, 896 897 { 898 name: "32_lane offset=0 laneIndex=0", 899 laneSize: 32, 900 laneIndex: 0, 901 offset: 0, 902 exp: [16]byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 903 }, 904 { 905 name: "32_lane offset=1 laneIndex=0", 906 laneSize: 32, 907 laneIndex: 0, 908 offset: 1, 909 exp: [16]byte{0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 910 }, 911 { 912 name: "32_lane offset=5 laneIndex=3", 913 laneSize: 32, 914 laneIndex: 3, 915 offset: 5, 916 exp: [16]byte{0, 0, 0, 0, 0, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0}, 917 }, 918 919 { 920 name: "64_lane offset=0 laneIndex=0", 921 laneSize: 64, 922 laneIndex: 0, 923 offset: 0, 924 exp: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0}, 925 }, 926 { 927 name: "64_lane offset=1 laneIndex=0", 928 laneSize: 64, 929 laneIndex: 0, 930 offset: 1, 931 exp: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0}, 932 }, 933 { 934 name: "64_lane offset=5 laneIndex=3", 935 laneSize: 64, 936 laneIndex: 1, 937 offset: 6, 938 exp: [16]byte{0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0}, 939 }, 940 } 941 942 for _, tc := range tests { 943 tc := tc 944 t.Run(tc.name, func(t *testing.T) { 945 env := newCompilerEnvironment() 946 947 compiler := env.requireNewCompiler(t, newCompiler, 948 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 949 950 err := compiler.compilePreamble() 951 require.NoError(t, err) 952 953 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.offset}) 954 require.NoError(t, err) 955 956 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 957 Lo: binary.LittleEndian.Uint64(vecBytes[:8]), 958 Hi: binary.LittleEndian.Uint64(vecBytes[8:]), 959 }) 960 require.NoError(t, err) 961 962 err = compiler.compileV128StoreLane(&wazeroir.OperationV128StoreLane{ 963 LaneIndex: tc.laneIndex, LaneSize: tc.laneSize, Arg: &wazeroir.MemoryArg{}, 964 }) 965 require.NoError(t, err) 966 967 requireRuntimeLocationStackPointerEqual(t, uint64(0), compiler) 968 require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters)) 969 970 err = compiler.compileReturnFunction() 971 require.NoError(t, err) 972 973 // Generate and run the code under test. 974 code, _, err := compiler.compile() 975 require.NoError(t, err) 976 env.exec(code) 977 978 require.Equal(t, tc.exp[:], env.memory()[:16]) 979 }) 980 } 981 } 982 983 func TestCompiler_compileV128ExtractLane(t *testing.T) { 984 tests := []struct { 985 name string 986 vecBytes [16]byte 987 shape wazeroir.Shape 988 signed bool 989 laneIndex byte 990 exp uint64 991 }{ 992 { 993 name: "i8x16 unsigned index=0", 994 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 995 shape: wazeroir.ShapeI8x16, 996 signed: false, 997 laneIndex: 0, 998 exp: uint64(byte(1)), 999 }, 1000 { 1001 name: "i8x16 unsigned index=15", 1002 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xff}, 1003 shape: wazeroir.ShapeI8x16, 1004 signed: false, 1005 laneIndex: 15, 1006 exp: uint64(byte(0xff)), 1007 }, 1008 { 1009 name: "i8x16 signed index=0", 1010 vecBytes: [16]byte{0xf1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 1011 shape: wazeroir.ShapeI8x16, 1012 signed: true, 1013 laneIndex: 0, 1014 exp: uint64(0xff_ff_ff_f1), 1015 }, 1016 { 1017 name: "i8x16 signed index=1", 1018 vecBytes: [16]byte{0xf0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 1019 shape: wazeroir.ShapeI8x16, 1020 signed: true, 1021 laneIndex: 1, 1022 exp: uint64(2), 1023 }, 1024 { 1025 name: "i16x8 unsigned index=0", 1026 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 1027 shape: wazeroir.ShapeI16x8, 1028 signed: false, 1029 laneIndex: 0, 1030 exp: uint64(uint16(0x2<<8 | 0x1)), 1031 }, 1032 { 1033 name: "i16x8 unsigned index=7", 1034 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xff}, 1035 shape: wazeroir.ShapeI16x8, 1036 signed: false, 1037 laneIndex: 7, 1038 exp: uint64(uint16(0xff<<8 | 15)), 1039 }, 1040 { 1041 name: "i16x8 signed index=0", 1042 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 1043 shape: wazeroir.ShapeI16x8, 1044 signed: true, 1045 laneIndex: 0, 1046 exp: uint64(uint16(0x2<<8 | 0x1)), 1047 }, 1048 { 1049 name: "i16x8 signed index=7", 1050 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xf1}, 1051 shape: wazeroir.ShapeI16x8, 1052 signed: true, 1053 laneIndex: 7, 1054 exp: uint64(uint32(0xffff<<16) | uint32(uint16(0xf1<<8|15))), 1055 }, 1056 { 1057 name: "i32x4 index=0", 1058 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1059 shape: wazeroir.ShapeI32x4, 1060 laneIndex: 0, 1061 exp: uint64(uint32(0x04_03_02_01)), 1062 }, 1063 { 1064 name: "i32x4 index=3", 1065 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1066 shape: wazeroir.ShapeI32x4, 1067 laneIndex: 3, 1068 exp: uint64(uint32(0x16_15_14_13)), 1069 }, 1070 { 1071 name: "i64x4 index=0", 1072 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1073 shape: wazeroir.ShapeI64x2, 1074 laneIndex: 0, 1075 exp: uint64(0x08_07_06_05_04_03_02_01), 1076 }, 1077 { 1078 name: "i64x4 index=1", 1079 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1080 shape: wazeroir.ShapeI64x2, 1081 laneIndex: 1, 1082 exp: uint64(0x16_15_14_13_12_11_10_09), 1083 }, 1084 { 1085 name: "f32x4 index=0", 1086 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1087 shape: wazeroir.ShapeF32x4, 1088 laneIndex: 0, 1089 exp: uint64(uint32(0x04_03_02_01)), 1090 }, 1091 { 1092 name: "f32x4 index=3", 1093 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1094 shape: wazeroir.ShapeF32x4, 1095 laneIndex: 3, 1096 exp: uint64(uint32(0x16_15_14_13)), 1097 }, 1098 { 1099 name: "f64x4 index=0", 1100 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1101 shape: wazeroir.ShapeF64x2, 1102 laneIndex: 0, 1103 exp: uint64(0x08_07_06_05_04_03_02_01), 1104 }, 1105 { 1106 name: "f64x4 index=1", 1107 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}, 1108 shape: wazeroir.ShapeF64x2, 1109 laneIndex: 1, 1110 exp: uint64(0x16_15_14_13_12_11_10_09), 1111 }, 1112 } 1113 1114 for _, tc := range tests { 1115 tc := tc 1116 t.Run(tc.name, func(t *testing.T) { 1117 env := newCompilerEnvironment() 1118 1119 compiler := env.requireNewCompiler(t, newCompiler, 1120 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1121 1122 err := compiler.compilePreamble() 1123 require.NoError(t, err) 1124 1125 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 1126 Lo: binary.LittleEndian.Uint64(tc.vecBytes[:8]), 1127 Hi: binary.LittleEndian.Uint64(tc.vecBytes[8:]), 1128 }) 1129 require.NoError(t, err) 1130 1131 err = compiler.compileV128ExtractLane(&wazeroir.OperationV128ExtractLane{ 1132 LaneIndex: tc.laneIndex, 1133 Signed: tc.signed, 1134 Shape: tc.shape, 1135 }) 1136 require.NoError(t, err) 1137 1138 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 1139 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 1140 1141 vt := compiler.runtimeValueLocationStack().peek().valueType 1142 switch tc.shape { 1143 case wazeroir.ShapeI8x16, wazeroir.ShapeI16x8, wazeroir.ShapeI32x4: 1144 require.Equal(t, runtimeValueTypeI32, vt) 1145 case wazeroir.ShapeI64x2: 1146 require.Equal(t, runtimeValueTypeI64, vt) 1147 case wazeroir.ShapeF32x4: 1148 require.Equal(t, runtimeValueTypeF32, vt) 1149 case wazeroir.ShapeF64x2: 1150 require.Equal(t, runtimeValueTypeF64, vt) 1151 } 1152 1153 err = compiler.compileReturnFunction() 1154 require.NoError(t, err) 1155 1156 // Generate and run the code under test. 1157 code, _, err := compiler.compile() 1158 require.NoError(t, err) 1159 env.exec(code) 1160 1161 switch tc.shape { 1162 case wazeroir.ShapeI8x16, wazeroir.ShapeI16x8, wazeroir.ShapeI32x4, wazeroir.ShapeF32x4: 1163 require.Equal(t, uint32(tc.exp), env.stackTopAsUint32()) 1164 case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2: 1165 require.Equal(t, tc.exp, env.stackTopAsUint64()) 1166 } 1167 }) 1168 } 1169 } 1170 1171 func TestCompiler_compileV128ReplaceLane(t *testing.T) { 1172 tests := []struct { 1173 name string 1174 originValueSetupFn func(*testing.T, compilerImpl) 1175 shape wazeroir.Shape 1176 laneIndex byte 1177 exp [16]byte 1178 lo, hi uint64 1179 }{ 1180 { 1181 name: "i8x16 index=0", 1182 shape: wazeroir.ShapeI8x16, 1183 laneIndex: 5, 1184 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1185 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff}) 1186 require.NoError(t, err) 1187 }, 1188 exp: [16]byte{5: 0xff}, 1189 }, 1190 { 1191 name: "i8x16 index=3", 1192 shape: wazeroir.ShapeI8x16, 1193 laneIndex: 5, 1194 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1195 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff << 8}) 1196 require.NoError(t, err) 1197 }, 1198 exp: [16]byte{}, 1199 }, 1200 { 1201 name: "i8x16 index=5", 1202 shape: wazeroir.ShapeI8x16, 1203 laneIndex: 5, 1204 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1205 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff}) 1206 require.NoError(t, err) 1207 }, 1208 exp: [16]byte{5: 0xff}, 1209 }, 1210 { 1211 name: "i16x8 index=0", 1212 shape: wazeroir.ShapeI16x8, 1213 laneIndex: 0, 1214 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1215 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xee_ff}) 1216 require.NoError(t, err) 1217 }, 1218 exp: [16]byte{0: 0xff, 1: 0xee}, 1219 }, 1220 { 1221 name: "i16x8 index=3", 1222 shape: wazeroir.ShapeI16x8, 1223 laneIndex: 3, 1224 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1225 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_00}) 1226 require.NoError(t, err) 1227 }, 1228 exp: [16]byte{7: 0xaa}, 1229 }, 1230 { 1231 name: "i16x8 index=7", 1232 shape: wazeroir.ShapeI16x8, 1233 laneIndex: 3, 1234 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1235 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_bb << 16}) 1236 require.NoError(t, err) 1237 }, 1238 exp: [16]byte{}, 1239 }, 1240 { 1241 name: "i32x4 index=0", 1242 shape: wazeroir.ShapeI32x4, 1243 laneIndex: 0, 1244 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1245 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_bb_cc_dd}) 1246 require.NoError(t, err) 1247 }, 1248 exp: [16]byte{0: 0xdd, 1: 0xcc, 2: 0xbb, 3: 0xaa}, 1249 }, 1250 { 1251 name: "i32x4 index=3", 1252 shape: wazeroir.ShapeI32x4, 1253 laneIndex: 3, 1254 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1255 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xaa_bb_cc_dd}) 1256 require.NoError(t, err) 1257 }, 1258 exp: [16]byte{12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, 1259 }, 1260 { 1261 name: "i64x2 index=0", 1262 shape: wazeroir.ShapeI64x2, 1263 laneIndex: 0, 1264 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1265 err := c.compileConstI64(&wazeroir.OperationConstI64{Value: 0xaa_bb_cc_dd_01_02_03_04}) 1266 require.NoError(t, err) 1267 }, 1268 exp: [16]byte{0: 0x04, 1: 0x03, 2: 0x02, 3: 0x01, 4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa}, 1269 }, 1270 { 1271 name: "i64x2 index=1", 1272 shape: wazeroir.ShapeI64x2, 1273 laneIndex: 1, 1274 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1275 err := c.compileConstI64(&wazeroir.OperationConstI64{Value: 0xaa_bb_cc_dd_01_02_03_04}) 1276 require.NoError(t, err) 1277 }, 1278 exp: [16]byte{8: 0x04, 9: 0x03, 10: 0x02, 11: 0x01, 12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, 1279 }, 1280 { 1281 name: "f32x4 index=0", 1282 shape: wazeroir.ShapeF32x4, 1283 laneIndex: 0, 1284 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1285 err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) 1286 require.NoError(t, err) 1287 }, 1288 exp: [16]byte{0: 0xdd, 1: 0xcc, 2: 0xbb, 3: 0xaa}, 1289 }, 1290 { 1291 name: "f32x4 index=1", 1292 shape: wazeroir.ShapeF32x4, 1293 laneIndex: 1, 1294 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1295 err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) 1296 require.NoError(t, err) 1297 }, 1298 exp: [16]byte{4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa}, 1299 }, 1300 { 1301 name: "f32x4 index=2", 1302 shape: wazeroir.ShapeF32x4, 1303 laneIndex: 2, 1304 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1305 err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) 1306 require.NoError(t, err) 1307 }, 1308 exp: [16]byte{8: 0xdd, 9: 0xcc, 10: 0xbb, 11: 0xaa}, 1309 }, 1310 { 1311 name: "f32x4 index=3", 1312 shape: wazeroir.ShapeF32x4, 1313 laneIndex: 3, 1314 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1315 err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xaa_bb_cc_dd)}) 1316 require.NoError(t, err) 1317 }, 1318 exp: [16]byte{12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, 1319 }, 1320 { 1321 name: "f64x2 index=0", 1322 shape: wazeroir.ShapeF64x2, 1323 laneIndex: 0, 1324 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1325 err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0xaa_bb_cc_dd_01_02_03_04)}) 1326 require.NoError(t, err) 1327 }, 1328 exp: [16]byte{0: 0x04, 1: 0x03, 2: 0x02, 3: 0x01, 4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa}, 1329 }, 1330 { 1331 name: "f64x2 index=1", 1332 shape: wazeroir.ShapeF64x2, 1333 laneIndex: 1, 1334 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1335 err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0xaa_bb_cc_dd_01_02_03_04)}) 1336 require.NoError(t, err) 1337 }, 1338 exp: [16]byte{8: 0x04, 9: 0x03, 10: 0x02, 11: 0x01, 12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa}, 1339 }, 1340 { 1341 name: "f64x2 index=0 / lo,hi = 1.0", 1342 shape: wazeroir.ShapeF64x2, 1343 laneIndex: 0, 1344 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1345 err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0.0)}) 1346 require.NoError(t, err) 1347 }, 1348 lo: math.Float64bits(1.0), 1349 hi: math.Float64bits(1.0), 1350 exp: [16]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f}, 1351 }, 1352 { 1353 name: "f64x2 index=1 / lo,hi = 1.0", 1354 shape: wazeroir.ShapeF64x2, 1355 laneIndex: 1, 1356 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1357 err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0.0)}) 1358 require.NoError(t, err) 1359 }, 1360 lo: math.Float64bits(1.0), 1361 hi: math.Float64bits(1.0), 1362 exp: [16]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 1363 }, 1364 } 1365 1366 for _, tc := range tests { 1367 tc := tc 1368 t.Run(tc.name, func(t *testing.T) { 1369 env := newCompilerEnvironment() 1370 compiler := env.requireNewCompiler(t, newCompiler, 1371 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1372 1373 err := compiler.compilePreamble() 1374 require.NoError(t, err) 1375 1376 err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: tc.lo, Hi: tc.hi}) 1377 require.NoError(t, err) 1378 1379 tc.originValueSetupFn(t, compiler) 1380 1381 err = compiler.compileV128ReplaceLane(&wazeroir.OperationV128ReplaceLane{ 1382 LaneIndex: tc.laneIndex, 1383 Shape: tc.shape, 1384 }) 1385 require.NoError(t, err) 1386 1387 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 1388 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 1389 1390 err = compiler.compileReturnFunction() 1391 require.NoError(t, err) 1392 1393 // Generate and run the code under test. 1394 code, _, err := compiler.compile() 1395 require.NoError(t, err) 1396 env.exec(code) 1397 1398 lo, hi := env.stackTopAsV128() 1399 var actual [16]byte 1400 binary.LittleEndian.PutUint64(actual[:8], lo) 1401 binary.LittleEndian.PutUint64(actual[8:], hi) 1402 require.Equal(t, tc.exp, actual) 1403 }) 1404 } 1405 } 1406 1407 func TestCompiler_compileV128Splat(t *testing.T) { 1408 tests := []struct { 1409 name string 1410 originValueSetupFn func(*testing.T, compilerImpl) 1411 shape wazeroir.Shape 1412 exp [16]byte 1413 }{ 1414 { 1415 name: "i8x16", 1416 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1417 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0x1}) 1418 require.NoError(t, err) 1419 }, 1420 shape: wazeroir.ShapeI8x16, 1421 exp: [16]byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}, 1422 }, 1423 { 1424 name: "i16x8", 1425 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1426 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff_11}) 1427 require.NoError(t, err) 1428 }, 1429 shape: wazeroir.ShapeI16x8, 1430 exp: [16]byte{0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff}, 1431 }, 1432 { 1433 name: "i32x4", 1434 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1435 err := c.compileConstI32(&wazeroir.OperationConstI32{Value: 0xff_11_ee_22}) 1436 require.NoError(t, err) 1437 }, 1438 shape: wazeroir.ShapeI32x4, 1439 exp: [16]byte{0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff}, 1440 }, 1441 { 1442 name: "i64x2", 1443 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1444 err := c.compileConstI64(&wazeroir.OperationConstI64{Value: 0xff_00_ee_00_11_00_22_00}) 1445 require.NoError(t, err) 1446 }, 1447 shape: wazeroir.ShapeI64x2, 1448 exp: [16]byte{0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff, 0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff}, 1449 }, 1450 { 1451 name: "f32x4", 1452 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1453 err := c.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(0xff_11_ee_22)}) 1454 require.NoError(t, err) 1455 }, 1456 shape: wazeroir.ShapeF32x4, 1457 exp: [16]byte{0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff}, 1458 }, 1459 { 1460 name: "f64x2", 1461 originValueSetupFn: func(t *testing.T, c compilerImpl) { 1462 err := c.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(0xff_00_ee_00_11_00_22_00)}) 1463 require.NoError(t, err) 1464 }, 1465 shape: wazeroir.ShapeF64x2, 1466 exp: [16]byte{0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff, 0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff}, 1467 }, 1468 } 1469 1470 for _, tc := range tests { 1471 tc := tc 1472 t.Run(tc.name, func(t *testing.T) { 1473 env := newCompilerEnvironment() 1474 compiler := env.requireNewCompiler(t, newCompiler, 1475 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1476 1477 err := compiler.compilePreamble() 1478 require.NoError(t, err) 1479 1480 tc.originValueSetupFn(t, compiler) 1481 1482 err = compiler.compileV128Splat(&wazeroir.OperationV128Splat{Shape: tc.shape}) 1483 require.NoError(t, err) 1484 1485 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 1486 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 1487 1488 err = compiler.compileReturnFunction() 1489 require.NoError(t, err) 1490 1491 // Generate and run the code under test. 1492 code, _, err := compiler.compile() 1493 require.NoError(t, err) 1494 env.exec(code) 1495 1496 lo, hi := env.stackTopAsV128() 1497 var actual [16]byte 1498 binary.LittleEndian.PutUint64(actual[:8], lo) 1499 binary.LittleEndian.PutUint64(actual[8:], hi) 1500 require.Equal(t, tc.exp, actual) 1501 }) 1502 } 1503 } 1504 1505 func TestCompiler_compileV128AnyTrue(t *testing.T) { 1506 tests := []struct { 1507 name string 1508 lo, hi uint64 1509 exp uint32 1510 }{ 1511 {name: "lo == 0 && hi == 0", lo: 0, hi: 0, exp: 0}, 1512 {name: "lo != 0", lo: 1, exp: 1}, 1513 {name: "hi != 0", hi: 1, exp: 1}, 1514 {name: "lo != 0 && hi != 0", lo: 1, hi: 1, exp: 1}, 1515 } 1516 1517 for _, tc := range tests { 1518 tc := tc 1519 t.Run(tc.name, func(t *testing.T) { 1520 env := newCompilerEnvironment() 1521 compiler := env.requireNewCompiler(t, newCompiler, 1522 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1523 1524 err := compiler.compilePreamble() 1525 require.NoError(t, err) 1526 1527 err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: tc.lo, Hi: tc.hi}) 1528 require.NoError(t, err) 1529 1530 err = compiler.compileV128AnyTrue(&wazeroir.OperationV128AnyTrue{}) 1531 require.NoError(t, err) 1532 1533 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 1534 1535 err = compiler.compileReturnFunction() 1536 require.NoError(t, err) 1537 1538 // Generate and run the code under test. 1539 code, _, err := compiler.compile() 1540 require.NoError(t, err) 1541 env.exec(code) 1542 1543 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 1544 require.Equal(t, uint64(1), env.stackPointer()) 1545 require.Equal(t, tc.exp, env.stackTopAsUint32()) 1546 }) 1547 } 1548 } 1549 1550 func TestCompiler_compileV128AllTrue(t *testing.T) { 1551 tests := []struct { 1552 name string 1553 shape wazeroir.Shape 1554 lo, hi uint64 1555 exp uint32 1556 }{ 1557 { 1558 name: "i8x16 - true", 1559 shape: wazeroir.ShapeI8x16, 1560 lo: 0xffff_ffff_ffff_ffff, 1561 hi: 0x0101_0101_0101_0101, 1562 exp: 1, 1563 }, 1564 { 1565 name: "i8x16 - false on lo", 1566 shape: wazeroir.ShapeI8x16, 1567 lo: 0xffff_ffff_ffff_ffff, 1568 hi: 0x1111_1111_0011_1111, 1569 exp: 0, 1570 }, 1571 { 1572 name: "i8x16 - false on hi", 1573 shape: wazeroir.ShapeI8x16, 1574 lo: 0xffff_00ff_ffff_ffff, 1575 hi: 0x1111_1111_1111_1111, 1576 exp: 0, 1577 }, 1578 { 1579 name: "i16x8 - true", 1580 shape: wazeroir.ShapeI16x8, 1581 lo: 0x1000_0100_0010_0001, 1582 hi: 0x0101_0101_0101_0101, 1583 exp: 1, 1584 }, 1585 { 1586 name: "i16x8 - false on hi", 1587 shape: wazeroir.ShapeI16x8, 1588 lo: 0x1000_0100_0010_0001, 1589 hi: 0x1111_1111_0000_1111, 1590 exp: 0, 1591 }, 1592 { 1593 name: "i16x8 - false on lo", 1594 shape: wazeroir.ShapeI16x8, 1595 lo: 0xffff_0000_ffff_ffff, 1596 hi: 0x1111_1111_1111_1111, 1597 exp: 0, 1598 }, 1599 { 1600 name: "i32x4 - true", 1601 shape: wazeroir.ShapeI32x4, 1602 lo: 0x1000_0000_0010_0000, 1603 hi: 0x0000_0001_0000_1000, 1604 exp: 1, 1605 }, 1606 { 1607 name: "i32x4 - true", 1608 shape: wazeroir.ShapeI32x4, 1609 lo: 0x0000_1111_1111_0000, 1610 hi: 0x0000_0001_1000_0000, 1611 exp: 1, 1612 }, 1613 { 1614 name: "i32x4 - false on lo", 1615 shape: wazeroir.ShapeI32x4, 1616 lo: 0x1111_1111_0000_0000, 1617 hi: 0x1111_1111_1111_1111, 1618 exp: 0, 1619 }, 1620 { 1621 name: "i32x4 - false on lo", 1622 shape: wazeroir.ShapeI32x4, 1623 lo: 0x0000_0000_1111_1111, 1624 hi: 0x1111_1111_1111_1111, 1625 exp: 0, 1626 }, 1627 { 1628 name: "i32x4 - false on hi", 1629 shape: wazeroir.ShapeI32x4, 1630 lo: 0x1111_1111_1111_1111, 1631 hi: 0x1111_1111_0000_0000, 1632 exp: 0, 1633 }, 1634 { 1635 name: "i32x4 - false on hi", 1636 shape: wazeroir.ShapeI32x4, 1637 lo: 0x1111_1111_1111_1111, 1638 hi: 0x0000_0000_1111_1111, 1639 exp: 0, 1640 }, 1641 1642 { 1643 name: "i64x2 - true", 1644 shape: wazeroir.ShapeI64x2, 1645 lo: 0x1000_0000_0000_0000, 1646 hi: 0x0000_0001_0000_0000, 1647 exp: 1, 1648 }, 1649 { 1650 name: "i64x2 - true", 1651 shape: wazeroir.ShapeI64x2, 1652 lo: 0x0000_0000_0010_0000, 1653 hi: 0x0000_0000_0000_0100, 1654 exp: 1, 1655 }, 1656 { 1657 name: "i64x2 - true", 1658 shape: wazeroir.ShapeI64x2, 1659 lo: 0x0000_0000_0000_1000, 1660 hi: 0x1000_0000_0000_0000, 1661 exp: 1, 1662 }, 1663 { 1664 name: "i64x2 - false on lo", 1665 shape: wazeroir.ShapeI64x2, 1666 lo: 0, 1667 hi: 0x1111_1111_1111_1111, 1668 exp: 0, 1669 }, 1670 { 1671 name: "i64x2 - false on hi", 1672 shape: wazeroir.ShapeI64x2, 1673 lo: 0x1111_1111_1111_1111, 1674 hi: 0, 1675 exp: 0, 1676 }, 1677 } 1678 1679 for _, tc := range tests { 1680 tc := tc 1681 t.Run(tc.name, func(t *testing.T) { 1682 env := newCompilerEnvironment() 1683 compiler := env.requireNewCompiler(t, newCompiler, 1684 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1685 1686 err := compiler.compilePreamble() 1687 require.NoError(t, err) 1688 1689 err = compiler.compileV128Const(&wazeroir.OperationV128Const{Lo: tc.lo, Hi: tc.hi}) 1690 require.NoError(t, err) 1691 1692 err = compiler.compileV128AllTrue(&wazeroir.OperationV128AllTrue{Shape: tc.shape}) 1693 require.NoError(t, err) 1694 1695 require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters)) 1696 1697 err = compiler.compileReturnFunction() 1698 require.NoError(t, err) 1699 1700 // Generate and run the code under test. 1701 code, _, err := compiler.compile() 1702 require.NoError(t, err) 1703 env.exec(code) 1704 1705 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 1706 require.Equal(t, uint64(1), env.stackPointer()) 1707 require.Equal(t, tc.exp, env.stackTopAsUint32()) 1708 }) 1709 } 1710 } 1711 1712 func i8ToU8(v int8) byte { 1713 return byte(v) 1714 } 1715 1716 func i16ToU16(v int16) uint16 { 1717 return uint16(v) 1718 } 1719 1720 func i32ToU32(v int32) uint32 { 1721 return uint32(v) 1722 } 1723 1724 func i64ToU64(v int64) uint64 { 1725 return uint64(v) 1726 } 1727 1728 func TestCompiler_compileV128Swizzle(t *testing.T) { 1729 tests := []struct { 1730 name string 1731 indexVec, baseVec [16]byte 1732 expVec [16]byte 1733 }{ 1734 { 1735 name: "1", 1736 baseVec: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, 1737 indexVec: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 1738 expVec: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, 1739 }, 1740 { 1741 name: "2", 1742 baseVec: [16]byte{ 1743 i8ToU8(-16), i8ToU8(-15), i8ToU8(-14), i8ToU8(-13), i8ToU8(-12), 1744 i8ToU8(-11), i8ToU8(-10), i8ToU8(-9), i8ToU8(-8), i8ToU8(-7), i8ToU8(-6), i8ToU8(-5), 1745 i8ToU8(-4), i8ToU8(-3), i8ToU8(-2), i8ToU8(-1), 1746 }, 1747 indexVec: [16]byte{ 1748 i8ToU8(-8), i8ToU8(-7), i8ToU8(-6), i8ToU8(-5), i8ToU8(-4), 1749 i8ToU8(-3), i8ToU8(-2), i8ToU8(-1), 16, 17, 18, 19, 20, 21, 22, 23, 1750 }, 1751 expVec: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1752 }, 1753 { 1754 name: "3", 1755 baseVec: [16]byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}, 1756 indexVec: [16]byte{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, 1757 expVec: [16]byte{115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100}, 1758 }, 1759 { 1760 name: "4", 1761 baseVec: [16]byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}, 1762 indexVec: [16]byte{ 1763 9, 16, 10, 17, 11, 18, 12, 19, 13, 20, 14, 21, 15, 22, 16, 23, 1764 }, 1765 expVec: [16]byte{109, 0, 110, 0, 111, 0, 112, 0, 113, 0, 114, 0, 115, 0, 0, 0}, 1766 }, 1767 { 1768 name: "5", 1769 baseVec: [16]byte{0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73}, 1770 indexVec: [16]byte{9, 16, 10, 17, 11, 18, 12, 19, 13, 20, 14, 21, 15, 22, 16, 23}, 1771 expVec: [16]byte{0x6d, 0, 0x6e, 0, 0x6f, 0, 0x70, 0, 0x71, 0, 0x72, 0, 0x73, 0, 0, 0}, 1772 }, 1773 } 1774 1775 for _, tc := range tests { 1776 tc := tc 1777 t.Run(tc.name, func(t *testing.T) { 1778 env := newCompilerEnvironment() 1779 compiler := env.requireNewCompiler(t, newCompiler, 1780 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1781 1782 err := compiler.compilePreamble() 1783 require.NoError(t, err) 1784 1785 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 1786 Lo: binary.LittleEndian.Uint64(tc.baseVec[:8]), 1787 Hi: binary.LittleEndian.Uint64(tc.baseVec[8:]), 1788 }) 1789 require.NoError(t, err) 1790 1791 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 1792 Lo: binary.LittleEndian.Uint64(tc.indexVec[:8]), 1793 Hi: binary.LittleEndian.Uint64(tc.indexVec[8:]), 1794 }) 1795 require.NoError(t, err) 1796 1797 err = compiler.compileV128Swizzle(&wazeroir.OperationV128Swizzle{}) 1798 require.NoError(t, err) 1799 1800 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 1801 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 1802 1803 err = compiler.compileReturnFunction() 1804 require.NoError(t, err) 1805 1806 // Generate and run the code under test. 1807 code, _, err := compiler.compile() 1808 require.NoError(t, err) 1809 env.exec(code) 1810 1811 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 1812 1813 lo, hi := env.stackTopAsV128() 1814 var actual [16]byte 1815 binary.LittleEndian.PutUint64(actual[:8], lo) 1816 binary.LittleEndian.PutUint64(actual[8:], hi) 1817 require.Equal(t, tc.expVec, actual) 1818 }) 1819 } 1820 } 1821 1822 func TestCompiler_compileV128Shuffle(t *testing.T) { 1823 tests := []struct { 1824 name string 1825 lanes, w, v, exp [16]byte 1826 }{ 1827 { 1828 name: "v only", 1829 lanes: [16]byte{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0}, 1830 v: [16]byte{0: 0xa, 1: 0xb, 10: 0xc}, 1831 w: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 1832 exp: [16]byte{ 1833 0xb, 0xb, 0xb, 0xb, 1834 0xa, 0xa, 0xa, 0xa, 1835 0xc, 0xc, 0xc, 0xc, 1836 0xa, 0xa, 0xa, 0xa, 1837 }, 1838 }, 1839 { 1840 name: "w only", 1841 lanes: [16]byte{17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16}, 1842 v: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 1843 w: [16]byte{0: 0xa, 1: 0xb, 10: 0xc}, 1844 exp: [16]byte{ 1845 0xb, 0xb, 0xb, 0xb, 1846 0xa, 0xa, 0xa, 0xa, 1847 0xc, 0xc, 0xc, 0xc, 1848 0xa, 0xa, 0xa, 0xa, 1849 }, 1850 }, 1851 { 1852 name: "mix", 1853 lanes: [16]byte{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}, 1854 v: [16]byte{ 1855 0x1, 0xff, 0x2, 0xff, 0x3, 0xff, 0x4, 0xff, 1856 0x5, 0xff, 0x6, 0xff, 0x7, 0xff, 0x8, 0xff, 1857 }, 1858 w: [16]byte{ 1859 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 1860 0xff, 0x15, 0xff, 0x16, 0xff, 0x17, 0xff, 0x18, 1861 }, 1862 exp: [16]byte{ 1863 0x1, 0x11, 0x2, 0x12, 0x3, 0x13, 0x4, 0x14, 1864 0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18, 1865 }, 1866 }, 1867 { 1868 name: "mix", 1869 lanes: [16]byte{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}, 1870 v: [16]byte{ 1871 0x1, 0xff, 0x2, 0xff, 0x3, 0xff, 0x4, 0xff, 1872 0x5, 0xff, 0x6, 0xff, 0x7, 0xff, 0x8, 0xff, 1873 }, 1874 w: [16]byte{ 1875 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14, 1876 0xff, 0x15, 0xff, 0x16, 0xff, 0x17, 0xff, 0x18, 1877 }, 1878 exp: [16]byte{ 1879 0x1, 0x11, 0x2, 0x12, 0x3, 0x13, 0x4, 0x14, 1880 0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18, 1881 }, 1882 }, 1883 } 1884 1885 for _, tc := range tests { 1886 tc := tc 1887 t.Run(tc.name, func(t *testing.T) { 1888 env := newCompilerEnvironment() 1889 compiler := env.requireNewCompiler(t, newCompiler, 1890 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 1891 1892 err := compiler.compilePreamble() 1893 require.NoError(t, err) 1894 1895 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 1896 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 1897 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 1898 }) 1899 require.NoError(t, err) 1900 1901 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 1902 Lo: binary.LittleEndian.Uint64(tc.w[:8]), 1903 Hi: binary.LittleEndian.Uint64(tc.w[8:]), 1904 }) 1905 require.NoError(t, err) 1906 1907 err = compiler.compileV128Shuffle(&wazeroir.OperationV128Shuffle{Lanes: tc.lanes}) 1908 require.NoError(t, err) 1909 1910 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 1911 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 1912 1913 err = compiler.compileReturnFunction() 1914 require.NoError(t, err) 1915 1916 // Generate and run the code under test. 1917 code, _, err := compiler.compile() 1918 require.NoError(t, err) 1919 env.exec(code) 1920 1921 lo, hi := env.stackTopAsV128() 1922 var actual [16]byte 1923 binary.LittleEndian.PutUint64(actual[:8], lo) 1924 binary.LittleEndian.PutUint64(actual[8:], hi) 1925 require.Equal(t, tc.exp, actual) 1926 }) 1927 } 1928 } 1929 1930 func TestCompiler_compileV128Bitmask(t *testing.T) { 1931 u16x8 := func(u1, u2, u3, u4, u5, u6, u7, u8 uint16) (ret [16]byte) { 1932 binary.LittleEndian.PutUint16(ret[0:], u1) 1933 binary.LittleEndian.PutUint16(ret[2:], u2) 1934 binary.LittleEndian.PutUint16(ret[4:], u3) 1935 binary.LittleEndian.PutUint16(ret[6:], u4) 1936 binary.LittleEndian.PutUint16(ret[8:], u5) 1937 binary.LittleEndian.PutUint16(ret[10:], u6) 1938 binary.LittleEndian.PutUint16(ret[12:], u7) 1939 binary.LittleEndian.PutUint16(ret[14:], u8) 1940 return 1941 } 1942 u32x4 := func(u1, u2, u3, u4 uint32) (ret [16]byte) { 1943 binary.LittleEndian.PutUint32(ret[0:], u1) 1944 binary.LittleEndian.PutUint32(ret[4:], u2) 1945 binary.LittleEndian.PutUint32(ret[8:], u3) 1946 binary.LittleEndian.PutUint32(ret[12:], u4) 1947 return 1948 } 1949 u64x2 := func(u1, u2 uint64) (ret [16]byte) { 1950 binary.LittleEndian.PutUint64(ret[0:], u1) 1951 binary.LittleEndian.PutUint64(ret[8:], u2) 1952 return 1953 } 1954 1955 tests := []struct { 1956 name string 1957 shape wazeroir.Shape 1958 v [16]byte 1959 exp uint32 1960 }{ 1961 { 1962 name: wasm.OpcodeVecI8x16BitMaskName, 1963 v: [16]byte{ 1964 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, 1965 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, 1966 }, 1967 shape: wazeroir.ShapeI8x16, 1968 exp: 0b0101_0101_0101_0101, 1969 }, 1970 { 1971 name: wasm.OpcodeVecI8x16BitMaskName, 1972 v: [16]byte{ 1973 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, 1974 0, 0, 0, 0, 0, 0, 0, 0, 1975 }, 1976 shape: wazeroir.ShapeI8x16, 1977 exp: 0b0000_0000_0101_0101, 1978 }, 1979 { 1980 name: wasm.OpcodeVecI8x16BitMaskName, 1981 v: [16]byte{ 1982 0, 0, 0, 0, 0, 0, 0, 0, 1983 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, 1984 }, 1985 shape: wazeroir.ShapeI8x16, 1986 exp: 0b0101_0101_0000_0000, 1987 }, 1988 { 1989 name: wasm.OpcodeVecI16x8BitMaskName, 1990 v: u16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 1991 shape: wazeroir.ShapeI16x8, 1992 exp: 0b1111_1111, 1993 }, 1994 { 1995 name: wasm.OpcodeVecI16x8BitMaskName, 1996 v: u16x8(0, 0xffff, 0, 0xffff, 0, 0xffff, 0, 0xffff), 1997 shape: wazeroir.ShapeI16x8, 1998 exp: 0b1010_1010, 1999 }, 2000 { 2001 name: wasm.OpcodeVecI32x4BitMaskName, 2002 v: u32x4(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff), 2003 shape: wazeroir.ShapeI32x4, 2004 exp: 0b1111, 2005 }, 2006 { 2007 name: wasm.OpcodeVecI32x4BitMaskName, 2008 v: u32x4(0, 0xffffffff, 0xffffffff, 0), 2009 shape: wazeroir.ShapeI32x4, 2010 exp: 0b0110, 2011 }, 2012 { 2013 name: wasm.OpcodeVecI64x2BitMaskName, 2014 v: u64x2(0, 0xffffffffffffffff), 2015 shape: wazeroir.ShapeI64x2, 2016 exp: 0b10, 2017 }, 2018 { 2019 name: wasm.OpcodeVecI64x2BitMaskName, 2020 v: u64x2(0xffffffffffffffff, 0xffffffffffffffff), 2021 shape: wazeroir.ShapeI64x2, 2022 exp: 0b11, 2023 }, 2024 } 2025 2026 for _, tc := range tests { 2027 tc := tc 2028 t.Run(tc.name, func(t *testing.T) { 2029 env := newCompilerEnvironment() 2030 compiler := env.requireNewCompiler(t, newCompiler, 2031 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 2032 2033 err := compiler.compilePreamble() 2034 require.NoError(t, err) 2035 2036 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2037 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 2038 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 2039 }) 2040 require.NoError(t, err) 2041 2042 err = compiler.compileV128BitMask(&wazeroir.OperationV128BitMask{Shape: tc.shape}) 2043 require.NoError(t, err) 2044 2045 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 2046 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 2047 2048 err = compiler.compileReturnFunction() 2049 require.NoError(t, err) 2050 2051 // Generate and run the code under test. 2052 2053 code, _, err := compiler.compile() 2054 require.NoError(t, err) 2055 env.exec(code) 2056 2057 actual := env.stackTopAsUint32() 2058 require.Equal(t, tc.exp, actual) 2059 }) 2060 } 2061 } 2062 2063 func TestCompiler_compileV128_Not(t *testing.T) { 2064 env := newCompilerEnvironment() 2065 compiler := env.requireNewCompiler(t, newCompiler, 2066 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 2067 2068 err := compiler.compilePreamble() 2069 require.NoError(t, err) 2070 2071 var originalLo, originalHi uint64 = 0xffff_0000_ffff_0000, 0x0000_ffff_0000_ffff 2072 2073 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2074 Lo: originalLo, 2075 Hi: originalHi, 2076 }) 2077 require.NoError(t, err) 2078 2079 err = compiler.compileV128Not(&wazeroir.OperationV128Not{}) 2080 require.NoError(t, err) 2081 2082 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 2083 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 2084 2085 err = compiler.compileReturnFunction() 2086 require.NoError(t, err) 2087 2088 // Generate and run the code under test. 2089 code, _, err := compiler.compile() 2090 require.NoError(t, err) 2091 env.exec(code) 2092 2093 lo, hi := env.stackTopAsV128() 2094 require.Equal(t, ^originalLo, lo) 2095 require.Equal(t, ^originalHi, hi) 2096 } 2097 2098 func TestCompiler_compileV128_And_Or_Xor_AndNot(t *testing.T) { 2099 tests := []struct { 2100 name string 2101 op wazeroir.OperationKind 2102 x1, x2, exp [16]byte 2103 }{ 2104 { 2105 name: "AND", 2106 op: wazeroir.OperationKindV128And, 2107 x1: [16]byte{ 2108 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2109 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2110 }, 2111 x2: [16]byte{}, 2112 exp: [16]byte{}, 2113 }, 2114 { 2115 name: "AND", 2116 op: wazeroir.OperationKindV128And, 2117 x2: [16]byte{ 2118 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2119 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2120 }, 2121 x1: [16]byte{}, 2122 exp: [16]byte{}, 2123 }, 2124 { 2125 name: "AND", 2126 op: wazeroir.OperationKindV128And, 2127 x2: [16]byte{ 2128 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2129 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2130 }, 2131 x1: [16]byte{0: 0x1, 5: 0x1, 15: 0x1}, 2132 exp: [16]byte{0: 0x1, 5: 0x1, 15: 0x1}, 2133 }, 2134 { 2135 name: "OR", 2136 op: wazeroir.OperationKindV128Or, 2137 x1: [16]byte{ 2138 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2139 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2140 }, 2141 x2: [16]byte{}, 2142 exp: [16]byte{ 2143 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2144 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2145 }, 2146 }, 2147 { 2148 name: "OR", 2149 op: wazeroir.OperationKindV128Or, 2150 x2: [16]byte{ 2151 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2152 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2153 }, 2154 x1: [16]byte{}, 2155 exp: [16]byte{ 2156 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2157 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2158 }, 2159 }, 2160 { 2161 name: "OR", 2162 op: wazeroir.OperationKindV128Or, 2163 x2: [16]byte{}, 2164 x1: [16]byte{0: 0x1, 5: 0x1, 15: 0x1}, 2165 exp: [16]byte{0: 0x1, 5: 0x1, 15: 0x1}, 2166 }, 2167 { 2168 name: "OR", 2169 op: wazeroir.OperationKindV128Or, 2170 x2: [16]byte{8: 0x1, 10: 0x1}, 2171 x1: [16]byte{0: 0x1}, 2172 exp: [16]byte{0: 0x1, 8: 0x1, 10: 0x1}, 2173 }, 2174 { 2175 name: "XOR", 2176 op: wazeroir.OperationKindV128Xor, 2177 x1: [16]byte{ 2178 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2179 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2180 }, 2181 x2: [16]byte{}, 2182 exp: [16]byte{ 2183 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2184 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2185 }, 2186 }, 2187 { 2188 name: "XOR", 2189 op: wazeroir.OperationKindV128Xor, 2190 x2: [16]byte{ 2191 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2192 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2193 }, 2194 x1: [16]byte{}, 2195 exp: [16]byte{ 2196 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2197 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2198 }, 2199 }, 2200 { 2201 name: "XOR", 2202 op: wazeroir.OperationKindV128Xor, 2203 x2: [16]byte{ 2204 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2205 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2206 }, 2207 x1: [16]byte{ 2208 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2209 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2210 }, 2211 exp: [16]byte{}, 2212 }, 2213 { 2214 name: "XOR", 2215 op: wazeroir.OperationKindV128Xor, 2216 x2: [16]byte{ 2217 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2218 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2219 }, 2220 x1: [16]byte{0: 0x1, 15: 0x2}, 2221 exp: [16]byte{ 2222 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2223 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 2224 }, 2225 }, 2226 2227 { 2228 name: "AndNot", 2229 op: wazeroir.OperationKindV128AndNot, 2230 x2: [16]byte{}, 2231 x1: [16]byte{ 2232 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2233 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2234 }, 2235 exp: [16]byte{ 2236 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2237 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2238 }, 2239 }, 2240 { 2241 name: "AndNot", 2242 op: wazeroir.OperationKindV128AndNot, 2243 x2: [16]byte{ 2244 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2245 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2246 }, 2247 x1: [16]byte{}, 2248 exp: [16]byte{}, 2249 }, 2250 { 2251 name: "AndNot", 2252 op: wazeroir.OperationKindV128AndNot, 2253 x2: [16]byte{ 2254 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2255 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2256 }, 2257 x1: [16]byte{ 2258 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2259 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2260 }, 2261 exp: [16]byte{}, 2262 }, 2263 { 2264 name: "AndNot", 2265 op: wazeroir.OperationKindV128AndNot, 2266 x2: [16]byte{ 2267 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2268 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 2269 }, 2270 x1: [16]byte{0: 0x1, 15: 0x2}, 2271 exp: [16]byte{0: 0x1, 15: 0x2}, 2272 }, 2273 } 2274 2275 for _, tc := range tests { 2276 tc := tc 2277 t.Run(tc.name, func(t *testing.T) { 2278 env := newCompilerEnvironment() 2279 compiler := env.requireNewCompiler(t, newCompiler, 2280 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 2281 2282 err := compiler.compilePreamble() 2283 require.NoError(t, err) 2284 2285 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2286 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 2287 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 2288 }) 2289 require.NoError(t, err) 2290 2291 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2292 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 2293 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 2294 }) 2295 require.NoError(t, err) 2296 2297 switch tc.op { 2298 case wazeroir.OperationKindV128And: 2299 err = compiler.compileV128And(nil) // And doesn't use the param. 2300 case wazeroir.OperationKindV128Or: 2301 err = compiler.compileV128Or(nil) // Or doesn't use the param. 2302 case wazeroir.OperationKindV128Xor: 2303 err = compiler.compileV128Xor(nil) // Xor doesn't use the param. 2304 case wazeroir.OperationKindV128AndNot: 2305 err = compiler.compileV128AndNot(nil) // AndNot doesn't use the param. 2306 } 2307 require.NoError(t, err) 2308 2309 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 2310 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 2311 2312 err = compiler.compileReturnFunction() 2313 require.NoError(t, err) 2314 2315 // Generate and run the code under test. 2316 code, _, err := compiler.compile() 2317 require.NoError(t, err) 2318 env.exec(code) 2319 2320 lo, hi := env.stackTopAsV128() 2321 var actual [16]byte 2322 binary.LittleEndian.PutUint64(actual[:8], lo) 2323 binary.LittleEndian.PutUint64(actual[8:], hi) 2324 require.Equal(t, tc.exp, actual) 2325 }) 2326 } 2327 } 2328 2329 func TestCompiler_compileV128Bitselect(t *testing.T) { 2330 tests := []struct { 2331 name string 2332 selector, x1, x2, exp [16]byte 2333 }{ 2334 { 2335 name: "all x1", 2336 selector: [16]byte{ 2337 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2338 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2339 }, 2340 x1: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 2341 x2: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, 2342 exp: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 2343 }, 2344 { 2345 name: "all x2", 2346 selector: [16]byte{}, 2347 x1: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 2348 x2: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, 2349 exp: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, 2350 }, 2351 { 2352 name: "mix", 2353 selector: [16]byte{ 2354 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 2355 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111, 2356 }, 2357 x1: [16]byte{ 2358 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 2359 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 2360 }, 2361 x2: [16]byte{ 2362 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 2363 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 2364 }, 2365 exp: [16]byte{ 2366 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 2367 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 2368 }, 2369 }, 2370 } 2371 2372 for _, tc := range tests { 2373 tc := tc 2374 t.Run(tc.name, func(t *testing.T) { 2375 env := newCompilerEnvironment() 2376 compiler := env.requireNewCompiler(t, newCompiler, 2377 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 2378 2379 err := compiler.compilePreamble() 2380 require.NoError(t, err) 2381 2382 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2383 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 2384 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 2385 }) 2386 require.NoError(t, err) 2387 2388 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2389 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 2390 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 2391 }) 2392 require.NoError(t, err) 2393 2394 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2395 Lo: binary.LittleEndian.Uint64(tc.selector[:8]), 2396 Hi: binary.LittleEndian.Uint64(tc.selector[8:]), 2397 }) 2398 require.NoError(t, err) 2399 2400 err = compiler.compileV128Bitselect(nil) 2401 require.NoError(t, err) 2402 2403 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 2404 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 2405 2406 err = compiler.compileReturnFunction() 2407 require.NoError(t, err) 2408 2409 // Generate and run the code under test. 2410 code, _, err := compiler.compile() 2411 require.NoError(t, err) 2412 env.exec(code) 2413 2414 lo, hi := env.stackTopAsV128() 2415 var actual [16]byte 2416 binary.LittleEndian.PutUint64(actual[:8], lo) 2417 binary.LittleEndian.PutUint64(actual[8:], hi) 2418 require.Equal(t, tc.exp, actual) 2419 }) 2420 } 2421 } 2422 2423 func TestCompiler_compileV128Shl(t *testing.T) { 2424 tests := []struct { 2425 name string 2426 shape wazeroir.Shape 2427 s uint32 2428 x, exp [16]byte 2429 }{ 2430 { 2431 name: "i8x16/shift=0", 2432 shape: wazeroir.ShapeI8x16, 2433 x: [16]byte{ 2434 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2435 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2436 }, 2437 exp: [16]byte{ 2438 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2439 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2440 }, 2441 s: 0, 2442 }, 2443 { 2444 name: "i8x16/shift=1", 2445 shape: wazeroir.ShapeI8x16, 2446 x: [16]byte{ 2447 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 2448 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 2449 }, 2450 exp: [16]byte{ 2451 2, 0xfe, 2, 0xfe, 2, 0xfe, 2, 0xfe, 2452 2, 0xfe, 2, 0xfe, 2, 0xfe, 2, 0xfe, 2453 }, 2454 s: 1, 2455 }, 2456 { 2457 name: "i8x16/shift=2", 2458 shape: wazeroir.ShapeI8x16, 2459 x: [16]byte{ 2460 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2461 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2462 }, 2463 exp: [16]byte{ 2464 4, 0, 4, 0, 4, 0, 4, 0, 2465 4, 0, 4, 0, 4, 0, 4, 0, 2466 }, 2467 s: 2, 2468 }, 2469 { 2470 name: "i8x16/shift=3", 2471 shape: wazeroir.ShapeI8x16, 2472 x: [16]byte{ 2473 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 2474 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 2475 }, 2476 exp: [16]byte{ 2477 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111, 2478 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111, 2479 }, 2480 s: 3, 2481 }, 2482 { 2483 name: "i8x16/shift=4", 2484 shape: wazeroir.ShapeI8x16, 2485 x: [16]byte{ 2486 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 2487 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 2488 }, 2489 exp: [16]byte{ 2490 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 2491 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 2492 }, 2493 s: 4, 2494 }, 2495 { 2496 name: "i8x16/shift=5", 2497 shape: wazeroir.ShapeI8x16, 2498 x: [16]byte{ 2499 0xff, 0xff, 0xff, 0xff, 1, 1, 1, 1, 2500 0xff, 0xff, 0xff, 0xff, 1, 1, 1, 1, 2501 }, 2502 exp: [16]byte{ 2503 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 32, 32, 32, 32, 2504 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 32, 32, 32, 32, 2505 }, 2506 s: 5, 2507 }, 2508 { 2509 name: "i8x16/shift=6", 2510 shape: wazeroir.ShapeI8x16, 2511 x: [16]byte{ 2512 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 2513 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 2514 }, 2515 exp: [16]byte{ 2516 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6, 2517 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6, 2518 }, 2519 s: 6, 2520 }, 2521 { 2522 name: "i8x16/shift=7", 2523 shape: wazeroir.ShapeI8x16, 2524 x: [16]byte{ 2525 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2526 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2527 }, 2528 exp: [16]byte{ 2529 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 2530 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 2531 }, 2532 s: 7, 2533 }, 2534 { 2535 name: "i16x8/shift=0", 2536 shape: wazeroir.ShapeI16x8, 2537 x: [16]byte{ 2538 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2539 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2540 }, 2541 exp: [16]byte{ 2542 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2543 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2544 }, 2545 s: 0, 2546 }, 2547 { 2548 name: "i16x8/shift=1", 2549 shape: wazeroir.ShapeI16x8, 2550 x: [16]byte{ 2551 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2552 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2553 }, 2554 exp: [16]byte{ 2555 2, 0, 2, 0, 2, 0, 2, 0, 2556 2, 0, 2, 0, 2, 0, 2, 0, 2557 }, 2558 s: 1, 2559 }, 2560 { 2561 name: "i16x8/shift=7", 2562 shape: wazeroir.ShapeI16x8, 2563 x: [16]byte{ 2564 1, 1, 1, 1, 0x80, 0x80, 0x80, 0x80, 2565 0, 0x80, 0, 0x80, 0b11, 0b11, 0b11, 0b11, 2566 }, 2567 exp: [16]byte{ 2568 0, 1, 0, 1, 0, 0x80, 0, 0x80, 2569 0, 0, 0, 0, 0, 0b11, 0, 0b11, 2570 }, 2571 s: 8, 2572 }, 2573 { 2574 name: "i16x8/shift=15", 2575 shape: wazeroir.ShapeI16x8, 2576 x: [16]byte{ 2577 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2578 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2579 }, 2580 exp: [16]byte{ 2581 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 2582 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 2583 }, 2584 s: 15, 2585 }, 2586 { 2587 name: "i32x4/shift=0", 2588 shape: wazeroir.ShapeI32x4, 2589 x: [16]byte{ 2590 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2591 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2592 }, 2593 exp: [16]byte{ 2594 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2595 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2596 }, 2597 s: 0, 2598 }, 2599 { 2600 name: "i32x4/shift=1", 2601 shape: wazeroir.ShapeI32x4, 2602 x: [16]byte{ 2603 1, 0x80, 0, 0x80, 1, 0x80, 0, 0x80, 2604 1, 0x80, 0, 0x80, 1, 0x80, 0, 0x80, 2605 }, 2606 exp: [16]byte{ 2607 2, 0, 1, 0, 2, 0, 1, 0, 2608 2, 0, 1, 0, 2, 0, 1, 0, 2609 }, 2610 s: 1, 2611 }, 2612 { 2613 name: "i32x4/shift=31", 2614 shape: wazeroir.ShapeI32x4, 2615 x: [16]byte{ 2616 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2617 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2618 }, 2619 exp: [16]byte{ 2620 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2621 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2622 }, 2623 s: 31, 2624 }, 2625 { 2626 name: "i64x2/shift=0", 2627 shape: wazeroir.ShapeI64x2, 2628 x: [16]byte{ 2629 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2630 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2631 }, 2632 exp: [16]byte{ 2633 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2634 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2635 }, 2636 s: 0, 2637 }, 2638 { 2639 name: "i64x2/shift=5", 2640 shape: wazeroir.ShapeI64x2, 2641 x: [16]byte{ 2642 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2643 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2644 }, 2645 exp: [16]byte{ 2646 1 << 5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0, 2647 1 << 5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0, 2648 }, 2649 s: 5, 2650 }, 2651 { 2652 name: "i64x2/shift=63", 2653 shape: wazeroir.ShapeI64x2, 2654 x: [16]byte{ 2655 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2656 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 2657 }, 2658 exp: [16]byte{ 2659 0, 0, 0, 0, 0, 0, 0, 0x80, 2660 0, 0, 0, 0, 0, 0, 0, 0x80, 2661 }, 2662 s: 63, 2663 }, 2664 } 2665 2666 for _, tc := range tests { 2667 tc := tc 2668 t.Run(tc.name, func(t *testing.T) { 2669 env := newCompilerEnvironment() 2670 compiler := env.requireNewCompiler(t, newCompiler, 2671 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 2672 2673 err := compiler.compilePreamble() 2674 require.NoError(t, err) 2675 2676 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2677 Lo: binary.LittleEndian.Uint64(tc.x[:8]), 2678 Hi: binary.LittleEndian.Uint64(tc.x[8:]), 2679 }) 2680 require.NoError(t, err) 2681 2682 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.s}) 2683 require.NoError(t, err) 2684 2685 err = compiler.compileV128Shl(&wazeroir.OperationV128Shl{Shape: tc.shape}) 2686 require.NoError(t, err) 2687 2688 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 2689 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 2690 2691 err = compiler.compileReturnFunction() 2692 require.NoError(t, err) 2693 2694 // Generate and run the code under test. 2695 code, _, err := compiler.compile() 2696 require.NoError(t, err) 2697 env.exec(code) 2698 2699 lo, hi := env.stackTopAsV128() 2700 var actual [16]byte 2701 binary.LittleEndian.PutUint64(actual[:8], lo) 2702 binary.LittleEndian.PutUint64(actual[8:], hi) 2703 require.Equal(t, tc.exp, actual) 2704 }) 2705 } 2706 } 2707 2708 func TestCompiler_compileV128Shr(t *testing.T) { 2709 tests := []struct { 2710 name string 2711 signed bool 2712 shape wazeroir.Shape 2713 s uint32 2714 x, exp [16]byte 2715 }{ 2716 { 2717 name: "i8x16/shift=0/signed=false", 2718 shape: wazeroir.ShapeI8x16, 2719 x: [16]byte{ 2720 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2721 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2722 }, 2723 exp: [16]byte{ 2724 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2725 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2726 }, 2727 s: 0, 2728 signed: false, 2729 }, 2730 { 2731 name: "i8x16/shift=7/signed=false", 2732 shape: wazeroir.ShapeI8x16, 2733 x: [16]byte{ 2734 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 2735 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 2736 }, 2737 exp: [16]byte{ 2738 1, 1, 1, 1, 1, 1, 1, 1, 2739 1, 1, 1, 1, 1, 1, 1, 1, 2740 }, 2741 s: 7, 2742 signed: false, 2743 }, 2744 { 2745 name: "i8x16/shift=0/signed=false", 2746 shape: wazeroir.ShapeI8x16, 2747 x: [16]byte{ 2748 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2749 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2750 }, 2751 exp: [16]byte{ 2752 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2753 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2754 }, 2755 s: 0, 2756 signed: true, 2757 }, 2758 { 2759 name: "i8x16/shift=7/signed=false", 2760 shape: wazeroir.ShapeI8x16, 2761 x: [16]byte{ 2762 1, 0x80, 0x7e, 0x80, 1, 0x80, 0x7e, 0x80, 2763 1, 0x80, 0x7e, 0x80, 1, 0x80, 0x7e, 0x80, 2764 }, 2765 exp: [16]byte{ 2766 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 2767 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff, 2768 }, 2769 s: 7, 2770 signed: true, 2771 }, 2772 { 2773 name: "i16x8/shift=0/signed=false", 2774 shape: wazeroir.ShapeI16x8, 2775 x: [16]byte{ 2776 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2777 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2778 }, 2779 exp: [16]byte{ 2780 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2781 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2782 }, 2783 s: 0, 2784 signed: false, 2785 }, 2786 { 2787 name: "i16x8/shift=8/signed=false", 2788 shape: wazeroir.ShapeI16x8, 2789 x: [16]byte{ 2790 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 2791 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 2792 }, 2793 exp: [16]byte{ 2794 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 2795 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0, 2796 }, 2797 s: 8, 2798 signed: false, 2799 }, 2800 { 2801 name: "i16x8/shift=0/signed=true", 2802 shape: wazeroir.ShapeI16x8, 2803 x: [16]byte{ 2804 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2805 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2806 }, 2807 exp: [16]byte{ 2808 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2809 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2810 }, 2811 s: 0, 2812 signed: true, 2813 }, 2814 { 2815 name: "i16x8/shift=8/signed=true", 2816 shape: wazeroir.ShapeI16x8, 2817 x: [16]byte{ 2818 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 2819 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 2820 }, 2821 exp: [16]byte{ 2822 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 2823 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 2824 }, 2825 s: 8, 2826 signed: true, 2827 }, 2828 { 2829 name: "i32x4/shift=0/signed=false", 2830 shape: wazeroir.ShapeI32x4, 2831 x: [16]byte{ 2832 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2833 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2834 }, 2835 exp: [16]byte{ 2836 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2837 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2838 }, 2839 s: 0, 2840 signed: false, 2841 }, 2842 { 2843 name: "i32x4/shift=16/signed=false", 2844 shape: wazeroir.ShapeI32x4, 2845 x: [16]byte{ 2846 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2847 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2848 }, 2849 exp: [16]byte{ 2850 0, 0x80, 0, 0, 0, 0x80, 0, 0, 2851 0, 0x80, 0, 0, 0, 0x80, 0, 0, 2852 }, 2853 s: 16, 2854 signed: false, 2855 }, 2856 { 2857 name: "i32x4/shift=0/signed=true", 2858 shape: wazeroir.ShapeI32x4, 2859 x: [16]byte{ 2860 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2861 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2862 }, 2863 exp: [16]byte{ 2864 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2865 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2866 }, 2867 s: 0, 2868 signed: true, 2869 }, 2870 { 2871 name: "i32x4/shift=16/signed=true", 2872 shape: wazeroir.ShapeI32x4, 2873 x: [16]byte{ 2874 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2875 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2876 }, 2877 exp: [16]byte{ 2878 0, 0x80, 0xff, 0xff, 0, 0x80, 0xff, 0xff, 2879 0, 0x80, 0xff, 0xff, 0, 0x80, 0xff, 0xff, 2880 }, 2881 s: 16, 2882 signed: true, 2883 }, 2884 { 2885 name: "i64x2/shift=0/signed=false", 2886 shape: wazeroir.ShapeI32x4, 2887 x: [16]byte{ 2888 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2889 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2890 }, 2891 exp: [16]byte{ 2892 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2893 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2894 }, 2895 s: 0, 2896 signed: false, 2897 }, 2898 { 2899 name: "i64x2/shift=16/signed=false", 2900 shape: wazeroir.ShapeI64x2, 2901 x: [16]byte{ 2902 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2903 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2904 }, 2905 exp: [16]byte{ 2906 0, 0x80, 0, 0, 0, 0x80, 0, 0, 2907 0, 0x80, 0, 0, 0, 0x80, 0, 0, 2908 }, 2909 s: 16, 2910 signed: false, 2911 }, 2912 { 2913 name: "i64x2/shift=0/signed=true", 2914 shape: wazeroir.ShapeI64x2, 2915 x: [16]byte{ 2916 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2917 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2918 }, 2919 exp: [16]byte{ 2920 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2921 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80, 2922 }, 2923 s: 0, 2924 signed: true, 2925 }, 2926 { 2927 name: "i64x2/shift=16/signed=true", 2928 shape: wazeroir.ShapeI64x2, 2929 x: [16]byte{ 2930 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2931 0, 0, 0, 0x80, 0, 0, 0, 0x80, 2932 }, 2933 exp: [16]byte{ 2934 0, 0x80, 0, 0, 0, 0x80, 0xff, 0xff, 2935 0, 0x80, 0, 0, 0, 0x80, 0xff, 0xff, 2936 }, 2937 s: 16, 2938 signed: true, 2939 }, 2940 } 2941 2942 for _, tc := range tests { 2943 tc := tc 2944 t.Run(tc.name, func(t *testing.T) { 2945 env := newCompilerEnvironment() 2946 compiler := env.requireNewCompiler(t, newCompiler, 2947 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 2948 2949 err := compiler.compilePreamble() 2950 require.NoError(t, err) 2951 2952 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 2953 Lo: binary.LittleEndian.Uint64(tc.x[:8]), 2954 Hi: binary.LittleEndian.Uint64(tc.x[8:]), 2955 }) 2956 require.NoError(t, err) 2957 2958 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: tc.s}) 2959 require.NoError(t, err) 2960 2961 err = compiler.compileV128Shr(&wazeroir.OperationV128Shr{Shape: tc.shape, Signed: tc.signed}) 2962 require.NoError(t, err) 2963 2964 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 2965 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 2966 2967 err = compiler.compileReturnFunction() 2968 require.NoError(t, err) 2969 2970 // Generate and run the code under test. 2971 code, _, err := compiler.compile() 2972 require.NoError(t, err) 2973 env.exec(code) 2974 2975 lo, hi := env.stackTopAsV128() 2976 var actual [16]byte 2977 binary.LittleEndian.PutUint64(actual[:8], lo) 2978 binary.LittleEndian.PutUint64(actual[8:], hi) 2979 require.Equal(t, tc.exp, actual) 2980 }) 2981 } 2982 } 2983 2984 func i16x8(i1, i2, i3, i4, i5, i6, i7, i8 uint16) (ret [16]byte) { 2985 binary.LittleEndian.PutUint16(ret[0:], i1) 2986 binary.LittleEndian.PutUint16(ret[2:], i2) 2987 binary.LittleEndian.PutUint16(ret[4:], i3) 2988 binary.LittleEndian.PutUint16(ret[6:], i4) 2989 binary.LittleEndian.PutUint16(ret[8:], i5) 2990 binary.LittleEndian.PutUint16(ret[10:], i6) 2991 binary.LittleEndian.PutUint16(ret[12:], i7) 2992 binary.LittleEndian.PutUint16(ret[14:], i8) 2993 return 2994 } 2995 2996 func i32x4(i1, i2, i3, i4 uint32) (ret [16]byte) { 2997 binary.LittleEndian.PutUint32(ret[0:], i1) 2998 binary.LittleEndian.PutUint32(ret[4:], i2) 2999 binary.LittleEndian.PutUint32(ret[8:], i3) 3000 binary.LittleEndian.PutUint32(ret[12:], i4) 3001 return 3002 } 3003 3004 func f32x4(f1, f2, f3, f4 float32) (ret [16]byte) { 3005 binary.LittleEndian.PutUint32(ret[0:], math.Float32bits(f1)) 3006 binary.LittleEndian.PutUint32(ret[4:], math.Float32bits(f2)) 3007 binary.LittleEndian.PutUint32(ret[8:], math.Float32bits(f3)) 3008 binary.LittleEndian.PutUint32(ret[12:], math.Float32bits(f4)) 3009 return 3010 } 3011 3012 func i64x2(i1, i2 uint64) (ret [16]byte) { 3013 binary.LittleEndian.PutUint64(ret[0:], i1) 3014 binary.LittleEndian.PutUint64(ret[8:], i2) 3015 return 3016 } 3017 3018 func f64x2(f1, f2 float64) (ret [16]byte) { 3019 binary.LittleEndian.PutUint64(ret[0:], math.Float64bits(f1)) 3020 binary.LittleEndian.PutUint64(ret[8:], math.Float64bits(f2)) 3021 return 3022 } 3023 3024 func TestCompiler_compileV128Cmp(t *testing.T) { 3025 tests := []struct { 3026 name string 3027 cmpType wazeroir.V128CmpType 3028 x1, x2, exp [16]byte 3029 }{ 3030 { 3031 name: "f32x4 eq", 3032 cmpType: wazeroir.V128CmpTypeF32x4Eq, 3033 x1: f32x4(1.0, -123.123, 0, 3214231), 3034 x2: f32x4(0, 0, 0, 0), 3035 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0}, 3036 }, 3037 { 3038 name: "f32x4 ne", 3039 cmpType: wazeroir.V128CmpTypeF32x4Ne, 3040 x1: f32x4(1.0, -123.123, 123, 3214231), 3041 x2: f32x4(2.0, 213123123.1231, 123, 0), 3042 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff}, 3043 }, 3044 { 3045 name: "f32x4 lt", 3046 cmpType: wazeroir.V128CmpTypeF32x4Lt, 3047 x1: f32x4(2.0, -123.123, 1234, 3214231), 3048 x2: f32x4(2.0, 213123123.1231, 123, 0), 3049 exp: [16]byte{0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, 3050 }, 3051 { 3052 name: "f32x4 le", 3053 cmpType: wazeroir.V128CmpTypeF32x4Le, 3054 x1: f32x4(2.0, -123.123, 1234, 3214231), 3055 x2: f32x4(2.0, 213123123.1231, 123, 0), 3056 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, 3057 }, 3058 { 3059 name: "f32x4 gt", 3060 cmpType: wazeroir.V128CmpTypeF32x4Gt, 3061 x1: f32x4(2.0, -123.123, 1234, 3214231), 3062 x2: f32x4(2.0, 213123123.1231, 123, 0), 3063 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 3064 }, 3065 { 3066 name: "f32x4 ge", 3067 cmpType: wazeroir.V128CmpTypeF32x4Ge, 3068 x1: f32x4(2.0, -123.123, 1234, 3214231), 3069 x2: f32x4(2.0, 213123123.1231, 123, 0), 3070 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 3071 }, 3072 { 3073 name: "f64x2 eq", 3074 cmpType: wazeroir.V128CmpTypeF64x2Eq, 3075 x1: f64x2(1.0, -123.12412), 3076 x2: f64x2(1.0, 123.123124), 3077 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0}, 3078 }, 3079 { 3080 name: "f64x2 ne", 3081 cmpType: wazeroir.V128CmpTypeF64x2Ne, 3082 x1: f64x2(1.0, -123.12412), 3083 x2: f64x2(1.0, 123.123124), 3084 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 3085 }, 3086 { 3087 name: "f64x2 lt", 3088 cmpType: wazeroir.V128CmpTypeF64x2Lt, 3089 x1: f64x2(-123, math.Inf(-1)), 3090 x2: f64x2(-123, -1234515), 3091 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 3092 }, 3093 { 3094 name: "f64x2 le", 3095 cmpType: wazeroir.V128CmpTypeF64x2Le, 3096 x1: f64x2(-123, 123), 3097 x2: f64x2(-123, math.MaxFloat64), 3098 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 3099 }, 3100 { 3101 name: "f64x2 gt", 3102 cmpType: wazeroir.V128CmpTypeF64x2Gt, 3103 x1: f64x2(math.MaxFloat64, -123.0), 3104 x2: f64x2(123, -123.0), 3105 exp: [16]byte{ 3106 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 3107 0, 0, 0, 0, 0, 0, 0, 0, 3108 }, 3109 }, 3110 { 3111 name: "f64x2 ge", 3112 cmpType: wazeroir.V128CmpTypeF64x2Ge, 3113 x1: f64x2(math.MaxFloat64, -123.0), 3114 x2: f64x2(123, -123.0), 3115 exp: [16]byte{ 3116 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 3117 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 3118 }, 3119 }, 3120 { 3121 name: "i8x16 eq", 3122 cmpType: wazeroir.V128CmpTypeI8x16Eq, 3123 x1: [16]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1}, 3124 x2: [16]byte{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0}, 3125 exp: [16]byte{0, 0xff, 0, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xff, 0}, 3126 }, 3127 { 3128 name: "i8x16 ne", 3129 cmpType: wazeroir.V128CmpTypeI8x16Ne, 3130 x1: [16]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1}, 3131 x2: [16]byte{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0}, 3132 exp: [16]byte{0xff, 0, 0xff, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0xff, 0, 0, 0xff}, 3133 }, 3134 { 3135 name: "i8x16 lt_s", 3136 cmpType: wazeroir.V128CmpTypeI8x16LtS, 3137 x1: [16]byte{0: i8ToU8(-1), 15: 0}, 3138 x2: [16]byte{0: 0x7f, 15: i8ToU8(-1)}, 3139 exp: [16]byte{0: 0xff}, 3140 }, 3141 { 3142 name: "i8x16 lt_u", 3143 cmpType: wazeroir.V128CmpTypeI8x16LtU, 3144 x1: [16]byte{0: 0xff, 15: 0}, 3145 x2: [16]byte{0: 0x7f, 15: 0xff}, 3146 exp: [16]byte{15: 0xff}, 3147 }, 3148 { 3149 name: "i8x16 gt_s", 3150 cmpType: wazeroir.V128CmpTypeI8x16GtS, 3151 x1: [16]byte{0: i8ToU8(-1), 15: 0}, 3152 x2: [16]byte{0: 0x7f, 15: i8ToU8(-1)}, 3153 exp: [16]byte{15: 0xff}, 3154 }, 3155 { 3156 name: "i8x16 gt_u", 3157 cmpType: wazeroir.V128CmpTypeI8x16GtU, 3158 x1: [16]byte{0: 0xff, 15: 0}, 3159 x2: [16]byte{0: 0x7f, 15: 0xff}, 3160 exp: [16]byte{0: 0xff}, 3161 }, 3162 { 3163 name: "i8x16 le_s", 3164 cmpType: wazeroir.V128CmpTypeI8x16LeS, 3165 x1: [16]byte{i8ToU8(-1), 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, i8ToU8(-1)}, 3166 x2: [16]byte{0: 0x7f, 15: i8ToU8(-1)}, 3167 exp: [16]byte{0: 0xff, 15: 0xff}, 3168 }, 3169 { 3170 name: "i8x16 le_u", 3171 cmpType: wazeroir.V128CmpTypeI8x16LeU, 3172 x1: [16]byte{0x80, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff}, 3173 x2: [16]byte{0: 0x7f, 5: 0x1, 15: 0xff}, 3174 exp: [16]byte{5: 0xff, 15: 0xff}, 3175 }, 3176 { 3177 name: "i8x16 ge_s", 3178 cmpType: wazeroir.V128CmpTypeI8x16GeS, 3179 x1: [16]byte{0: 0x7f, 15: i8ToU8(-1)}, 3180 x2: [16]byte{i8ToU8(-1), 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, i8ToU8(-1)}, 3181 exp: [16]byte{0: 0xff, 15: 0xff}, 3182 }, 3183 { 3184 name: "i8x16 ge_u", 3185 cmpType: wazeroir.V128CmpTypeI8x16GeU, 3186 x1: [16]byte{0: 0x7f, 3: 0xe, 15: 0xff}, 3187 x2: [16]byte{0xff, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff}, 3188 exp: [16]byte{3: 0xff, 15: 0xff}, 3189 }, 3190 { 3191 name: "i16x8 eq", 3192 cmpType: wazeroir.V128CmpTypeI16x8Eq, 3193 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1), 3194 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0), 3195 exp: i16x8(0xffff, 0, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0), 3196 }, 3197 { 3198 name: "i8x16 ne", 3199 cmpType: wazeroir.V128CmpTypeI16x8Ne, 3200 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1), 3201 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0), 3202 exp: i16x8(0, 0xffff, 0, 0, 0xffff, 0, 0, 0xffff), 3203 }, 3204 { 3205 name: "i8x16 lt_s", 3206 cmpType: wazeroir.V128CmpTypeI16x8LtS, 3207 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1), 3208 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0), 3209 exp: i16x8(0xffff, 0, 0, 0, 0xffff, 0, 0, 0), 3210 }, 3211 { 3212 name: "i8x16 lt_u", 3213 cmpType: wazeroir.V128CmpTypeI16x8LtU, 3214 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1), 3215 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0), 3216 exp: i16x8(0, 0, 0, 0, 0xffff, 0, 0, 0), 3217 }, 3218 { 3219 name: "i8x16 gt_s", 3220 cmpType: wazeroir.V128CmpTypeI16x8GtS, 3221 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1), 3222 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0), 3223 exp: i16x8(0xffff, 0xffff, 0, 0, 0, 0, 0, 0xffff), 3224 }, 3225 { 3226 name: "i8x16 gt_u", 3227 cmpType: wazeroir.V128CmpTypeI16x8GtU, 3228 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1), 3229 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0), 3230 exp: i16x8(0, 0xffff, 0, 0, 0, 0, 0, 0xffff), 3231 }, 3232 { 3233 name: "i8x16 le_s", 3234 cmpType: wazeroir.V128CmpTypeI16x8LeS, 3235 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1), 3236 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0), 3237 exp: i16x8(0xffff, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0), 3238 }, 3239 { 3240 name: "i8x16 le_u", 3241 cmpType: wazeroir.V128CmpTypeI16x8LeU, 3242 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1), 3243 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0), 3244 exp: i16x8(0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0), 3245 }, 3246 { 3247 name: "i8x16 ge_s", 3248 cmpType: wazeroir.V128CmpTypeI16x8GeS, 3249 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1), 3250 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0), 3251 exp: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0xffff), 3252 }, 3253 { 3254 name: "i8x16 ge_u", 3255 cmpType: wazeroir.V128CmpTypeI16x8GeU, 3256 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1), 3257 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0), 3258 exp: i16x8(0, 0xffff, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0xffff), 3259 }, 3260 { 3261 name: "i32x4 eq", 3262 cmpType: wazeroir.V128CmpTypeI32x4Eq, 3263 x1: i32x4(0, 1, 1, 0), 3264 x2: i32x4(0, 1, 0, 1), 3265 exp: i32x4(0xffffffff, 0xffffffff, 0, 0), 3266 }, 3267 { 3268 name: "i32x4 ne", 3269 cmpType: wazeroir.V128CmpTypeI32x4Ne, 3270 x1: i32x4(0, 1, 1, 0), 3271 x2: i32x4(0, 1, 0, 1), 3272 exp: i32x4(0, 0, 0xffffffff, 0xffffffff), 3273 }, 3274 { 3275 name: "i32x4 lt_s", 3276 cmpType: wazeroir.V128CmpTypeI32x4LtS, 3277 x1: i32x4(0xffffffff, 1, 1, 0), 3278 x2: i32x4(0, 1, 0, 1), 3279 exp: i32x4(0xffffffff, 0, 0, 0xffffffff), 3280 }, 3281 { 3282 name: "i32x4 lt_u", 3283 cmpType: wazeroir.V128CmpTypeI32x4LtU, 3284 x1: i32x4(0xffffffff, 1, 1, 0), 3285 x2: i32x4(0, 1, 0, 1), 3286 exp: i32x4(0, 0, 0, 0xffffffff), 3287 }, 3288 { 3289 name: "i32x4 gt_s", 3290 cmpType: wazeroir.V128CmpTypeI32x4GtS, 3291 x1: i32x4(0, 1, 1, 1), 3292 x2: i32x4(0xffffffff, 1, 0, 0), 3293 exp: i32x4(0xffffffff, 0, 0xffffffff, 0xffffffff), 3294 }, 3295 { 3296 name: "i32x4 gt_u", 3297 cmpType: wazeroir.V128CmpTypeI32x4GtU, 3298 x1: i32x4(0, 1, 1, 1), 3299 x2: i32x4(0xffffffff, 1, 0, 0), 3300 exp: i32x4(0, 0, 0xffffffff, 0xffffffff), 3301 }, 3302 { 3303 name: "i32x4 le_s", 3304 cmpType: wazeroir.V128CmpTypeI32x4LeS, 3305 x1: i32x4(0xffffffff, 1, 1, 0), 3306 x2: i32x4(0, 1, 0, 1), 3307 exp: i32x4(0xffffffff, 0xffffffff, 0, 0xffffffff), 3308 }, 3309 { 3310 name: "i32x4 le_u", 3311 cmpType: wazeroir.V128CmpTypeI32x4LeU, 3312 x1: i32x4(0xffffffff, 1, 1, 0), 3313 x2: i32x4(0, 1, 0, 1), 3314 exp: i32x4(0, 0xffffffff, 0, 0xffffffff), 3315 }, 3316 { 3317 name: "i32x4 ge_s", 3318 cmpType: wazeroir.V128CmpTypeI32x4GeS, 3319 x1: i32x4(0, 1, 1, 0), 3320 x2: i32x4(0xffffffff, 1, 0, 1), 3321 exp: i32x4(0xffffffff, 0xffffffff, 0xffffffff, 0), 3322 }, 3323 { 3324 name: "i32x4 ge_u", 3325 cmpType: wazeroir.V128CmpTypeI32x4GeU, 3326 x1: i32x4(0, 1, 1, 0), 3327 x2: i32x4(0xffffffff, 1, 0, 1), 3328 exp: i32x4(0, 0xffffffff, 0xffffffff, 0), 3329 }, 3330 { 3331 name: "i64x2 eq", 3332 cmpType: wazeroir.V128CmpTypeI64x2Eq, 3333 x1: i64x2(1, 0), 3334 x2: i64x2(0, 0), 3335 exp: i64x2(0, 0xffffffffffffffff), 3336 }, 3337 { 3338 name: "i64x2 ne", 3339 cmpType: wazeroir.V128CmpTypeI64x2Ne, 3340 x1: i64x2(1, 0), 3341 x2: i64x2(0, 0), 3342 exp: i64x2(0xffffffffffffffff, 0), 3343 }, 3344 { 3345 name: "i64x2 lt_s", 3346 cmpType: wazeroir.V128CmpTypeI64x2LtS, 3347 x1: i64x2(0xffffffffffffffff, 0), 3348 x2: i64x2(0, 0), 3349 exp: i64x2(0xffffffffffffffff, 0), 3350 }, 3351 { 3352 name: "i64x2 gt_s", 3353 cmpType: wazeroir.V128CmpTypeI64x2GtS, 3354 x1: i64x2(123, 0), 3355 x2: i64x2(123, 0xffffffffffffffff), 3356 exp: i64x2(0, 0xffffffffffffffff), 3357 }, 3358 { 3359 name: "i64x2 le_s", 3360 cmpType: wazeroir.V128CmpTypeI64x2LeS, 3361 x1: i64x2(123, 0xffffffffffffffff), 3362 x2: i64x2(123, 0), 3363 exp: i64x2(0xffffffffffffffff, 0xffffffffffffffff), 3364 }, 3365 { 3366 name: "i64x2 ge_s", 3367 cmpType: wazeroir.V128CmpTypeI64x2GeS, 3368 x1: i64x2(123, 0), 3369 x2: i64x2(123, 0xffffffffffffffff), 3370 exp: i64x2(0xffffffffffffffff, 0xffffffffffffffff), 3371 }, 3372 } 3373 3374 for _, tc := range tests { 3375 tc := tc 3376 t.Run(tc.name, func(t *testing.T) { 3377 env := newCompilerEnvironment() 3378 compiler := env.requireNewCompiler(t, newCompiler, 3379 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3380 3381 err := compiler.compilePreamble() 3382 require.NoError(t, err) 3383 3384 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3385 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 3386 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 3387 }) 3388 require.NoError(t, err) 3389 3390 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3391 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 3392 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 3393 }) 3394 require.NoError(t, err) 3395 3396 err = compiler.compileV128Cmp(&wazeroir.OperationV128Cmp{Type: tc.cmpType}) 3397 require.NoError(t, err) 3398 3399 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3400 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3401 3402 err = compiler.compileReturnFunction() 3403 require.NoError(t, err) 3404 3405 // Generate and run the code under test. 3406 code, _, err := compiler.compile() 3407 require.NoError(t, err) 3408 env.exec(code) 3409 3410 lo, hi := env.stackTopAsV128() 3411 var actual [16]byte 3412 binary.LittleEndian.PutUint64(actual[:8], lo) 3413 binary.LittleEndian.PutUint64(actual[8:], hi) 3414 require.Equal(t, tc.exp, actual) 3415 }) 3416 } 3417 } 3418 3419 func TestCompiler_compileV128AvgrU(t *testing.T) { 3420 tests := []struct { 3421 name string 3422 shape wazeroir.Shape 3423 x1, x2, exp [16]byte 3424 }{ 3425 { 3426 name: "i8x16", 3427 shape: wazeroir.ShapeI8x16, 3428 x1: [16]byte{0: 1, 2: 10, 10: 10, 15: math.MaxUint8}, 3429 x2: [16]byte{0: 10, 4: 5, 10: 5, 15: 10}, 3430 exp: [16]byte{ 3431 0: byte((uint16(1) + uint16(10) + 1) / 2), 3432 2: byte((uint16(10) + 1) / 2), 3433 4: byte((uint16(5) + 1) / 2), 3434 10: byte((uint16(10) + uint16(5) + 1) / 2), 3435 15: byte((uint16(math.MaxUint8) + uint16(10) + 1) / 2), 3436 }, 3437 }, 3438 { 3439 name: "i16x8", 3440 shape: wazeroir.ShapeI16x8, 3441 x1: i16x8(1, 0, 100, 0, 0, math.MaxUint16, 0, 0), 3442 x2: i16x8(10, 0, math.MaxUint16, 0, 0, 1, 0, 0), 3443 exp: i16x8( 3444 uint16((uint32(1)+uint32(10)+1)/2), 3445 0, 3446 uint16((uint32(100)+uint32(math.MaxUint16)+1)/2), 3447 0, 3448 0, 3449 uint16((uint32(1)+uint32(math.MaxUint16)+1)/2), 3450 0, 0, 3451 ), 3452 }, 3453 } 3454 3455 for _, tc := range tests { 3456 tc := tc 3457 t.Run(tc.name, func(t *testing.T) { 3458 env := newCompilerEnvironment() 3459 compiler := env.requireNewCompiler(t, newCompiler, 3460 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3461 3462 err := compiler.compilePreamble() 3463 require.NoError(t, err) 3464 3465 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3466 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 3467 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 3468 }) 3469 require.NoError(t, err) 3470 3471 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3472 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 3473 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 3474 }) 3475 require.NoError(t, err) 3476 3477 err = compiler.compileV128AvgrU(&wazeroir.OperationV128AvgrU{Shape: tc.shape}) 3478 require.NoError(t, err) 3479 3480 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3481 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3482 3483 err = compiler.compileReturnFunction() 3484 require.NoError(t, err) 3485 3486 // Generate and run the code under test. 3487 code, _, err := compiler.compile() 3488 require.NoError(t, err) 3489 env.exec(code) 3490 3491 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 3492 3493 lo, hi := env.stackTopAsV128() 3494 var actual [16]byte 3495 binary.LittleEndian.PutUint64(actual[:8], lo) 3496 binary.LittleEndian.PutUint64(actual[8:], hi) 3497 require.Equal(t, tc.exp, actual) 3498 }) 3499 } 3500 } 3501 3502 func TestCompiler_compileV128Sqrt(t *testing.T) { 3503 tests := []struct { 3504 name string 3505 shape wazeroir.Shape 3506 v, exp [16]byte 3507 }{ 3508 { 3509 name: "f32x4", 3510 shape: wazeroir.ShapeF32x4, 3511 v: f32x4(1.23, -123.1231, math.MaxFloat32, float32(math.Inf(1))), 3512 exp: f32x4( 3513 float32(math.Sqrt(float64(float32(1.23)))), 3514 float32(math.Sqrt(float64(float32(-123.1231)))), 3515 float32(math.Sqrt(float64(float32(math.MaxFloat32)))), 3516 float32(math.Sqrt(float64(float32(math.Inf(1))))), 3517 ), 3518 }, 3519 { 3520 name: "f64x2", 3521 shape: wazeroir.ShapeF64x2, 3522 v: f64x2(1.2314, math.MaxFloat64), 3523 exp: f64x2(math.Sqrt(1.2314), math.Sqrt(math.MaxFloat64)), 3524 }, 3525 } 3526 3527 for _, tc := range tests { 3528 tc := tc 3529 t.Run(tc.name, func(t *testing.T) { 3530 env := newCompilerEnvironment() 3531 compiler := env.requireNewCompiler(t, newCompiler, 3532 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3533 3534 err := compiler.compilePreamble() 3535 require.NoError(t, err) 3536 3537 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3538 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 3539 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 3540 }) 3541 require.NoError(t, err) 3542 3543 err = compiler.compileV128Sqrt(&wazeroir.OperationV128Sqrt{Shape: tc.shape}) 3544 require.NoError(t, err) 3545 3546 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3547 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3548 3549 err = compiler.compileReturnFunction() 3550 require.NoError(t, err) 3551 3552 // Generate and run the code under test. 3553 code, _, err := compiler.compile() 3554 require.NoError(t, err) 3555 env.exec(code) 3556 3557 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 3558 3559 lo, hi := env.stackTopAsV128() 3560 var actual [16]byte 3561 binary.LittleEndian.PutUint64(actual[:8], lo) 3562 binary.LittleEndian.PutUint64(actual[8:], hi) 3563 require.Equal(t, tc.exp, actual) 3564 }) 3565 } 3566 } 3567 3568 func TestCompiler_compileV128Mul(t *testing.T) { 3569 tests := []struct { 3570 name string 3571 shape wazeroir.Shape 3572 x1, x2, exp [16]byte 3573 }{ 3574 { 3575 name: "i16x8", 3576 shape: wazeroir.ShapeI16x8, 3577 x1: i16x8(1123, 0, 123, 1, 1, 5, 8, 1), 3578 x2: i16x8(0, 123, 123, 0, 1, 5, 9, 1), 3579 exp: i16x8(0, 0, 123*123, 0, 1, 25, 8*9, 1), 3580 }, 3581 { 3582 name: "i32x4", 3583 shape: wazeroir.ShapeI32x4, 3584 x1: i32x4(i32ToU32(-123), 5, 4, math.MaxUint32), 3585 x2: i32x4(i32ToU32(-10), 1, i32ToU32(-104), 0), 3586 exp: i32x4(1230, 5, i32ToU32(-416), 0), 3587 }, 3588 { 3589 name: "i64x2", 3590 shape: wazeroir.ShapeI64x2, 3591 x1: i64x2(1, 12345), 3592 x2: i64x2(100, i64ToU64(-10)), 3593 exp: i64x2(100, i64ToU64(-123450)), 3594 }, 3595 { 3596 name: "f32x4", 3597 shape: wazeroir.ShapeF32x4, 3598 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))), 3599 x2: f32x4(51234.12341, 123, math.MaxFloat32, -123), 3600 exp: f32x4(51234.12341, 123*123, float32(math.Inf(1)), float32(math.Inf(1))), 3601 }, 3602 { 3603 name: "f64x2", 3604 shape: wazeroir.ShapeF64x2, 3605 x1: f64x2(1.123, math.Inf(1)), 3606 x2: f64x2(1.123, math.MinInt64), 3607 exp: f64x2(1.123*1.123, math.Inf(-1)), 3608 }, 3609 } 3610 3611 for _, tc := range tests { 3612 tc := tc 3613 t.Run(tc.name, func(t *testing.T) { 3614 env := newCompilerEnvironment() 3615 compiler := env.requireNewCompiler(t, newCompiler, 3616 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3617 3618 err := compiler.compilePreamble() 3619 require.NoError(t, err) 3620 3621 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3622 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 3623 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 3624 }) 3625 require.NoError(t, err) 3626 3627 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3628 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 3629 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 3630 }) 3631 require.NoError(t, err) 3632 3633 err = compiler.compileV128Mul(&wazeroir.OperationV128Mul{Shape: tc.shape}) 3634 require.NoError(t, err) 3635 3636 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3637 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3638 3639 err = compiler.compileReturnFunction() 3640 require.NoError(t, err) 3641 3642 // Generate and run the code under test. 3643 code, _, err := compiler.compile() 3644 require.NoError(t, err) 3645 env.exec(code) 3646 3647 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 3648 3649 lo, hi := env.stackTopAsV128() 3650 var actual [16]byte 3651 binary.LittleEndian.PutUint64(actual[:8], lo) 3652 binary.LittleEndian.PutUint64(actual[8:], hi) 3653 require.Equal(t, tc.exp, actual) 3654 }) 3655 } 3656 } 3657 3658 func TestCompiler_compileV128Neg(t *testing.T) { 3659 tests := []struct { 3660 name string 3661 shape wazeroir.Shape 3662 v, exp [16]byte 3663 }{ 3664 { 3665 name: "i8x16", 3666 shape: wazeroir.ShapeI8x16, 3667 v: [16]byte{1: 123, 5: i8ToU8(-1), 15: i8ToU8(-125)}, 3668 exp: [16]byte{1: i8ToU8(-123), 5: 1, 15: 125}, 3669 }, 3670 { 3671 name: "i16x8", 3672 shape: wazeroir.ShapeI16x8, 3673 v: i16x8(0, 0, i16ToU16(-123), 0, 1, 25, 8, i16ToU16(-1)), 3674 exp: i16x8(0, 0, 123, 0, i16ToU16(-1), i16ToU16(-25), i16ToU16(-8), 1), 3675 }, 3676 { 3677 name: "i32x4", 3678 shape: wazeroir.ShapeI32x4, 3679 v: i32x4(1230, 5, i32ToU32(-416), 0), 3680 exp: i32x4(i32ToU32(-1230), i32ToU32(-5), 416, 0), 3681 }, 3682 { 3683 name: "i64x2", 3684 shape: wazeroir.ShapeI64x2, 3685 v: i64x2(100, i64ToU64(-123450)), 3686 exp: i64x2(i64ToU64(-100), 123450), 3687 }, 3688 { 3689 name: "f32x4", 3690 shape: wazeroir.ShapeF32x4, 3691 v: f32x4(51234.12341, -123, float32(math.Inf(1)), 0.1), 3692 exp: f32x4(-51234.12341, 123, float32(math.Inf(-1)), -0.1), 3693 }, 3694 { 3695 name: "f32x4", 3696 shape: wazeroir.ShapeF32x4, 3697 v: f32x4(51234.12341, 0, float32(math.Inf(1)), 0.1), 3698 exp: f32x4(-51234.12341, float32(math.Copysign(0, -1)), float32(math.Inf(-1)), -0.1), 3699 }, 3700 { 3701 name: "f64x2", 3702 shape: wazeroir.ShapeF64x2, 3703 v: f64x2(1.123, math.Inf(-1)), 3704 exp: f64x2(-1.123, math.Inf(1)), 3705 }, 3706 { 3707 name: "f64x2", 3708 shape: wazeroir.ShapeF64x2, 3709 v: f64x2(0, math.Inf(-1)), 3710 exp: f64x2(math.Copysign(0, -1), math.Inf(1)), 3711 }, 3712 } 3713 3714 for _, tc := range tests { 3715 tc := tc 3716 t.Run(tc.name, func(t *testing.T) { 3717 env := newCompilerEnvironment() 3718 compiler := env.requireNewCompiler(t, newCompiler, 3719 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3720 3721 err := compiler.compilePreamble() 3722 require.NoError(t, err) 3723 3724 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3725 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 3726 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 3727 }) 3728 require.NoError(t, err) 3729 3730 err = compiler.compileV128Neg(&wazeroir.OperationV128Neg{Shape: tc.shape}) 3731 require.NoError(t, err) 3732 3733 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3734 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3735 3736 err = compiler.compileReturnFunction() 3737 require.NoError(t, err) 3738 3739 // Generate and run the code under test. 3740 code, _, err := compiler.compile() 3741 require.NoError(t, err) 3742 env.exec(code) 3743 3744 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 3745 3746 lo, hi := env.stackTopAsV128() 3747 var actual [16]byte 3748 binary.LittleEndian.PutUint64(actual[:8], lo) 3749 binary.LittleEndian.PutUint64(actual[8:], hi) 3750 require.Equal(t, tc.exp, actual) 3751 }) 3752 } 3753 } 3754 3755 func TestCompiler_compileV128Abs(t *testing.T) { 3756 tests := []struct { 3757 name string 3758 shape wazeroir.Shape 3759 v, exp [16]byte 3760 }{ 3761 { 3762 name: "i8x16", 3763 shape: wazeroir.ShapeI8x16, 3764 v: [16]byte{1: 123, 5: i8ToU8(-1), 15: i8ToU8(-125)}, 3765 exp: [16]byte{1: 123, 5: 1, 15: 125}, 3766 }, 3767 { 3768 name: "i16x8", 3769 shape: wazeroir.ShapeI16x8, 3770 v: i16x8(0, 0, i16ToU16(-123), 0, 1, 25, 8, i16ToU16(-1)), 3771 exp: i16x8(0, 0, 123, 0, 1, 25, 8, 1), 3772 }, 3773 { 3774 name: "i32x4", 3775 shape: wazeroir.ShapeI32x4, 3776 v: i32x4(i32ToU32(-1230), 5, i32ToU32(-416), 0), 3777 exp: i32x4(1230, 5, 416, 0), 3778 }, 3779 { 3780 name: "i64x2", 3781 shape: wazeroir.ShapeI64x2, 3782 v: i64x2(i64ToU64(-100), i64ToU64(-123450)), 3783 exp: i64x2(100, 123450), 3784 }, 3785 { 3786 name: "f32x4", 3787 shape: wazeroir.ShapeF32x4, 3788 v: f32x4(51234.12341, -123, float32(math.Inf(1)), 0.1), 3789 exp: f32x4(51234.12341, 123, float32(math.Inf(1)), 0.1), 3790 }, 3791 { 3792 name: "f32x4", 3793 shape: wazeroir.ShapeF32x4, 3794 v: f32x4(51234.12341, 0, float32(math.Inf(1)), -0.1), 3795 exp: f32x4(51234.12341, 0, float32(math.Inf(1)), 0.1), 3796 }, 3797 { 3798 name: "f64x2", 3799 shape: wazeroir.ShapeF64x2, 3800 v: f64x2(-1.123, math.Inf(-1)), 3801 exp: f64x2(1.123, math.Inf(1)), 3802 }, 3803 { 3804 name: "f64x2", 3805 shape: wazeroir.ShapeF64x2, 3806 v: f64x2(0, math.Inf(-1)), 3807 exp: f64x2(0, math.Inf(1)), 3808 }, 3809 } 3810 3811 for _, tc := range tests { 3812 tc := tc 3813 t.Run(tc.name, func(t *testing.T) { 3814 env := newCompilerEnvironment() 3815 compiler := env.requireNewCompiler(t, newCompiler, 3816 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3817 3818 err := compiler.compilePreamble() 3819 require.NoError(t, err) 3820 3821 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3822 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 3823 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 3824 }) 3825 require.NoError(t, err) 3826 3827 err = compiler.compileV128Abs(&wazeroir.OperationV128Abs{Shape: tc.shape}) 3828 require.NoError(t, err) 3829 3830 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3831 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3832 3833 err = compiler.compileReturnFunction() 3834 require.NoError(t, err) 3835 3836 // Generate and run the code under test. 3837 code, _, err := compiler.compile() 3838 require.NoError(t, err) 3839 env.exec(code) 3840 3841 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 3842 3843 lo, hi := env.stackTopAsV128() 3844 var actual [16]byte 3845 binary.LittleEndian.PutUint64(actual[:8], lo) 3846 binary.LittleEndian.PutUint64(actual[8:], hi) 3847 require.Equal(t, tc.exp, actual) 3848 }) 3849 } 3850 } 3851 3852 func TestCompiler_compileV128Div(t *testing.T) { 3853 tests := []struct { 3854 name string 3855 shape wazeroir.Shape 3856 x1, x2, exp [16]byte 3857 }{ 3858 { 3859 name: "f32x4", 3860 shape: wazeroir.ShapeF32x4, 3861 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))), 3862 x2: f32x4(123.12, 123, math.MaxFloat32, -123), 3863 exp: f32x4(float32(1.0)/float32(123.12), 1, float32(math.Inf(1)), float32(math.Inf(1))), 3864 }, 3865 { 3866 name: "f64x2", 3867 shape: wazeroir.ShapeF64x2, 3868 x1: f64x2(1.123, math.Inf(1)), 3869 x2: f64x2(1.123, math.MinInt64), 3870 exp: f64x2(1.0, math.Inf(-1)), 3871 }, 3872 { 3873 name: "f64x2", 3874 shape: wazeroir.ShapeF64x2, 3875 x1: f64x2(0, math.Inf(1)), 3876 x2: f64x2(1.123, math.MaxInt64), 3877 exp: f64x2(0, math.Inf(1)), 3878 }, 3879 } 3880 3881 for _, tc := range tests { 3882 tc := tc 3883 t.Run(tc.name, func(t *testing.T) { 3884 env := newCompilerEnvironment() 3885 compiler := env.requireNewCompiler(t, newCompiler, 3886 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 3887 3888 err := compiler.compilePreamble() 3889 require.NoError(t, err) 3890 3891 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3892 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 3893 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 3894 }) 3895 require.NoError(t, err) 3896 3897 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 3898 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 3899 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 3900 }) 3901 require.NoError(t, err) 3902 3903 err = compiler.compileV128Div(&wazeroir.OperationV128Div{Shape: tc.shape}) 3904 require.NoError(t, err) 3905 3906 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 3907 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 3908 3909 err = compiler.compileReturnFunction() 3910 require.NoError(t, err) 3911 3912 // Generate and run the code under test. 3913 code, _, err := compiler.compile() 3914 require.NoError(t, err) 3915 env.exec(code) 3916 3917 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 3918 3919 lo, hi := env.stackTopAsV128() 3920 var actual [16]byte 3921 binary.LittleEndian.PutUint64(actual[:8], lo) 3922 binary.LittleEndian.PutUint64(actual[8:], hi) 3923 require.Equal(t, tc.exp, actual) 3924 }) 3925 } 3926 } 3927 3928 func TestCompiler_compileV128Min(t *testing.T) { 3929 tests := []struct { 3930 name string 3931 shape wazeroir.Shape 3932 signed bool 3933 x1, x2, exp [16]byte 3934 }{ 3935 { 3936 name: "i8x16s", 3937 shape: wazeroir.ShapeI8x16, 3938 signed: true, 3939 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2}, 3940 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)}, 3941 exp: [16]byte{0: 1, 5: i8ToU8(-1), 15: i8ToU8(-1)}, 3942 }, 3943 { 3944 name: "i8x16u", 3945 shape: wazeroir.ShapeI8x16, 3946 signed: false, 3947 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2}, 3948 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)}, 3949 exp: [16]byte{0: 1, 5: 0, 15: 2}, 3950 }, 3951 { 3952 name: "i16x8s", 3953 shape: wazeroir.ShapeI16x8, 3954 signed: true, 3955 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1), 3956 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1), 3957 exp: i16x8(0, 0, i16ToU16(-123), 1, 1, 4, i16ToU16(-123), 1), 3958 }, 3959 { 3960 name: "i16x8u", 3961 shape: wazeroir.ShapeI16x8, 3962 signed: false, 3963 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1), 3964 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1), 3965 exp: i16x8(0, 0, 123, 1, 1, 4, 5, 1), 3966 }, 3967 { 3968 name: "i32x4s", 3969 shape: wazeroir.ShapeI32x4, 3970 signed: true, 3971 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)), 3972 x2: i32x4(123, 5, 1, 0), 3973 exp: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)), 3974 }, 3975 { 3976 name: "i32x4u", 3977 shape: wazeroir.ShapeI32x4, 3978 signed: false, 3979 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)), 3980 x2: i32x4(123, 5, 1, 0), 3981 exp: i32x4(123, 0, 1, 0), 3982 }, 3983 { 3984 name: "f32x4", 3985 shape: wazeroir.ShapeF32x4, 3986 // If the sign of zeros are different, negative zeros must be returned according to the spec. 3987 x1: f32x4(0, math.Float32frombits(0b1<<31), 0, 0), 3988 x2: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0), 3989 exp: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0), 3990 }, 3991 { 3992 name: "f32x4", 3993 shape: wazeroir.ShapeF32x4, 3994 // If the sign of zeros are different, negative zeros must be returned according to the spec. 3995 x1: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0), 3996 x2: f32x4(0, math.Float32frombits(0b1<<31), 0, 0), 3997 exp: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0), 3998 }, 3999 { 4000 name: "f32x4", 4001 shape: wazeroir.ShapeF32x4, 4002 x1: f32x4(float32(math.NaN()), -123.12, 2.3, float32(math.Inf(1))), 4003 x2: f32x4(5.5, 123.12, 5.0, float32(math.Inf(-1))), 4004 exp: f32x4(float32(math.NaN()), -123.12, 2.3, float32(math.Inf(-1))), 4005 }, 4006 { 4007 name: "f32x4", 4008 shape: wazeroir.ShapeF32x4, 4009 x1: f32x4(5.5, 123.12, -5.0, float32(math.Inf(-1))), 4010 x2: f32x4(-123.12, float32(math.NaN()), 2.3, float32(math.Inf(-1))), 4011 exp: f32x4(-123.12, float32(math.NaN()), -5.0, float32(math.Inf(-1))), 4012 }, 4013 { 4014 name: "f32x4", 4015 shape: wazeroir.ShapeF32x4, 4016 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4017 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4018 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4019 }, 4020 { 4021 name: "f32x4", 4022 shape: wazeroir.ShapeF32x4, 4023 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4024 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4025 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4026 }, 4027 { 4028 name: "f64x2", 4029 shape: wazeroir.ShapeF64x2, 4030 // If the sign of zeros are different, negative zeros must be returned according to the spec. 4031 x1: f64x2(math.Copysign(0, -1), math.Copysign(0, 1)), 4032 x2: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)), 4033 exp: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)), 4034 }, 4035 { 4036 name: "f64x2", 4037 shape: wazeroir.ShapeF64x2, 4038 // If the sign of zeros are different, negative zeros must be returned according to the spec. 4039 x1: f64x2(math.Copysign(0, -1), 0), 4040 x2: f64x2(0, math.Copysign(0, -1)), 4041 exp: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)), 4042 }, 4043 { 4044 name: "f64x2", 4045 shape: wazeroir.ShapeF64x2, 4046 x1: f64x2(math.MinInt64, 0), 4047 x2: f64x2(math.MaxInt64, -12.3), 4048 exp: f64x2(math.MinInt64, -12.3), 4049 }, 4050 { 4051 name: "f64x2", 4052 shape: wazeroir.ShapeF64x2, 4053 x1: f64x2(math.MaxInt64, -12.3), 4054 x2: f64x2(math.MinInt64, 0), 4055 exp: f64x2(math.MinInt64, -12.3), 4056 }, 4057 { 4058 name: "f64x2", 4059 shape: wazeroir.ShapeF64x2, 4060 x1: f64x2(math.NaN(), math.NaN()), 4061 x2: f64x2(math.Inf(1), math.Inf(-1)), 4062 exp: f64x2(math.NaN(), math.NaN()), 4063 }, 4064 { 4065 name: "f64x2", 4066 shape: wazeroir.ShapeF64x2, 4067 x1: f64x2(math.Inf(1), math.Inf(-1)), 4068 x2: f64x2(math.NaN(), math.NaN()), 4069 exp: f64x2(math.NaN(), math.NaN()), 4070 }, 4071 } 4072 4073 for _, tc := range tests { 4074 tc := tc 4075 t.Run(tc.name, func(t *testing.T) { 4076 env := newCompilerEnvironment() 4077 compiler := env.requireNewCompiler(t, newCompiler, 4078 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 4079 4080 err := compiler.compilePreamble() 4081 require.NoError(t, err) 4082 4083 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4084 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 4085 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 4086 }) 4087 require.NoError(t, err) 4088 4089 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4090 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 4091 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 4092 }) 4093 require.NoError(t, err) 4094 4095 err = compiler.compileV128Min(&wazeroir.OperationV128Min{Shape: tc.shape, Signed: tc.signed}) 4096 require.NoError(t, err) 4097 4098 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 4099 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 4100 4101 err = compiler.compileReturnFunction() 4102 require.NoError(t, err) 4103 4104 // Generate and run the code under test. 4105 code, _, err := compiler.compile() 4106 require.NoError(t, err) 4107 env.exec(code) 4108 4109 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 4110 4111 lo, hi := env.stackTopAsV128() 4112 switch tc.shape { 4113 case wazeroir.ShapeF64x2: 4114 for _, vs := range [][2]float64{ 4115 {math.Float64frombits(lo), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8]))}, 4116 {math.Float64frombits(hi), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:]))}, 4117 } { 4118 actual, exp := vs[0], vs[1] 4119 if math.IsNaN(exp) { 4120 require.True(t, math.IsNaN(actual)) 4121 } else { 4122 require.Equal(t, math.Float64bits(exp), math.Float64bits(actual)) 4123 } 4124 } 4125 case wazeroir.ShapeF32x4: 4126 for _, vs := range [][2]float32{ 4127 {math.Float32frombits(uint32(lo)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4]))}, 4128 {math.Float32frombits(uint32(lo >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8]))}, 4129 {math.Float32frombits(uint32(hi)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12]))}, 4130 {math.Float32frombits(uint32(hi >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:]))}, 4131 } { 4132 actual, exp := vs[0], vs[1] 4133 if math.IsNaN(float64(exp)) { 4134 require.True(t, math.IsNaN(float64(actual))) 4135 } else { 4136 require.Equal(t, math.Float32bits(exp), math.Float32bits(actual)) 4137 } 4138 } 4139 default: 4140 var actual [16]byte 4141 binary.LittleEndian.PutUint64(actual[:8], lo) 4142 binary.LittleEndian.PutUint64(actual[8:], hi) 4143 require.Equal(t, tc.exp, actual) 4144 } 4145 }) 4146 } 4147 } 4148 4149 func TestCompiler_compileV128Max(t *testing.T) { 4150 tests := []struct { 4151 name string 4152 shape wazeroir.Shape 4153 signed bool 4154 x1, x2, exp [16]byte 4155 }{ 4156 { 4157 name: "i8x16s", 4158 shape: wazeroir.ShapeI8x16, 4159 signed: true, 4160 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2}, 4161 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)}, 4162 exp: [16]byte{0: 123, 5: 0, 15: 2}, 4163 }, 4164 { 4165 name: "i8x16u", 4166 shape: wazeroir.ShapeI8x16, 4167 signed: false, 4168 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2}, 4169 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)}, 4170 exp: [16]byte{0: 123, 5: i8ToU8(-1), 15: i8ToU8(-1)}, 4171 }, 4172 { 4173 name: "i16x8s", 4174 shape: wazeroir.ShapeI16x8, 4175 signed: true, 4176 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1), 4177 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1), 4178 exp: i16x8(1123, 123, 123, 3, 1, 6, 5, 1), 4179 }, 4180 { 4181 name: "i16x8u", 4182 shape: wazeroir.ShapeI16x8, 4183 signed: false, 4184 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1), 4185 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1), 4186 exp: i16x8(1123, 123, i16ToU16(-123), 3, 1, 6, i16ToU16(-123), 1), 4187 }, 4188 { 4189 name: "i32x4s", 4190 shape: wazeroir.ShapeI32x4, 4191 signed: true, 4192 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)), 4193 x2: i32x4(123, 5, 1, 0), 4194 exp: i32x4(123, 5, 1, 0), 4195 }, 4196 { 4197 name: "i32x4u", 4198 shape: wazeroir.ShapeI32x4, 4199 signed: false, 4200 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)), 4201 x2: i32x4(123, 5, 1, 0), 4202 exp: i32x4(i32ToU32(-123), 5, 1, i32ToU32(math.MinInt32)), 4203 }, 4204 { 4205 name: "f32x4", 4206 shape: wazeroir.ShapeF32x4, 4207 x1: f32x4(float32(math.NaN()), -123.12, 2.3, float32(math.Inf(1))), 4208 x2: f32x4(5.5, 123.12, 5.0, float32(math.Inf(-1))), 4209 exp: f32x4(float32(math.NaN()), 123.12, 5.0, float32(math.Inf(1))), 4210 }, 4211 { 4212 name: "f32x4", 4213 shape: wazeroir.ShapeF32x4, 4214 x1: f32x4(5.5, 123.12, -5.0, float32(math.Inf(-1))), 4215 x2: f32x4(-123.12, float32(math.NaN()), 2.3, float32(math.Inf(-1))), 4216 exp: f32x4(5.5, float32(math.NaN()), 2.3, float32(math.Inf(-1))), 4217 }, 4218 { 4219 name: "f32x4", 4220 shape: wazeroir.ShapeF32x4, 4221 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4222 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4223 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4224 }, 4225 { 4226 name: "f32x4", 4227 shape: wazeroir.ShapeF32x4, 4228 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4229 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4230 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4231 }, 4232 { 4233 name: "f32x4", 4234 shape: wazeroir.ShapeF32x4, 4235 // If the sign of zeros are different, positive zeros must be returned according to the spec. 4236 x1: f32x4(0, 0, math.Float32frombits(1<<31), 0), 4237 x2: f32x4(math.Float32frombits(1<<31), math.Float32frombits(1<<31), math.Float32frombits(1<<31), math.Float32frombits(1<<31)), 4238 exp: f32x4(0, 0, math.Float32frombits(1<<31), 0), 4239 }, 4240 { 4241 name: "f64x2", 4242 shape: wazeroir.ShapeF64x2, 4243 x1: f64x2(math.MinInt64, 0), 4244 x2: f64x2(math.MaxInt64, -12.3), 4245 exp: f64x2(math.MaxInt64, 0), 4246 }, 4247 { 4248 name: "f64x2", 4249 shape: wazeroir.ShapeF64x2, 4250 x1: f64x2(math.MaxInt64, -12.3), 4251 x2: f64x2(math.MinInt64, 0), 4252 exp: f64x2(math.MaxInt64, 0), 4253 }, 4254 { 4255 name: "f64x2", 4256 shape: wazeroir.ShapeF64x2, 4257 x1: f64x2(math.NaN(), -12.3), 4258 x2: f64x2(math.MinInt64, math.NaN()), 4259 exp: f64x2(math.NaN(), math.NaN()), 4260 }, 4261 { 4262 name: "f64x2", 4263 shape: wazeroir.ShapeF64x2, 4264 x1: f64x2(math.MinInt64, math.NaN()), 4265 x2: f64x2(math.NaN(), -12.3), 4266 exp: f64x2(math.NaN(), math.NaN()), 4267 }, 4268 { 4269 name: "f64x2", 4270 shape: wazeroir.ShapeF64x2, 4271 x1: f64x2(math.NaN(), math.NaN()), 4272 x2: f64x2(math.Inf(1), math.Inf(-1)), 4273 exp: f64x2(math.NaN(), math.NaN()), 4274 }, 4275 { 4276 name: "f64x2", 4277 shape: wazeroir.ShapeF64x2, 4278 x1: f64x2(math.Inf(1), math.Inf(-1)), 4279 x2: f64x2(math.NaN(), math.NaN()), 4280 exp: f64x2(math.NaN(), math.NaN()), 4281 }, 4282 { 4283 name: "f64x2", 4284 shape: wazeroir.ShapeF64x2, 4285 // If the sign of zeros are different, positive zeros must be returned according to the spec. 4286 x1: f64x2(0, 0), 4287 x2: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)), 4288 exp: f64x2(0, 0), 4289 }, 4290 { 4291 name: "f64x2", 4292 shape: wazeroir.ShapeF64x2, 4293 // If the sign of zeros are different, positive zeros must be returned according to the spec. 4294 x1: f64x2(math.Copysign(0, -1), math.Copysign(0, 1)), 4295 x2: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)), 4296 exp: f64x2(math.Copysign(0, -1), 0), 4297 }, 4298 } 4299 4300 for _, tc := range tests { 4301 tc := tc 4302 t.Run(tc.name, func(t *testing.T) { 4303 env := newCompilerEnvironment() 4304 compiler := env.requireNewCompiler(t, newCompiler, 4305 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 4306 4307 err := compiler.compilePreamble() 4308 require.NoError(t, err) 4309 4310 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4311 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 4312 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 4313 }) 4314 require.NoError(t, err) 4315 4316 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4317 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 4318 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 4319 }) 4320 require.NoError(t, err) 4321 4322 err = compiler.compileV128Max(&wazeroir.OperationV128Max{Shape: tc.shape, Signed: tc.signed}) 4323 require.NoError(t, err) 4324 4325 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 4326 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 4327 4328 err = compiler.compileReturnFunction() 4329 require.NoError(t, err) 4330 4331 // Generate and run the code under test. 4332 code, _, err := compiler.compile() 4333 require.NoError(t, err) 4334 env.exec(code) 4335 4336 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 4337 4338 lo, hi := env.stackTopAsV128() 4339 switch tc.shape { 4340 case wazeroir.ShapeF64x2: 4341 for _, vs := range [][2]float64{ 4342 {math.Float64frombits(lo), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8]))}, 4343 {math.Float64frombits(hi), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:]))}, 4344 } { 4345 actual, exp := vs[0], vs[1] 4346 if math.IsNaN(exp) { 4347 require.True(t, math.IsNaN(actual)) 4348 } else { 4349 require.Equal(t, math.Float64bits(exp), math.Float64bits(actual)) 4350 } 4351 } 4352 case wazeroir.ShapeF32x4: 4353 for _, vs := range [][2]float32{ 4354 {math.Float32frombits(uint32(lo)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4]))}, 4355 {math.Float32frombits(uint32(lo >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8]))}, 4356 {math.Float32frombits(uint32(hi)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12]))}, 4357 {math.Float32frombits(uint32(hi >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:]))}, 4358 } { 4359 actual, exp := vs[0], vs[1] 4360 if math.IsNaN(float64(exp)) { 4361 require.True(t, math.IsNaN(float64(actual))) 4362 } else { 4363 require.Equal(t, math.Float32bits(exp), math.Float32bits(actual)) 4364 } 4365 } 4366 default: 4367 var actual [16]byte 4368 binary.LittleEndian.PutUint64(actual[:8], lo) 4369 binary.LittleEndian.PutUint64(actual[8:], hi) 4370 require.Equal(t, tc.exp, actual) 4371 } 4372 }) 4373 } 4374 } 4375 4376 func TestCompiler_compileV128AddSat(t *testing.T) { 4377 tests := []struct { 4378 name string 4379 shape wazeroir.Shape 4380 signed bool 4381 x1, x2, exp [16]byte 4382 }{ 4383 { 4384 name: "i8x16s", 4385 shape: wazeroir.ShapeI8x16, 4386 signed: true, 4387 x1: [16]byte{ 4388 0: i8ToU8(math.MaxInt8), 4389 5: i8ToU8(-1), 4390 15: i8ToU8(math.MinInt8), 4391 }, 4392 x2: [16]byte{ 4393 0: 1, 4394 5: 0, 4395 15: i8ToU8(-1), 4396 }, 4397 exp: [16]byte{ 4398 0: i8ToU8(math.MaxInt8), 4399 5: i8ToU8(-1), 4400 15: i8ToU8(math.MinInt8), 4401 }, 4402 }, 4403 { 4404 name: "i8x16u", 4405 shape: wazeroir.ShapeI8x16, 4406 signed: false, 4407 x1: [16]byte{ 4408 0: i8ToU8(math.MaxInt8), 4409 5: 0, 4410 15: math.MaxUint8, 4411 }, 4412 x2: [16]byte{ 4413 0: 1, 4414 5: i8ToU8(-1), 4415 15: 1, 4416 }, 4417 exp: [16]byte{ 4418 0: i8ToU8(math.MaxInt8) + 1, 4419 5: i8ToU8(-1), 4420 15: math.MaxUint8, 4421 }, 4422 }, 4423 { 4424 name: "i16x8s", 4425 shape: wazeroir.ShapeI16x8, 4426 signed: true, 4427 x1: i16x8(i16ToU16(math.MinInt16), 0, 123, 1, 1, 6, i16ToU16(-123), i16ToU16(math.MaxInt16)), 4428 x2: i16x8(i16ToU16(-1), 123, i16ToU16(-123), 3, 1, 4, 5, 1), 4429 exp: i16x8(i16ToU16(math.MinInt16), 123, 0, 4, 2, 10, i16ToU16(-118), i16ToU16(math.MaxInt16)), 4430 }, 4431 { 4432 name: "i16x8u", 4433 shape: wazeroir.ShapeI16x8, 4434 signed: false, 4435 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), math.MaxUint16), 4436 x2: i16x8(0, 123, math.MaxUint16, 3, 1, 4, 0, 1), 4437 exp: i16x8(1123, 123, math.MaxUint16, 4, 2, 10, i16ToU16(-123), math.MaxUint16), 4438 }, 4439 } 4440 4441 for _, tc := range tests { 4442 tc := tc 4443 t.Run(tc.name, func(t *testing.T) { 4444 env := newCompilerEnvironment() 4445 compiler := env.requireNewCompiler(t, newCompiler, 4446 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 4447 4448 err := compiler.compilePreamble() 4449 require.NoError(t, err) 4450 4451 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4452 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 4453 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 4454 }) 4455 require.NoError(t, err) 4456 4457 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4458 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 4459 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 4460 }) 4461 require.NoError(t, err) 4462 4463 err = compiler.compileV128AddSat(&wazeroir.OperationV128AddSat{Shape: tc.shape, Signed: tc.signed}) 4464 require.NoError(t, err) 4465 4466 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 4467 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 4468 4469 err = compiler.compileReturnFunction() 4470 require.NoError(t, err) 4471 4472 // Generate and run the code under test. 4473 code, _, err := compiler.compile() 4474 require.NoError(t, err) 4475 env.exec(code) 4476 4477 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 4478 4479 lo, hi := env.stackTopAsV128() 4480 var actual [16]byte 4481 binary.LittleEndian.PutUint64(actual[:8], lo) 4482 binary.LittleEndian.PutUint64(actual[8:], hi) 4483 require.Equal(t, tc.exp, actual) 4484 }) 4485 } 4486 } 4487 4488 func TestCompiler_compileV128SubSat(t *testing.T) { 4489 tests := []struct { 4490 name string 4491 shape wazeroir.Shape 4492 signed bool 4493 x1, x2, exp [16]byte 4494 }{ 4495 { 4496 name: "i8x16s", 4497 shape: wazeroir.ShapeI8x16, 4498 signed: true, 4499 x1: [16]byte{ 4500 0: i8ToU8(math.MinInt8), 4501 5: i8ToU8(-1), 4502 15: i8ToU8(math.MaxInt8), 4503 }, 4504 x2: [16]byte{ 4505 0: 1, 4506 5: 0, 4507 15: i8ToU8(-1), 4508 }, 4509 exp: [16]byte{ 4510 0: i8ToU8(math.MinInt8), 4511 5: i8ToU8(-1), 4512 15: i8ToU8(math.MaxInt8), 4513 }, 4514 }, 4515 { 4516 name: "i8x16u", 4517 shape: wazeroir.ShapeI8x16, 4518 signed: false, 4519 x1: [16]byte{ 4520 0: i8ToU8(math.MinInt8), 4521 5: i8ToU8(-1), 4522 15: 0, 4523 }, 4524 x2: [16]byte{ 4525 0: 1, 4526 5: 0, 4527 15: 1, 4528 }, 4529 exp: [16]byte{ 4530 0: i8ToU8(math.MinInt8) - 1, 4531 5: i8ToU8(-1), 4532 15: 0, 4533 }, 4534 }, 4535 { 4536 name: "i16x8s", 4537 shape: wazeroir.ShapeI16x8, 4538 signed: true, 4539 x1: i16x8(i16ToU16(math.MinInt16), 0, 123, 1, 1, 6, i16ToU16(-123), i16ToU16(math.MaxInt16)), 4540 x2: i16x8(1, 123, i16ToU16(-123), 3, 1, 4, 5, i16ToU16(-123)), 4541 exp: i16x8(i16ToU16(math.MinInt16), i16ToU16(-123), 246, i16ToU16(-2), 0, 2, i16ToU16(-128), i16ToU16(math.MaxInt16)), 4542 }, 4543 { 4544 name: "i16x8u", 4545 shape: wazeroir.ShapeI16x8, 4546 signed: false, 4547 x1: i16x8(1123, 0, 123, 1, 1, 6, 200, math.MaxUint16), 4548 x2: i16x8(0, 123, math.MaxUint16, 3, 1, 4, i16ToU16(-1), 12), 4549 exp: i16x8(1123, 0, 0, 0, 0, 2, 0, math.MaxUint16-12), 4550 }, 4551 } 4552 4553 for _, tc := range tests { 4554 tc := tc 4555 t.Run(tc.name, func(t *testing.T) { 4556 env := newCompilerEnvironment() 4557 compiler := env.requireNewCompiler(t, newCompiler, 4558 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 4559 4560 err := compiler.compilePreamble() 4561 require.NoError(t, err) 4562 4563 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4564 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 4565 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 4566 }) 4567 require.NoError(t, err) 4568 4569 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4570 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 4571 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 4572 }) 4573 require.NoError(t, err) 4574 4575 err = compiler.compileV128SubSat(&wazeroir.OperationV128SubSat{Shape: tc.shape, Signed: tc.signed}) 4576 require.NoError(t, err) 4577 4578 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 4579 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 4580 4581 err = compiler.compileReturnFunction() 4582 require.NoError(t, err) 4583 4584 // Generate and run the code under test. 4585 code, _, err := compiler.compile() 4586 require.NoError(t, err) 4587 env.exec(code) 4588 4589 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 4590 4591 lo, hi := env.stackTopAsV128() 4592 var actual [16]byte 4593 binary.LittleEndian.PutUint64(actual[:8], lo) 4594 binary.LittleEndian.PutUint64(actual[8:], hi) 4595 require.Equal(t, tc.exp, actual) 4596 }) 4597 } 4598 } 4599 4600 func TestCompiler_compileV128Popcnt(t *testing.T) { 4601 tests := []struct { 4602 name string 4603 v, exp [16]byte 4604 }{ 4605 { 4606 name: "ones", 4607 v: [16]byte{ 4608 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, 4609 0, 1 << 2, 0, 1 << 4, 0, 1 << 6, 0, 0, 4610 }, 4611 exp: [16]byte{ 4612 1, 1, 1, 1, 1, 1, 1, 1, 4613 0, 1, 0, 1, 0, 1, 0, 0, 4614 }, 4615 }, 4616 { 4617 name: "mix", 4618 v: [16]byte{ 4619 0b1, 0b11, 0b111, 0b1111, 0b11111, 0b111111, 0b1111111, 0b11111111, 4620 0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b10010000, 0b10100000, 0b11000000, 0, 4621 }, 4622 exp: [16]byte{ 4623 1, 2, 3, 4, 5, 6, 7, 8, 4624 2, 2, 2, 2, 2, 2, 2, 0, 4625 }, 4626 }, 4627 } 4628 4629 for _, tc := range tests { 4630 tc := tc 4631 t.Run(tc.name, func(t *testing.T) { 4632 env := newCompilerEnvironment() 4633 compiler := env.requireNewCompiler(t, newCompiler, 4634 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 4635 4636 err := compiler.compilePreamble() 4637 require.NoError(t, err) 4638 4639 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4640 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 4641 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 4642 }) 4643 require.NoError(t, err) 4644 4645 err = compiler.compileV128Popcnt(&wazeroir.OperationV128Popcnt{}) 4646 require.NoError(t, err) 4647 4648 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 4649 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 4650 4651 err = compiler.compileReturnFunction() 4652 require.NoError(t, err) 4653 4654 // Generate and run the code under test. 4655 code, _, err := compiler.compile() 4656 require.NoError(t, err) 4657 env.exec(code) 4658 4659 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 4660 4661 lo, hi := env.stackTopAsV128() 4662 var actual [16]byte 4663 binary.LittleEndian.PutUint64(actual[:8], lo) 4664 binary.LittleEndian.PutUint64(actual[8:], hi) 4665 require.Equal(t, tc.exp, actual) 4666 }) 4667 } 4668 } 4669 4670 func TestCompiler_compileV128Round(t *testing.T) { 4671 tests := []struct { 4672 name string 4673 shape wazeroir.Shape 4674 kind wazeroir.OperationKind 4675 v [16]byte 4676 }{ 4677 { 4678 name: "f32 ceil", 4679 shape: wazeroir.ShapeF32x4, 4680 kind: wazeroir.OperationKindV128Ceil, 4681 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4682 }, 4683 { 4684 name: "f32 ceil", 4685 shape: wazeroir.ShapeF32x4, 4686 kind: wazeroir.OperationKindV128Ceil, 4687 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))), 4688 }, 4689 { 4690 name: "f64 ceil", 4691 shape: wazeroir.ShapeF64x2, 4692 kind: wazeroir.OperationKindV128Ceil, 4693 v: f64x2(1.231, -123.12313), 4694 }, 4695 { 4696 name: "f64 ceil", 4697 shape: wazeroir.ShapeF64x2, 4698 kind: wazeroir.OperationKindV128Ceil, 4699 v: f64x2(math.Inf(1), math.NaN()), 4700 }, 4701 { 4702 name: "f64 ceil", 4703 shape: wazeroir.ShapeF64x2, 4704 kind: wazeroir.OperationKindV128Ceil, 4705 v: f64x2(math.Inf(-1), math.Pi), 4706 }, 4707 { 4708 name: "f32 floor", 4709 shape: wazeroir.ShapeF32x4, 4710 kind: wazeroir.OperationKindV128Floor, 4711 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4712 }, 4713 { 4714 name: "f32 floor", 4715 shape: wazeroir.ShapeF32x4, 4716 kind: wazeroir.OperationKindV128Floor, 4717 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))), 4718 }, 4719 { 4720 name: "f64 floor", 4721 shape: wazeroir.ShapeF64x2, 4722 kind: wazeroir.OperationKindV128Floor, 4723 v: f64x2(1.231, -123.12313), 4724 }, 4725 { 4726 name: "f64 floor", 4727 shape: wazeroir.ShapeF64x2, 4728 kind: wazeroir.OperationKindV128Floor, 4729 v: f64x2(math.Inf(1), math.NaN()), 4730 }, 4731 { 4732 name: "f64 floor", 4733 shape: wazeroir.ShapeF64x2, 4734 kind: wazeroir.OperationKindV128Floor, 4735 v: f64x2(math.Inf(-1), math.Pi), 4736 }, 4737 { 4738 name: "f32 trunc", 4739 shape: wazeroir.ShapeF32x4, 4740 kind: wazeroir.OperationKindV128Trunc, 4741 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4742 }, 4743 { 4744 name: "f32 trunc", 4745 shape: wazeroir.ShapeF32x4, 4746 kind: wazeroir.OperationKindV128Trunc, 4747 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))), 4748 }, 4749 { 4750 name: "f64 trunc", 4751 shape: wazeroir.ShapeF64x2, 4752 kind: wazeroir.OperationKindV128Trunc, 4753 v: f64x2(1.231, -123.12313), 4754 }, 4755 { 4756 name: "f64 trunc", 4757 shape: wazeroir.ShapeF64x2, 4758 kind: wazeroir.OperationKindV128Trunc, 4759 v: f64x2(math.Inf(1), math.NaN()), 4760 }, 4761 { 4762 name: "f64 trunc", 4763 shape: wazeroir.ShapeF64x2, 4764 kind: wazeroir.OperationKindV128Trunc, 4765 v: f64x2(math.Inf(-1), math.Pi), 4766 }, 4767 { 4768 name: "f32 nearest", 4769 shape: wazeroir.ShapeF32x4, 4770 kind: wazeroir.OperationKindV128Nearest, 4771 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4772 }, 4773 { 4774 name: "f32 nearest", 4775 shape: wazeroir.ShapeF32x4, 4776 kind: wazeroir.OperationKindV128Nearest, 4777 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))), 4778 }, 4779 { 4780 name: "f64 nearest", 4781 shape: wazeroir.ShapeF64x2, 4782 kind: wazeroir.OperationKindV128Nearest, 4783 v: f64x2(1.231, -123.12313), 4784 }, 4785 { 4786 name: "f64 nearest", 4787 shape: wazeroir.ShapeF64x2, 4788 kind: wazeroir.OperationKindV128Nearest, 4789 v: f64x2(math.Inf(1), math.NaN()), 4790 }, 4791 { 4792 name: "f64 nearest", 4793 shape: wazeroir.ShapeF64x2, 4794 kind: wazeroir.OperationKindV128Nearest, 4795 v: f64x2(math.Inf(-1), math.Pi), 4796 }, 4797 } 4798 4799 for _, tc := range tests { 4800 tc := tc 4801 t.Run(tc.name, func(t *testing.T) { 4802 env := newCompilerEnvironment() 4803 compiler := env.requireNewCompiler(t, newCompiler, 4804 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 4805 4806 err := compiler.compilePreamble() 4807 require.NoError(t, err) 4808 4809 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 4810 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 4811 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 4812 }) 4813 require.NoError(t, err) 4814 4815 is32bit := tc.shape == wazeroir.ShapeF32x4 4816 switch tc.kind { 4817 case wazeroir.OperationKindV128Ceil: 4818 err = compiler.compileV128Ceil(&wazeroir.OperationV128Ceil{Shape: tc.shape}) 4819 case wazeroir.OperationKindV128Floor: 4820 err = compiler.compileV128Floor(&wazeroir.OperationV128Floor{Shape: tc.shape}) 4821 case wazeroir.OperationKindV128Trunc: 4822 err = compiler.compileV128Trunc(&wazeroir.OperationV128Trunc{Shape: tc.shape}) 4823 case wazeroir.OperationKindV128Nearest: 4824 err = compiler.compileV128Nearest(&wazeroir.OperationV128Nearest{Shape: tc.shape}) 4825 } 4826 require.NoError(t, err) 4827 4828 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 4829 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 4830 4831 err = compiler.compileReturnFunction() 4832 require.NoError(t, err) 4833 4834 // Generate and run the code under test. 4835 code, _, err := compiler.compile() 4836 require.NoError(t, err) 4837 env.exec(code) 4838 4839 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 4840 4841 lo, hi := env.stackTopAsV128() 4842 4843 if is32bit { 4844 actualFs := [4]float32{ 4845 math.Float32frombits(uint32(lo)), 4846 math.Float32frombits(uint32(lo >> 32)), 4847 math.Float32frombits(uint32(hi)), 4848 math.Float32frombits(uint32(hi >> 32)), 4849 } 4850 f1Original, f2Original, f3Original, f4Original := math.Float32frombits(binary.LittleEndian.Uint32(tc.v[:4])), 4851 math.Float32frombits(binary.LittleEndian.Uint32(tc.v[4:8])), 4852 math.Float32frombits(binary.LittleEndian.Uint32(tc.v[8:12])), 4853 math.Float32frombits(binary.LittleEndian.Uint32(tc.v[12:])) 4854 4855 var expFs [4]float32 4856 switch tc.kind { 4857 case wazeroir.OperationKindV128Ceil: 4858 expFs[0] = float32(math.Ceil(float64(f1Original))) 4859 expFs[1] = float32(math.Ceil(float64(f2Original))) 4860 expFs[2] = float32(math.Ceil(float64(f3Original))) 4861 expFs[3] = float32(math.Ceil(float64(f4Original))) 4862 case wazeroir.OperationKindV128Floor: 4863 expFs[0] = float32(math.Floor(float64(f1Original))) 4864 expFs[1] = float32(math.Floor(float64(f2Original))) 4865 expFs[2] = float32(math.Floor(float64(f3Original))) 4866 expFs[3] = float32(math.Floor(float64(f4Original))) 4867 case wazeroir.OperationKindV128Trunc: 4868 expFs[0] = float32(math.Trunc(float64(f1Original))) 4869 expFs[1] = float32(math.Trunc(float64(f2Original))) 4870 expFs[2] = float32(math.Trunc(float64(f3Original))) 4871 expFs[3] = float32(math.Trunc(float64(f4Original))) 4872 case wazeroir.OperationKindV128Nearest: 4873 expFs[0] = moremath.WasmCompatNearestF32(f1Original) 4874 expFs[1] = moremath.WasmCompatNearestF32(f2Original) 4875 expFs[2] = moremath.WasmCompatNearestF32(f3Original) 4876 expFs[3] = moremath.WasmCompatNearestF32(f4Original) 4877 } 4878 4879 for i := range expFs { 4880 exp, actual := expFs[i], actualFs[i] 4881 if math.IsNaN(float64(exp)) { 4882 require.True(t, math.IsNaN(float64(actual))) 4883 } else { 4884 require.Equal(t, exp, actual) 4885 } 4886 } 4887 } else { 4888 actualFs := [2]float64{math.Float64frombits(lo), math.Float64frombits(hi)} 4889 f1Original, f2Original := math.Float64frombits(binary.LittleEndian.Uint64(tc.v[:8])), math.Float64frombits(binary.LittleEndian.Uint64(tc.v[8:])) 4890 4891 var expFs [2]float64 4892 switch tc.kind { 4893 case wazeroir.OperationKindV128Ceil: 4894 expFs[0] = math.Ceil(f1Original) 4895 expFs[1] = math.Ceil(f2Original) 4896 case wazeroir.OperationKindV128Floor: 4897 expFs[0] = math.Floor(f1Original) 4898 expFs[1] = math.Floor(f2Original) 4899 case wazeroir.OperationKindV128Trunc: 4900 expFs[0] = math.Trunc(f1Original) 4901 expFs[1] = math.Trunc(f2Original) 4902 case wazeroir.OperationKindV128Nearest: 4903 expFs[0] = moremath.WasmCompatNearestF64(f1Original) 4904 expFs[1] = moremath.WasmCompatNearestF64(f2Original) 4905 } 4906 4907 for i := range expFs { 4908 exp, actual := expFs[i], actualFs[i] 4909 if math.IsNaN(exp) { 4910 require.True(t, math.IsNaN(actual)) 4911 } else { 4912 require.Equal(t, exp, actual) 4913 } 4914 } 4915 } 4916 }) 4917 } 4918 } 4919 4920 func TestCompiler_compileV128_Pmax_Pmin(t *testing.T) { 4921 tests := []struct { 4922 name string 4923 shape wazeroir.Shape 4924 kind wazeroir.OperationKind 4925 x1, x2, exp [16]byte 4926 }{ 4927 { 4928 name: "f32 pmin", 4929 shape: wazeroir.ShapeF32x4, 4930 kind: wazeroir.OperationKindV128Pmin, 4931 x1: f32x4(float32(math.Inf(1)), -1.5, 1123.5, float32(math.Inf(1))), 4932 x2: f32x4(1.4, float32(math.Inf(-1)), -1231.5, float32(math.Inf(1))), 4933 exp: f32x4(1.4, float32(math.Inf(-1)), -1231.5, float32(math.Inf(1))), 4934 }, 4935 { 4936 name: "f32 pmin", 4937 shape: wazeroir.ShapeF32x4, 4938 kind: wazeroir.OperationKindV128Pmin, 4939 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4940 x2: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4941 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4942 }, 4943 { 4944 name: "f32 pmin", 4945 shape: wazeroir.ShapeF32x4, 4946 kind: wazeroir.OperationKindV128Pmin, 4947 x1: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4948 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4949 exp: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 4950 }, 4951 { 4952 name: "f32 pmin", 4953 shape: wazeroir.ShapeF32x4, 4954 kind: wazeroir.OperationKindV128Pmin, 4955 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4956 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4957 exp: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4958 }, 4959 { 4960 name: "f32 pmin", 4961 shape: wazeroir.ShapeF32x4, 4962 kind: wazeroir.OperationKindV128Pmin, 4963 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4964 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 4965 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 4966 }, 4967 { 4968 name: "f64 pmin", 4969 shape: wazeroir.ShapeF64x2, 4970 kind: wazeroir.OperationKindV128Pmin, 4971 x1: f64x2(math.Inf(1), -123123.1231), 4972 x2: f64x2(-123123.1, math.Inf(-1)), 4973 exp: f64x2(-123123.1, math.Inf(-1)), 4974 }, 4975 { 4976 name: "f64 pmin", 4977 shape: wazeroir.ShapeF64x2, 4978 kind: wazeroir.OperationKindV128Pmin, 4979 x1: f64x2(math.NaN(), math.NaN()), 4980 x2: f64x2(-123123.1, 1.0), 4981 exp: f64x2(math.NaN(), math.NaN()), 4982 }, 4983 { 4984 name: "f64 pmin", 4985 shape: wazeroir.ShapeF64x2, 4986 kind: wazeroir.OperationKindV128Pmin, 4987 x1: f64x2(-123123.1, 1.0), 4988 x2: f64x2(math.NaN(), math.NaN()), 4989 exp: f64x2(-123123.1, 1.0), 4990 }, 4991 { 4992 name: "f64 pmin", 4993 shape: wazeroir.ShapeF64x2, 4994 kind: wazeroir.OperationKindV128Pmin, 4995 x1: f64x2(math.NaN(), math.NaN()), 4996 x2: f64x2(math.Inf(1), math.Inf(-1)), 4997 exp: f64x2(math.NaN(), math.NaN()), 4998 }, 4999 { 5000 name: "f64 pmin", 5001 shape: wazeroir.ShapeF64x2, 5002 kind: wazeroir.OperationKindV128Pmin, 5003 x1: f64x2(math.Inf(1), math.Inf(-1)), 5004 x2: f64x2(math.NaN(), math.NaN()), 5005 exp: f64x2(math.Inf(1), math.Inf(-1)), 5006 }, 5007 { 5008 name: "f32 pmax", 5009 shape: wazeroir.ShapeF32x4, 5010 kind: wazeroir.OperationKindV128Pmax, 5011 x1: f32x4(float32(math.Inf(1)), -1.5, 1123.5, float32(math.Inf(1))), 5012 x2: f32x4(1.4, float32(math.Inf(-1)), -1231.5, float32(math.Inf(1))), 5013 exp: f32x4(float32(math.Inf(1)), -1.5, 1123.5, float32(math.Inf(1))), 5014 }, 5015 { 5016 name: "f32 pmax", 5017 shape: wazeroir.ShapeF32x4, 5018 kind: wazeroir.OperationKindV128Pmax, 5019 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 5020 x2: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 5021 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 5022 }, 5023 { 5024 name: "f32 pmax", 5025 shape: wazeroir.ShapeF32x4, 5026 kind: wazeroir.OperationKindV128Pmax, 5027 x1: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 5028 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 5029 exp: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))), 5030 }, 5031 { 5032 name: "f32 pmax", 5033 shape: wazeroir.ShapeF32x4, 5034 kind: wazeroir.OperationKindV128Pmax, 5035 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 5036 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 5037 exp: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 5038 }, 5039 { 5040 name: "f32 pmax", 5041 shape: wazeroir.ShapeF32x4, 5042 kind: wazeroir.OperationKindV128Pmax, 5043 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 5044 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))), 5045 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())), 5046 }, 5047 { 5048 name: "f64 pmax", 5049 shape: wazeroir.ShapeF64x2, 5050 kind: wazeroir.OperationKindV128Pmax, 5051 x1: f64x2(math.Inf(1), -123123.1231), 5052 x2: f64x2(-123123.1, math.Inf(-1)), 5053 exp: f64x2(math.Inf(1), -123123.1231), 5054 }, 5055 { 5056 name: "f64 pmax", 5057 shape: wazeroir.ShapeF64x2, 5058 kind: wazeroir.OperationKindV128Pmax, 5059 x1: f64x2(math.NaN(), math.NaN()), 5060 x2: f64x2(-123123.1, 1.0), 5061 exp: f64x2(math.NaN(), math.NaN()), 5062 }, 5063 { 5064 name: "f64 pmax", 5065 shape: wazeroir.ShapeF64x2, 5066 kind: wazeroir.OperationKindV128Pmax, 5067 x1: f64x2(-123123.1, 1.0), 5068 x2: f64x2(math.NaN(), math.NaN()), 5069 exp: f64x2(-123123.1, 1.0), 5070 }, 5071 { 5072 name: "f64 pmax", 5073 shape: wazeroir.ShapeF64x2, 5074 kind: wazeroir.OperationKindV128Pmax, 5075 x1: f64x2(math.NaN(), math.NaN()), 5076 x2: f64x2(math.Inf(1), math.Inf(-1)), 5077 exp: f64x2(math.NaN(), math.NaN()), 5078 }, 5079 { 5080 name: "f64 pmax", 5081 shape: wazeroir.ShapeF64x2, 5082 kind: wazeroir.OperationKindV128Pmax, 5083 x1: f64x2(math.Inf(1), math.Inf(-1)), 5084 x2: f64x2(math.NaN(), math.NaN()), 5085 exp: f64x2(math.Inf(1), math.Inf(-1)), 5086 }, 5087 } 5088 5089 for _, tc := range tests { 5090 tc := tc 5091 t.Run(tc.name, func(t *testing.T) { 5092 env := newCompilerEnvironment() 5093 compiler := env.requireNewCompiler(t, newCompiler, 5094 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 5095 5096 err := compiler.compilePreamble() 5097 require.NoError(t, err) 5098 5099 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 5100 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 5101 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 5102 }) 5103 require.NoError(t, err) 5104 5105 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 5106 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 5107 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 5108 }) 5109 require.NoError(t, err) 5110 5111 is32bit := tc.shape == wazeroir.ShapeF32x4 5112 switch tc.kind { 5113 case wazeroir.OperationKindV128Pmin: 5114 err = compiler.compileV128Pmin(&wazeroir.OperationV128Pmin{Shape: tc.shape}) 5115 case wazeroir.OperationKindV128Pmax: 5116 err = compiler.compileV128Pmax(&wazeroir.OperationV128Pmax{Shape: tc.shape}) 5117 } 5118 require.NoError(t, err) 5119 5120 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 5121 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 5122 5123 err = compiler.compileReturnFunction() 5124 require.NoError(t, err) 5125 5126 // Generate and run the code under test. 5127 code, _, err := compiler.compile() 5128 require.NoError(t, err) 5129 env.exec(code) 5130 5131 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 5132 5133 lo, hi := env.stackTopAsV128() 5134 5135 if is32bit { 5136 actualFs := [4]float32{ 5137 math.Float32frombits(uint32(lo)), 5138 math.Float32frombits(uint32(lo >> 32)), 5139 math.Float32frombits(uint32(hi)), 5140 math.Float32frombits(uint32(hi >> 32)), 5141 } 5142 expFs := [4]float32{ 5143 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4])), 5144 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8])), 5145 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12])), 5146 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:])), 5147 } 5148 for i := range expFs { 5149 exp, actual := expFs[i], actualFs[i] 5150 if math.IsNaN(float64(exp)) { 5151 require.True(t, math.IsNaN(float64(actual))) 5152 } else { 5153 require.Equal(t, exp, actual) 5154 } 5155 } 5156 } else { 5157 actualFs := [2]float64{ 5158 math.Float64frombits(lo), math.Float64frombits(hi), 5159 } 5160 expFs := [2]float64{ 5161 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8])), 5162 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:])), 5163 } 5164 for i := range expFs { 5165 exp, actual := expFs[i], actualFs[i] 5166 if math.IsNaN(exp) { 5167 require.True(t, math.IsNaN(actual)) 5168 } else { 5169 require.Equal(t, exp, actual) 5170 } 5171 } 5172 } 5173 }) 5174 } 5175 } 5176 5177 func TestCompiler_compileV128ExtMul(t *testing.T) { 5178 tests := []struct { 5179 name string 5180 shape wazeroir.Shape 5181 signed, useLow bool 5182 x1, x2, exp [16]byte 5183 }{ 5184 { 5185 name: "i8x16s low", 5186 shape: wazeroir.ShapeI8x16, 5187 signed: true, 5188 useLow: true, 5189 x1: [16]byte{}, x2: [16]byte{}, 5190 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5191 }, 5192 { 5193 name: "i8x16s low", 5194 shape: wazeroir.ShapeI8x16, 5195 signed: true, 5196 useLow: true, 5197 x1: [16]byte{ 5198 255, 255, 255, 255, 255, 255, 255, 255, 5199 0, 0, 0, 0, 0, 0, 0, 0, 5200 }, 5201 x2: [16]byte{ 5202 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5203 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5204 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5205 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5206 }, 5207 exp: i16x8(128, 128, 128, 128, 128, 128, 128, 128), 5208 }, 5209 { 5210 name: "i8x16s low", 5211 shape: wazeroir.ShapeI8x16, 5212 signed: true, 5213 useLow: true, 5214 x1: [16]byte{ 5215 255, 255, 255, 255, 255, 255, 255, 255, 5216 0, 0, 0, 0, 0, 0, 0, 0, 5217 }, 5218 x2: [16]byte{ 5219 255, 255, 255, 255, 255, 255, 255, 255, 5220 0, 0, 0, 0, 0, 0, 0, 0, 5221 }, 5222 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 5223 }, 5224 { 5225 name: "i8x16s low", 5226 shape: wazeroir.ShapeI8x16, 5227 signed: true, 5228 useLow: true, 5229 x1: [16]byte{ 5230 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5231 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5232 0, 0, 0, 0, 0, 0, 0, 0, 5233 }, 5234 x2: [16]byte{ 5235 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5236 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5237 0, 0, 0, 0, 0, 0, 0, 0, 5238 }, 5239 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384), 5240 }, 5241 { 5242 name: "i8x16s hi", 5243 shape: wazeroir.ShapeI8x16, 5244 signed: true, 5245 useLow: false, 5246 x1: [16]byte{}, x2: [16]byte{}, 5247 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5248 }, 5249 { 5250 name: "i8x16s hi", 5251 shape: wazeroir.ShapeI8x16, 5252 signed: true, 5253 useLow: false, 5254 x1: [16]byte{ 5255 0, 0, 0, 0, 0, 0, 0, 0, 5256 255, 255, 255, 255, 255, 255, 255, 255, 5257 }, 5258 x2: [16]byte{ 5259 0, 0, 0, 0, 0, 0, 0, 0, 5260 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5261 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5262 }, 5263 exp: i16x8(128, 128, 128, 128, 128, 128, 128, 128), 5264 }, 5265 { 5266 name: "i8x16s hi", 5267 shape: wazeroir.ShapeI8x16, 5268 signed: true, 5269 useLow: false, 5270 x1: [16]byte{ 5271 0, 0, 0, 0, 0, 0, 0, 0, 5272 255, 255, 255, 255, 255, 255, 255, 255, 5273 }, 5274 x2: [16]byte{ 5275 0, 0, 0, 0, 0, 0, 0, 0, 5276 255, 255, 255, 255, 255, 255, 255, 255, 5277 }, 5278 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 5279 }, 5280 { 5281 name: "i8x16s hi", 5282 shape: wazeroir.ShapeI8x16, 5283 signed: true, 5284 useLow: false, 5285 x1: [16]byte{ 5286 0, 0, 0, 0, 0, 0, 0, 0, 5287 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5288 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5289 }, 5290 x2: [16]byte{ 5291 0, 0, 0, 0, 0, 0, 0, 0, 5292 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5293 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5294 }, 5295 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384), 5296 }, 5297 { 5298 name: "i8x16u low", 5299 shape: wazeroir.ShapeI8x16, 5300 signed: false, 5301 useLow: true, 5302 x1: [16]byte{}, x2: [16]byte{}, 5303 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5304 }, 5305 { 5306 name: "i8x16u low", 5307 shape: wazeroir.ShapeI8x16, 5308 signed: false, 5309 useLow: true, 5310 x1: [16]byte{ 5311 255, 255, 255, 255, 255, 255, 255, 255, 5312 0, 0, 0, 0, 0, 0, 0, 0, 5313 }, 5314 x2: [16]byte{ 5315 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5316 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5317 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5318 0, 0, 0, 0, 5319 }, 5320 exp: i16x8(32640, 32640, 32640, 32640, 32640, 32640, 32640, 32640), 5321 }, 5322 { 5323 name: "i8x16u low", 5324 shape: wazeroir.ShapeI8x16, 5325 signed: false, 5326 useLow: true, 5327 x1: [16]byte{ 5328 255, 255, 255, 255, 255, 255, 255, 255, 5329 0, 0, 0, 0, 0, 0, 0, 0, 5330 }, 5331 x2: [16]byte{ 5332 255, 255, 255, 255, 255, 255, 255, 255, 5333 0, 0, 0, 0, 0, 0, 0, 0, 5334 }, 5335 exp: i16x8(i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), 5336 i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511)), 5337 }, 5338 { 5339 name: "i8x16u low", 5340 shape: wazeroir.ShapeI8x16, 5341 signed: false, 5342 useLow: true, 5343 x1: [16]byte{ 5344 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5345 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5346 0, 0, 0, 0, 0, 0, 0, 0, 5347 }, 5348 x2: [16]byte{ 5349 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5350 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5351 0, 0, 0, 0, 0, 0, 0, 0, 5352 }, 5353 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384), 5354 }, 5355 { 5356 name: "i8x16u hi", 5357 shape: wazeroir.ShapeI8x16, 5358 signed: false, 5359 useLow: false, 5360 x1: [16]byte{}, x2: [16]byte{}, 5361 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5362 }, 5363 { 5364 name: "i8x16u hi", 5365 shape: wazeroir.ShapeI8x16, 5366 signed: false, 5367 useLow: false, 5368 x1: [16]byte{ 5369 0, 0, 0, 0, 0, 0, 0, 0, 5370 255, 255, 255, 255, 255, 255, 255, 255, 5371 }, 5372 x2: [16]byte{ 5373 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5374 0, 0, 0, 0, 5375 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5376 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5377 }, 5378 exp: i16x8(32640, 32640, 32640, 32640, 32640, 32640, 32640, 32640), 5379 }, 5380 { 5381 name: "i8x16u hi", 5382 shape: wazeroir.ShapeI8x16, 5383 signed: false, 5384 useLow: false, 5385 x1: [16]byte{ 5386 0, 0, 0, 0, 0, 0, 0, 0, 5387 255, 255, 255, 255, 255, 255, 255, 255, 5388 }, 5389 x2: [16]byte{ 5390 0, 0, 0, 0, 0, 0, 0, 0, 5391 255, 255, 255, 255, 255, 255, 255, 255, 5392 }, 5393 exp: i16x8(i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), 5394 i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511)), 5395 }, 5396 { 5397 name: "i8x16u hi", 5398 shape: wazeroir.ShapeI8x16, 5399 signed: false, 5400 useLow: false, 5401 x1: [16]byte{ 5402 0, 0, 0, 0, 0, 0, 0, 0, 5403 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5404 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5405 }, 5406 x2: [16]byte{ 5407 0, 0, 0, 0, 0, 0, 0, 0, 5408 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5409 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 5410 }, 5411 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384), 5412 }, 5413 { 5414 name: "i16x8s lo", 5415 shape: wazeroir.ShapeI16x8, 5416 signed: true, 5417 useLow: true, 5418 x1: [16]byte{}, 5419 x2: [16]byte{}, 5420 exp: [16]byte{}, 5421 }, 5422 { 5423 name: "i16x8s lo", 5424 shape: wazeroir.ShapeI16x8, 5425 signed: true, 5426 useLow: true, 5427 x1: i16x8( 5428 16383, 16383, 16383, 16383, 5429 0, 0, 1, 0, 5430 ), 5431 x2: i16x8( 5432 16384, 16384, 16384, 16384, 5433 0, 0, 1, 0, 5434 ), 5435 exp: i32x4(268419072, 268419072, 268419072, 268419072), 5436 }, 5437 { 5438 name: "i16x8s lo", 5439 shape: wazeroir.ShapeI16x8, 5440 signed: true, 5441 useLow: true, 5442 x1: i16x8( 5443 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 5444 0, 0, 1, 0, 5445 ), 5446 x2: i16x8( 5447 i16ToU16(-32767), 0, i16ToU16(-32767), 0, 5448 0, 0, 1, 0, 5449 ), 5450 exp: i32x4(1073709056, 0, 1073709056, 0), 5451 }, 5452 { 5453 name: "i16x8s lo", 5454 shape: wazeroir.ShapeI16x8, 5455 signed: true, 5456 useLow: true, 5457 x1: i16x8( 5458 65535, 65535, 65535, 65535, 5459 0, 0, 1, 0, 5460 ), 5461 x2: i16x8( 5462 65535, 0, 65535, 0, 5463 0, 0, 1, 0, 5464 ), 5465 exp: i32x4(1, 0, 1, 0), 5466 }, 5467 { 5468 name: "i16x8s hi", 5469 shape: wazeroir.ShapeI16x8, 5470 signed: true, 5471 useLow: false, 5472 x1: [16]byte{}, 5473 x2: [16]byte{}, 5474 exp: [16]byte{}, 5475 }, 5476 { 5477 name: "i16x8s hi", 5478 shape: wazeroir.ShapeI16x8, 5479 signed: true, 5480 useLow: false, 5481 x1: i16x8( 5482 0, 0, 1, 0, 5483 16383, 16383, 16383, 16383, 5484 ), 5485 x2: i16x8( 5486 0, 0, 1, 0, 5487 16384, 16384, 16384, 16384, 5488 ), 5489 exp: i32x4(268419072, 268419072, 268419072, 268419072), 5490 }, 5491 { 5492 name: "i16x8s hi", 5493 shape: wazeroir.ShapeI16x8, 5494 signed: true, 5495 useLow: false, 5496 x1: i16x8( 5497 0, 0, 1, 0, 5498 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 5499 ), 5500 x2: i16x8( 5501 0, 0, 1, 0, 5502 i16ToU16(-32767), 0, i16ToU16(-32767), 0, 5503 ), 5504 exp: i32x4(1073709056, 0, 1073709056, 0), 5505 }, 5506 { 5507 name: "i16x8s hi", 5508 shape: wazeroir.ShapeI16x8, 5509 signed: true, 5510 useLow: false, 5511 x1: i16x8( 5512 0, 0, 1, 0, 5513 65535, 65535, 65535, 65535, 5514 ), 5515 x2: i16x8( 5516 0, 0, 1, 0, 5517 5518 65535, 0, 65535, 0, 5519 ), 5520 exp: i32x4(1, 0, 1, 0), 5521 }, 5522 { 5523 name: "i16x8u lo", 5524 shape: wazeroir.ShapeI16x8, 5525 signed: false, 5526 useLow: true, 5527 x1: [16]byte{}, 5528 x2: [16]byte{}, 5529 exp: [16]byte{}, 5530 }, 5531 { 5532 name: "i16x8u lo", 5533 shape: wazeroir.ShapeI16x8, 5534 signed: false, 5535 useLow: true, 5536 x1: i16x8( 5537 16383, 16383, 16383, 16383, 5538 0, 0, 1, 0, 5539 ), 5540 x2: i16x8( 5541 16384, 16384, 16384, 16384, 5542 0, 0, 1, 0, 5543 ), 5544 exp: i32x4(268419072, 268419072, 268419072, 268419072), 5545 }, 5546 { 5547 name: "i16x8u lo", 5548 shape: wazeroir.ShapeI16x8, 5549 signed: false, 5550 useLow: true, 5551 x1: i16x8( 5552 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 5553 0, 0, 1, 0, 5554 ), 5555 x2: i16x8( 5556 i16ToU16(-32767), 0, i16ToU16(-32767), 0, 5557 0, 0, 1, 0, 5558 ), 5559 exp: i32x4(1073774592, 0, 1073774592, 0), 5560 }, 5561 { 5562 name: "i16x8u lo", 5563 shape: wazeroir.ShapeI16x8, 5564 signed: false, 5565 useLow: true, 5566 x1: i16x8( 5567 65535, 65535, 65535, 65535, 5568 0, 0, 1, 0, 5569 ), 5570 x2: i16x8( 5571 65535, 0, 65535, 0, 5572 0, 0, 1, 0, 5573 ), 5574 exp: i32x4(i32ToU32(-131071), 0, i32ToU32(-131071), 0), 5575 }, 5576 { 5577 name: "i16x8u hi", 5578 shape: wazeroir.ShapeI16x8, 5579 signed: false, 5580 useLow: false, 5581 x1: [16]byte{}, 5582 x2: [16]byte{}, 5583 exp: [16]byte{}, 5584 }, 5585 { 5586 name: "i16x8u hi", 5587 shape: wazeroir.ShapeI16x8, 5588 signed: false, 5589 useLow: false, 5590 x1: i16x8( 5591 0, 0, 1, 0, 5592 16383, 16383, 16383, 16383, 5593 ), 5594 x2: i16x8( 5595 0, 0, 1, 0, 5596 16384, 16384, 16384, 16384, 5597 ), 5598 exp: i32x4(268419072, 268419072, 268419072, 268419072), 5599 }, 5600 { 5601 name: "i16x8u hi", 5602 shape: wazeroir.ShapeI16x8, 5603 signed: false, 5604 useLow: false, 5605 x1: i16x8( 5606 0, 0, 1, 0, 5607 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 5608 ), 5609 x2: i16x8( 5610 0, 0, 1, 0, 5611 i16ToU16(-32767), 0, i16ToU16(-32767), 0, 5612 ), 5613 exp: i32x4(1073774592, 0, 1073774592, 0), 5614 }, 5615 { 5616 name: "i16x8u hi", 5617 shape: wazeroir.ShapeI16x8, 5618 signed: false, 5619 useLow: false, 5620 x1: i16x8( 5621 0, 0, 1, 0, 5622 65535, 65535, 65535, 65535, 5623 ), 5624 x2: i16x8( 5625 0, 0, 1, 0, 5626 65535, 0, 65535, 0, 5627 ), 5628 exp: i32x4(i32ToU32(-131071), 0, i32ToU32(-131071), 0), 5629 }, 5630 { 5631 name: "i32x4s lo", 5632 shape: wazeroir.ShapeI32x4, 5633 signed: true, 5634 useLow: true, 5635 x1: [16]byte{}, 5636 x2: [16]byte{}, 5637 exp: [16]byte{}, 5638 }, 5639 { 5640 name: "i32x4s lo", 5641 shape: wazeroir.ShapeI32x4, 5642 signed: true, 5643 useLow: true, 5644 x1: i32x4( 5645 1, i32ToU32(-1), 5646 0, 0, 5647 ), 5648 x2: i32x4( 5649 i32ToU32(-1), 1, 5650 0, 0, 5651 ), 5652 exp: i64x2(i64ToU64(-1), i64ToU64(-1)), 5653 }, 5654 { 5655 name: "i32x4s lo", 5656 shape: wazeroir.ShapeI32x4, 5657 signed: true, 5658 useLow: true, 5659 x1: i32x4( 5660 1073741824, 4294967295, 5661 0, 0, 5662 ), 5663 x2: i32x4( 5664 1073741824, 4294967295, 5665 0, 0, 5666 ), 5667 exp: i64x2(1152921504606846976, 1), 5668 }, 5669 { 5670 name: "i32x4s hi", 5671 shape: wazeroir.ShapeI32x4, 5672 signed: true, 5673 useLow: false, 5674 x1: [16]byte{}, 5675 x2: [16]byte{}, 5676 exp: [16]byte{}, 5677 }, 5678 { 5679 name: "i32x4s hi", 5680 shape: wazeroir.ShapeI32x4, 5681 signed: true, 5682 useLow: false, 5683 x1: i32x4( 5684 0, 0, 5685 1, i32ToU32(-1), 5686 ), 5687 x2: i32x4( 5688 0, 0, 5689 i32ToU32(-1), 1, 5690 ), 5691 exp: i64x2(i64ToU64(-1), i64ToU64(-1)), 5692 }, 5693 { 5694 name: "i32x4s hi", 5695 shape: wazeroir.ShapeI32x4, 5696 signed: true, 5697 useLow: false, 5698 x1: i32x4( 5699 0, 0, 5700 1073741824, 4294967295, 5701 ), 5702 x2: i32x4( 5703 0, 0, 5704 1073741824, 4294967295, 5705 ), 5706 exp: i64x2(1152921504606846976, 1), 5707 }, 5708 { 5709 name: "i32x4u lo", 5710 shape: wazeroir.ShapeI32x4, 5711 signed: false, 5712 useLow: true, 5713 x1: [16]byte{}, 5714 x2: [16]byte{}, 5715 exp: [16]byte{}, 5716 }, 5717 { 5718 name: "i32x4u lo", 5719 shape: wazeroir.ShapeI32x4, 5720 signed: false, 5721 useLow: true, 5722 x1: i32x4( 5723 1, i32ToU32(-1), 5724 0, 0, 5725 ), 5726 x2: i32x4( 5727 i32ToU32(-1), 1, 5728 0, 0, 5729 ), 5730 exp: i64x2(4294967295, 4294967295), 5731 }, 5732 { 5733 name: "i32x4u lo", 5734 shape: wazeroir.ShapeI32x4, 5735 signed: false, 5736 useLow: true, 5737 x1: i32x4( 5738 1073741824, 4294967295, 5739 0, 0, 5740 ), 5741 x2: i32x4( 5742 1073741824, 4294967295, 5743 0, 0, 5744 ), 5745 exp: i64x2(1152921504606846976, i64ToU64(-8589934591)), 5746 }, 5747 { 5748 name: "i32x4u hi", 5749 shape: wazeroir.ShapeI32x4, 5750 signed: false, 5751 useLow: false, 5752 x1: [16]byte{}, 5753 x2: [16]byte{}, 5754 exp: [16]byte{}, 5755 }, 5756 { 5757 name: "i32x4u hi", 5758 shape: wazeroir.ShapeI32x4, 5759 signed: false, 5760 useLow: false, 5761 x1: i32x4( 5762 0, 0, 5763 1, i32ToU32(-1), 5764 ), 5765 x2: i32x4( 5766 0, 0, 5767 i32ToU32(-1), 1, 5768 ), 5769 exp: i64x2(4294967295, 4294967295), 5770 }, 5771 { 5772 name: "i32x4u hi", 5773 shape: wazeroir.ShapeI32x4, 5774 signed: false, 5775 useLow: false, 5776 x1: i32x4( 5777 0, 0, 5778 1073741824, 4294967295, 5779 ), 5780 x2: i32x4( 5781 0, 0, 5782 1073741824, 4294967295, 5783 ), 5784 exp: i64x2(1152921504606846976, i64ToU64(-8589934591)), 5785 }, 5786 } 5787 5788 for _, tc := range tests { 5789 tc := tc 5790 t.Run(tc.name, func(t *testing.T) { 5791 env := newCompilerEnvironment() 5792 compiler := env.requireNewCompiler(t, newCompiler, 5793 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 5794 5795 err := compiler.compilePreamble() 5796 require.NoError(t, err) 5797 5798 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 5799 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 5800 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 5801 }) 5802 require.NoError(t, err) 5803 5804 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 5805 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 5806 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 5807 }) 5808 require.NoError(t, err) 5809 5810 err = compiler.compileV128ExtMul(&wazeroir.OperationV128ExtMul{ 5811 OriginShape: tc.shape, Signed: tc.signed, UseLow: tc.useLow, 5812 }) 5813 require.NoError(t, err) 5814 5815 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 5816 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 5817 5818 err = compiler.compileReturnFunction() 5819 require.NoError(t, err) 5820 5821 // Generate and run the code under test. 5822 code, _, err := compiler.compile() 5823 require.NoError(t, err) 5824 env.exec(code) 5825 5826 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 5827 5828 lo, hi := env.stackTopAsV128() 5829 var actual [16]byte 5830 binary.LittleEndian.PutUint64(actual[:8], lo) 5831 binary.LittleEndian.PutUint64(actual[8:], hi) 5832 require.Equal(t, tc.exp, actual) 5833 }) 5834 } 5835 } 5836 5837 func TestCompiler_compileV128Extend(t *testing.T) { 5838 tests := []struct { 5839 name string 5840 shape wazeroir.Shape 5841 signed, useLow bool 5842 v, exp [16]byte 5843 }{ 5844 { 5845 name: "i8x16s hi", 5846 shape: wazeroir.ShapeI8x16, 5847 signed: true, 5848 useLow: false, 5849 v: [16]byte{}, 5850 exp: [16]byte{}, 5851 }, 5852 { 5853 name: "i8x16s hi", 5854 shape: wazeroir.ShapeI8x16, 5855 signed: true, 5856 useLow: false, 5857 v: [16]byte{ 5858 0, 0, 0, 0, 0, 0, 0, 0, 5859 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5860 }, 5861 exp: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)), 5862 }, 5863 { 5864 name: "i8x16s hi", 5865 shape: wazeroir.ShapeI8x16, 5866 signed: true, 5867 useLow: false, 5868 v: [16]byte{ 5869 0, 0, 0, 0, 0, 0, 0, 0, 5870 1, 1, 1, 1, 1, 1, 1, 1, 5871 }, 5872 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 5873 }, 5874 { 5875 name: "i8x16s hi", 5876 shape: wazeroir.ShapeI8x16, 5877 signed: true, 5878 useLow: false, 5879 v: [16]byte{ 5880 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5881 0, 0, 0, 0, 0, 0, 0, 0, 5882 }, 5883 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5884 }, 5885 { 5886 name: "i8x16s lo", 5887 shape: wazeroir.ShapeI8x16, 5888 signed: true, 5889 useLow: true, 5890 v: [16]byte{}, 5891 exp: [16]byte{}, 5892 }, 5893 { 5894 name: "i8x16s lo", 5895 shape: wazeroir.ShapeI8x16, 5896 signed: true, 5897 useLow: true, 5898 v: [16]byte{ 5899 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5900 0, 0, 0, 0, 0, 0, 0, 0, 5901 }, 5902 exp: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)), 5903 }, 5904 { 5905 name: "i8x16s lo", 5906 shape: wazeroir.ShapeI8x16, 5907 signed: true, 5908 useLow: true, 5909 v: [16]byte{ 5910 1, 1, 1, 1, 1, 1, 1, 1, 5911 0, 0, 0, 0, 0, 0, 0, 0, 5912 }, 5913 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 5914 }, 5915 { 5916 name: "i8x16s lo", 5917 shape: wazeroir.ShapeI8x16, 5918 signed: true, 5919 useLow: true, 5920 v: [16]byte{ 5921 0, 0, 0, 0, 0, 0, 0, 0, 5922 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5923 }, 5924 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5925 }, 5926 // unsigned 5927 { 5928 name: "i8x16u hi", 5929 shape: wazeroir.ShapeI8x16, 5930 signed: false, 5931 useLow: false, 5932 v: [16]byte{}, 5933 exp: [16]byte{}, 5934 }, 5935 { 5936 name: "i8x16u hi", 5937 shape: wazeroir.ShapeI8x16, 5938 signed: false, 5939 useLow: false, 5940 v: [16]byte{ 5941 0, 0, 0, 0, 0, 0, 0, 0, 5942 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5943 }, 5944 exp: i16x8(255, 255, 255, 255, 255, 255, 255, 255), 5945 }, 5946 { 5947 name: "i8x16u hi", 5948 shape: wazeroir.ShapeI8x16, 5949 signed: false, 5950 useLow: false, 5951 v: [16]byte{ 5952 0, 0, 0, 0, 0, 0, 0, 0, 5953 1, 1, 1, 1, 1, 1, 1, 1, 5954 }, 5955 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 5956 }, 5957 { 5958 name: "i8x16u hi", 5959 shape: wazeroir.ShapeI8x16, 5960 signed: false, 5961 useLow: false, 5962 v: [16]byte{ 5963 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5964 0, 0, 0, 0, 0, 0, 0, 0, 5965 }, 5966 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 5967 }, 5968 { 5969 name: "i8x16u lo", 5970 shape: wazeroir.ShapeI8x16, 5971 signed: false, 5972 useLow: true, 5973 v: [16]byte{}, 5974 exp: [16]byte{}, 5975 }, 5976 { 5977 name: "i8x16u lo", 5978 shape: wazeroir.ShapeI8x16, 5979 signed: false, 5980 useLow: true, 5981 v: [16]byte{ 5982 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 5983 0, 0, 0, 0, 0, 0, 0, 0, 5984 }, 5985 exp: i16x8(255, 255, 255, 255, 255, 255, 255, 255), 5986 }, 5987 { 5988 name: "i8x16u lo", 5989 shape: wazeroir.ShapeI8x16, 5990 signed: false, 5991 useLow: true, 5992 v: [16]byte{ 5993 1, 1, 1, 1, 1, 1, 1, 1, 5994 0, 0, 0, 0, 0, 0, 0, 0, 5995 }, 5996 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 5997 }, 5998 { 5999 name: "i8x16u lo", 6000 shape: wazeroir.ShapeI8x16, 6001 signed: false, 6002 useLow: true, 6003 v: [16]byte{ 6004 0, 0, 0, 0, 0, 0, 0, 0, 6005 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 6006 }, 6007 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6008 }, 6009 { 6010 name: "i16x8s hi", 6011 shape: wazeroir.ShapeI16x8, 6012 signed: true, 6013 useLow: false, 6014 v: [16]byte{}, 6015 exp: [16]byte{}, 6016 }, 6017 { 6018 name: "i16x8s hi", 6019 shape: wazeroir.ShapeI16x8, 6020 signed: true, 6021 useLow: false, 6022 v: i16x8(1, 1, 1, 1, 0, 0, 0, 0), 6023 exp: i32x4(0, 0, 0, 0), 6024 }, 6025 { 6026 name: "i16x8s hi", 6027 shape: wazeroir.ShapeI16x8, 6028 signed: true, 6029 useLow: false, 6030 v: i16x8(0, 0, 0, 0, i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)), 6031 exp: i32x4(i32ToU32(-1), i32ToU32(-1), i32ToU32(-1), i32ToU32(-1)), 6032 }, 6033 { 6034 name: "i16x8s hi", 6035 shape: wazeroir.ShapeI16x8, 6036 signed: true, 6037 useLow: false, 6038 v: i16x8(0, 0, 0, 0, 123, 0, 123, 0), 6039 exp: i32x4(123, 0, 123, 0), 6040 }, 6041 { 6042 name: "i16x8s lo", 6043 shape: wazeroir.ShapeI16x8, 6044 signed: true, 6045 useLow: true, 6046 v: [16]byte{}, 6047 exp: [16]byte{}, 6048 }, 6049 { 6050 name: "i16x8s lo", 6051 shape: wazeroir.ShapeI16x8, 6052 signed: true, 6053 useLow: true, 6054 v: i16x8(0, 0, 0, 0, 1, 1, 1, 1), 6055 exp: i32x4(0, 0, 0, 0), 6056 }, 6057 { 6058 name: "i16x8s lo", 6059 shape: wazeroir.ShapeI16x8, 6060 signed: true, 6061 useLow: true, 6062 v: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 0, 0, 0, 0), 6063 exp: i32x4(i32ToU32(-1), i32ToU32(-1), i32ToU32(-1), i32ToU32(-1)), 6064 }, 6065 { 6066 name: "i16x8s lo", 6067 shape: wazeroir.ShapeI16x8, 6068 signed: true, 6069 useLow: true, 6070 v: i16x8(123, 0, 123, 0, 0, 0, 0, 0), 6071 exp: i32x4(123, 0, 123, 0), 6072 }, 6073 { 6074 name: "i16x8u hi", 6075 shape: wazeroir.ShapeI16x8, 6076 signed: false, 6077 useLow: false, 6078 v: [16]byte{}, 6079 exp: [16]byte{}, 6080 }, 6081 { 6082 name: "i16x8u hi", 6083 shape: wazeroir.ShapeI16x8, 6084 signed: false, 6085 useLow: false, 6086 v: i16x8(1, 1, 1, 1, 0, 0, 0, 0), 6087 exp: i32x4(0, 0, 0, 0), 6088 }, 6089 { 6090 name: "i16x8u hi", 6091 shape: wazeroir.ShapeI16x8, 6092 signed: false, 6093 useLow: false, 6094 v: i16x8(0, 0, 0, 0, i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)), 6095 exp: i32x4(65535, 65535, 65535, 65535), 6096 }, 6097 { 6098 name: "i16x8u hi", 6099 shape: wazeroir.ShapeI16x8, 6100 signed: false, 6101 useLow: false, 6102 v: i16x8(0, 0, 0, 0, 123, 0, 123, 0), 6103 exp: i32x4(123, 0, 123, 0), 6104 }, 6105 { 6106 name: "i16x8u lo", 6107 shape: wazeroir.ShapeI16x8, 6108 signed: false, 6109 useLow: true, 6110 v: [16]byte{}, 6111 exp: [16]byte{}, 6112 }, 6113 { 6114 name: "i16x8u lo", 6115 shape: wazeroir.ShapeI16x8, 6116 signed: false, 6117 useLow: true, 6118 v: i16x8(0, 0, 0, 0, 1, 1, 1, 1), 6119 exp: i32x4(0, 0, 0, 0), 6120 }, 6121 { 6122 name: "i16x8u lo", 6123 shape: wazeroir.ShapeI16x8, 6124 signed: false, 6125 useLow: true, 6126 v: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 0, 0, 0, 0), 6127 exp: i32x4(65535, 65535, 65535, 65535), 6128 }, 6129 { 6130 name: "i16x8u lo", 6131 shape: wazeroir.ShapeI16x8, 6132 signed: false, 6133 useLow: true, 6134 v: i16x8(123, 0, 123, 0, 0, 0, 0, 0), 6135 exp: i32x4(123, 0, 123, 0), 6136 }, 6137 { 6138 name: "i32x4s hi", 6139 shape: wazeroir.ShapeI32x4, 6140 signed: true, 6141 useLow: false, 6142 v: [16]byte{}, 6143 exp: [16]byte{}, 6144 }, 6145 { 6146 name: "i32x4s hi", 6147 shape: wazeroir.ShapeI32x4, 6148 signed: true, 6149 useLow: false, 6150 v: i32x4(0, 0, 1, i32ToU32(-1)), 6151 exp: i64x2(1, i64ToU64(-1)), 6152 }, 6153 { 6154 name: "i32x4s hi", 6155 shape: wazeroir.ShapeI32x4, 6156 signed: true, 6157 useLow: false, 6158 v: i32x4(1, i32ToU32(-1), 0, 0), 6159 exp: i64x2(0, 0), 6160 }, 6161 { 6162 name: "i32x4s hi", 6163 shape: wazeroir.ShapeI32x4, 6164 signed: true, 6165 useLow: false, 6166 v: i32x4(1, i32ToU32(-1), 123, 123), 6167 exp: i64x2(123, 123), 6168 }, 6169 { 6170 name: "i32x4s lo", 6171 shape: wazeroir.ShapeI32x4, 6172 signed: true, 6173 useLow: true, 6174 v: [16]byte{}, 6175 exp: [16]byte{}, 6176 }, 6177 { 6178 name: "i32x4s lo", 6179 shape: wazeroir.ShapeI32x4, 6180 signed: true, 6181 useLow: true, 6182 v: i32x4(1, i32ToU32(-1), 0, 0), 6183 exp: i64x2(1, i64ToU64(-1)), 6184 }, 6185 { 6186 name: "i32x4s lo", 6187 shape: wazeroir.ShapeI32x4, 6188 signed: true, 6189 useLow: true, 6190 v: i32x4(0, 0, 1, i32ToU32(-1)), 6191 exp: i64x2(0, 0), 6192 }, 6193 { 6194 name: "i32x4s lo", 6195 shape: wazeroir.ShapeI32x4, 6196 signed: true, 6197 useLow: true, 6198 v: i32x4(123, 123, 1, i32ToU32(-1)), 6199 exp: i64x2(123, 123), 6200 }, 6201 { 6202 name: "i32x4u hi", 6203 shape: wazeroir.ShapeI32x4, 6204 signed: false, 6205 useLow: false, 6206 v: [16]byte{}, 6207 exp: [16]byte{}, 6208 }, 6209 { 6210 name: "i32x4u hi", 6211 shape: wazeroir.ShapeI32x4, 6212 signed: false, 6213 useLow: false, 6214 v: i32x4(0, 0, 1, i32ToU32(-1)), 6215 exp: i64x2(1, 4294967295), 6216 }, 6217 { 6218 name: "i32x4u hi", 6219 shape: wazeroir.ShapeI32x4, 6220 signed: false, 6221 useLow: false, 6222 v: i32x4(1, i32ToU32(-1), 0, 0), 6223 exp: i64x2(0, 0), 6224 }, 6225 { 6226 name: "i32x4u hi", 6227 shape: wazeroir.ShapeI32x4, 6228 signed: false, 6229 useLow: false, 6230 v: i32x4(1, i32ToU32(-1), 123, 123), 6231 exp: i64x2(123, 123), 6232 }, 6233 { 6234 name: "i32x4u lo", 6235 shape: wazeroir.ShapeI32x4, 6236 signed: false, 6237 useLow: true, 6238 v: [16]byte{}, 6239 exp: [16]byte{}, 6240 }, 6241 { 6242 name: "i32x4u lo", 6243 shape: wazeroir.ShapeI32x4, 6244 signed: false, 6245 useLow: true, 6246 v: i32x4(1, i32ToU32(-1), 0, 0), 6247 exp: i64x2(1, 4294967295), 6248 }, 6249 { 6250 name: "i32x4u lo", 6251 shape: wazeroir.ShapeI32x4, 6252 signed: false, 6253 useLow: true, 6254 v: i32x4(0, 0, 1, i32ToU32(-1)), 6255 exp: i64x2(0, 0), 6256 }, 6257 { 6258 name: "i32x4u lo", 6259 shape: wazeroir.ShapeI32x4, 6260 signed: false, 6261 useLow: true, 6262 v: i32x4(123, 123, 1, i32ToU32(-1)), 6263 exp: i64x2(123, 123), 6264 }, 6265 } 6266 6267 for _, tc := range tests { 6268 tc := tc 6269 t.Run(tc.name, func(t *testing.T) { 6270 env := newCompilerEnvironment() 6271 compiler := env.requireNewCompiler(t, newCompiler, 6272 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 6273 6274 err := compiler.compilePreamble() 6275 require.NoError(t, err) 6276 6277 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6278 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 6279 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 6280 }) 6281 require.NoError(t, err) 6282 6283 err = compiler.compileV128Extend(&wazeroir.OperationV128Extend{ 6284 OriginShape: tc.shape, Signed: tc.signed, UseLow: tc.useLow, 6285 }) 6286 require.NoError(t, err) 6287 6288 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 6289 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 6290 6291 err = compiler.compileReturnFunction() 6292 require.NoError(t, err) 6293 6294 // Generate and run the code under test. 6295 code, _, err := compiler.compile() 6296 require.NoError(t, err) 6297 env.exec(code) 6298 6299 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 6300 6301 lo, hi := env.stackTopAsV128() 6302 var actual [16]byte 6303 binary.LittleEndian.PutUint64(actual[:8], lo) 6304 binary.LittleEndian.PutUint64(actual[8:], hi) 6305 require.Equal(t, tc.exp, actual) 6306 }) 6307 } 6308 } 6309 6310 func TestCompiler_compileV128Q15mulrSatS(t *testing.T) { 6311 tests := []struct { 6312 name string 6313 x1, x2, exp [16]byte 6314 }{ 6315 { 6316 name: "1", 6317 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6318 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6319 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6320 }, 6321 { 6322 name: "2", 6323 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6324 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6325 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6326 }, 6327 { 6328 name: "3", 6329 x1: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6330 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6331 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6332 }, 6333 { 6334 name: "4", 6335 x1: i16x8(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535), 6336 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6337 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6338 }, 6339 { 6340 name: "5", 6341 x1: i16x8(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767), 6342 x2: i16x8(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767), 6343 exp: i16x8(32766, 32766, 32766, 32766, 32766, 32766, 32766, 32766), 6344 }, 6345 } 6346 6347 for _, tc := range tests { 6348 tc := tc 6349 t.Run(tc.name, func(t *testing.T) { 6350 env := newCompilerEnvironment() 6351 compiler := env.requireNewCompiler(t, newCompiler, 6352 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 6353 6354 err := compiler.compilePreamble() 6355 require.NoError(t, err) 6356 6357 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6358 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 6359 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 6360 }) 6361 require.NoError(t, err) 6362 6363 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6364 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 6365 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 6366 }) 6367 require.NoError(t, err) 6368 6369 err = compiler.compileV128Q15mulrSatS(&wazeroir.OperationV128Q15mulrSatS{}) 6370 require.NoError(t, err) 6371 6372 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 6373 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 6374 6375 err = compiler.compileReturnFunction() 6376 require.NoError(t, err) 6377 6378 // Generate and run the code under test. 6379 code, _, err := compiler.compile() 6380 require.NoError(t, err) 6381 env.exec(code) 6382 6383 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 6384 6385 lo, hi := env.stackTopAsV128() 6386 var actual [16]byte 6387 binary.LittleEndian.PutUint64(actual[:8], lo) 6388 binary.LittleEndian.PutUint64(actual[8:], hi) 6389 require.Equal(t, tc.exp, actual) 6390 }) 6391 } 6392 } 6393 6394 func TestCompiler_compileFloatPromote(t *testing.T) { 6395 tests := []struct { 6396 name string 6397 v, exp [16]byte 6398 }{ 6399 { 6400 name: "1", 6401 v: f32x4(float32(0x1.8f867ep+125), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)), 6402 exp: f64x2(6.6382536710104395e+37, 6.6382536710104395e+37), 6403 }, 6404 { 6405 name: "2", 6406 v: f32x4(float32(0x1.8f867ep+125), float32(0x1.8f867ep+125), 0, 0), 6407 exp: f64x2(6.6382536710104395e+37, 6.6382536710104395e+37), 6408 }, 6409 { 6410 name: "3", 6411 v: f32x4(0, 0, float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)), 6412 exp: f64x2(0, 0), 6413 }, 6414 { 6415 name: "4", 6416 v: f32x4(float32(math.NaN()), float32(math.NaN()), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)), 6417 exp: f64x2(math.NaN(), math.NaN()), 6418 }, 6419 { 6420 name: "5", 6421 v: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)), 6422 exp: f64x2(math.Inf(1), math.Inf(-1)), 6423 }, 6424 } 6425 6426 for _, tc := range tests { 6427 tc := tc 6428 t.Run(tc.name, func(t *testing.T) { 6429 env := newCompilerEnvironment() 6430 compiler := env.requireNewCompiler(t, newCompiler, 6431 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 6432 6433 err := compiler.compilePreamble() 6434 require.NoError(t, err) 6435 6436 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6437 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 6438 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 6439 }) 6440 require.NoError(t, err) 6441 6442 err = compiler.compileV128FloatPromote(&wazeroir.OperationV128FloatPromote{}) 6443 require.NoError(t, err) 6444 6445 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 6446 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 6447 6448 err = compiler.compileReturnFunction() 6449 require.NoError(t, err) 6450 6451 // Generate and run the code under test. 6452 code, _, err := compiler.compile() 6453 require.NoError(t, err) 6454 env.exec(code) 6455 6456 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 6457 6458 lo, hi := env.stackTopAsV128() 6459 actualFs := [2]float64{ 6460 math.Float64frombits(lo), math.Float64frombits(hi), 6461 } 6462 expFs := [2]float64{ 6463 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8])), 6464 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:])), 6465 } 6466 for i := range expFs { 6467 exp, actual := expFs[i], actualFs[i] 6468 if math.IsNaN(exp) { 6469 require.True(t, math.IsNaN(actual)) 6470 } else { 6471 require.Equal(t, exp, actual) 6472 } 6473 } 6474 }) 6475 } 6476 } 6477 6478 func TestCompiler_compileV128FloatDemote(t *testing.T) { 6479 tests := []struct { 6480 name string 6481 v, exp [16]byte 6482 }{ 6483 { 6484 name: "1", 6485 v: f64x2(0, 0), 6486 exp: f32x4(0, 0, 0, 0), 6487 }, 6488 { 6489 name: "2", 6490 v: f64x2(0x1.fffffe0000000p-127, 0x1.fffffe0000000p-127), 6491 exp: f32x4(0x1p-126, 0x1p-126, 0, 0), 6492 }, 6493 { 6494 name: "3", 6495 v: f64x2(0x1.fffffep+127, 0x1.fffffep+127), 6496 exp: f32x4(0x1.fffffep+127, 0x1.fffffep+127, 0, 0), 6497 }, 6498 { 6499 name: "4", 6500 v: f64x2(math.NaN(), math.NaN()), 6501 exp: f32x4(float32(math.NaN()), float32(math.NaN()), 0, 0), 6502 }, 6503 { 6504 name: "5", 6505 v: f64x2(math.Inf(1), math.Inf(-1)), 6506 exp: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), 0, 0), 6507 }, 6508 } 6509 6510 for _, tc := range tests { 6511 tc := tc 6512 t.Run(tc.name, func(t *testing.T) { 6513 env := newCompilerEnvironment() 6514 compiler := env.requireNewCompiler(t, newCompiler, 6515 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 6516 6517 err := compiler.compilePreamble() 6518 require.NoError(t, err) 6519 6520 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6521 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 6522 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 6523 }) 6524 require.NoError(t, err) 6525 6526 err = compiler.compileV128FloatDemote(&wazeroir.OperationV128FloatDemote{}) 6527 require.NoError(t, err) 6528 6529 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 6530 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 6531 6532 err = compiler.compileReturnFunction() 6533 require.NoError(t, err) 6534 6535 // Generate and run the code under test. 6536 code, _, err := compiler.compile() 6537 require.NoError(t, err) 6538 env.exec(code) 6539 6540 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 6541 6542 lo, hi := env.stackTopAsV128() 6543 actualFs := [4]float32{ 6544 math.Float32frombits(uint32(lo)), 6545 math.Float32frombits(uint32(lo >> 32)), 6546 math.Float32frombits(uint32(hi)), 6547 math.Float32frombits(uint32(hi >> 32)), 6548 } 6549 expFs := [4]float32{ 6550 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4])), 6551 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8])), 6552 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12])), 6553 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:])), 6554 } 6555 for i := range expFs { 6556 exp, actual := expFs[i], actualFs[i] 6557 if math.IsNaN(float64(exp)) { 6558 require.True(t, math.IsNaN(float64(actual))) 6559 } else { 6560 require.Equal(t, exp, actual) 6561 } 6562 } 6563 }) 6564 } 6565 } 6566 6567 func TestCompiler_compileV128ExtAddPairwise(t *testing.T) { 6568 tests := []struct { 6569 name string 6570 shape wazeroir.Shape 6571 signed bool 6572 v, exp [16]byte 6573 }{ 6574 { 6575 name: "i8x16 s", 6576 shape: wazeroir.ShapeI8x16, 6577 signed: true, 6578 v: [16]byte{}, 6579 exp: [16]byte{}, 6580 }, 6581 { 6582 name: "i8x16 s", 6583 shape: wazeroir.ShapeI8x16, 6584 signed: true, 6585 v: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 6586 exp: i16x8(2, 2, 2, 2, 2, 2, 2, 2), 6587 }, 6588 { 6589 name: "i8x16 s", 6590 shape: wazeroir.ShapeI8x16, 6591 signed: true, 6592 v: [16]byte{ 6593 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 6594 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 6595 }, 6596 exp: i16x8( 6597 i16ToU16(-2), i16ToU16(-2), i16ToU16(-2), i16ToU16(-2), 6598 i16ToU16(-2), i16ToU16(-2), i16ToU16(-2), i16ToU16(-2), 6599 ), 6600 }, 6601 { 6602 name: "i8x16 s", 6603 shape: wazeroir.ShapeI8x16, 6604 signed: true, 6605 v: [16]byte{ 6606 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 6607 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 6608 }, 6609 exp: i16x8( 6610 i16ToU16(-256), i16ToU16(-256), i16ToU16(-256), i16ToU16(-256), 6611 i16ToU16(-256), i16ToU16(-256), i16ToU16(-256), i16ToU16(-256), 6612 ), 6613 }, 6614 { 6615 name: "i8x16 u", 6616 shape: wazeroir.ShapeI8x16, 6617 signed: false, 6618 v: [16]byte{}, 6619 exp: [16]byte{}, 6620 }, 6621 { 6622 name: "i8x16 u", 6623 shape: wazeroir.ShapeI8x16, 6624 signed: false, 6625 v: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 6626 exp: i16x8(2, 2, 2, 2, 2, 2, 2, 2), 6627 }, 6628 { 6629 name: "i8x16 u", 6630 shape: wazeroir.ShapeI8x16, 6631 signed: false, 6632 v: [16]byte{ 6633 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 6634 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), 6635 }, 6636 exp: i16x8(510, 510, 510, 510, 510, 510, 510, 510), 6637 }, 6638 { 6639 name: "i8x16 u", 6640 shape: wazeroir.ShapeI8x16, 6641 signed: false, 6642 v: [16]byte{ 6643 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 6644 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), 6645 }, 6646 exp: i16x8(256, 256, 256, 256, 256, 256, 256, 256), 6647 }, 6648 { 6649 name: "i16x8 s", 6650 shape: wazeroir.ShapeI16x8, 6651 signed: true, 6652 v: [16]byte{}, 6653 exp: [16]byte{}, 6654 }, 6655 { 6656 name: "i16x8 s", 6657 shape: wazeroir.ShapeI16x8, 6658 signed: true, 6659 v: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6660 exp: i32x4(2, 2, 2, 2), 6661 }, 6662 { 6663 name: "i16x8 s", 6664 shape: wazeroir.ShapeI16x8, 6665 signed: true, 6666 v: i16x8( 6667 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 6668 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 6669 ), 6670 exp: i32x4(i32ToU32(-2), i32ToU32(-2), i32ToU32(-2), i32ToU32(-2)), 6671 }, 6672 { 6673 name: "i16x8 s", 6674 shape: wazeroir.ShapeI16x8, 6675 signed: true, 6676 v: i16x8( 6677 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 6678 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 6679 ), 6680 exp: i32x4(i32ToU32(-65536), i32ToU32(-65536), i32ToU32(-65536), i32ToU32(-65536)), 6681 }, 6682 { 6683 name: "i16x8 u", 6684 shape: wazeroir.ShapeI16x8, 6685 signed: false, 6686 v: [16]byte{}, 6687 exp: [16]byte{}, 6688 }, 6689 { 6690 name: "i16x8 u", 6691 shape: wazeroir.ShapeI16x8, 6692 signed: false, 6693 v: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6694 exp: i32x4(2, 2, 2, 2), 6695 }, 6696 { 6697 name: "i16x8 u", 6698 shape: wazeroir.ShapeI16x8, 6699 signed: false, 6700 v: i16x8( 6701 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 6702 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 6703 ), 6704 exp: i32x4(131070, 131070, 131070, 131070), 6705 }, 6706 { 6707 name: "i16x8 u", 6708 shape: wazeroir.ShapeI16x8, 6709 signed: false, 6710 v: i16x8( 6711 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 6712 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), 6713 ), 6714 exp: i32x4(65536, 65536, 65536, 65536), 6715 }, 6716 } 6717 6718 for _, tc := range tests { 6719 tc := tc 6720 t.Run(tc.name, func(t *testing.T) { 6721 env := newCompilerEnvironment() 6722 compiler := env.requireNewCompiler(t, newCompiler, 6723 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 6724 6725 err := compiler.compilePreamble() 6726 require.NoError(t, err) 6727 6728 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6729 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 6730 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 6731 }) 6732 require.NoError(t, err) 6733 6734 err = compiler.compileV128ExtAddPairwise(&wazeroir.OperationV128ExtAddPairwise{ 6735 OriginShape: tc.shape, Signed: tc.signed, 6736 }) 6737 require.NoError(t, err) 6738 6739 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 6740 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 6741 6742 err = compiler.compileReturnFunction() 6743 require.NoError(t, err) 6744 6745 // Generate and run the code under test. 6746 code, _, err := compiler.compile() 6747 require.NoError(t, err) 6748 env.exec(code) 6749 6750 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 6751 6752 lo, hi := env.stackTopAsV128() 6753 var actual [16]byte 6754 binary.LittleEndian.PutUint64(actual[:8], lo) 6755 binary.LittleEndian.PutUint64(actual[8:], hi) 6756 require.Equal(t, tc.exp, actual) 6757 }) 6758 } 6759 } 6760 6761 func TestCompiler_compileV128Narrow(t *testing.T) { 6762 tests := []struct { 6763 name string 6764 shape wazeroir.Shape 6765 signed bool 6766 x1, x2, exp [16]byte 6767 }{ 6768 { 6769 name: "i16x8 s", 6770 shape: wazeroir.ShapeI16x8, 6771 signed: true, 6772 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6773 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6774 exp: [16]byte{}, 6775 }, 6776 { 6777 name: "i16x8 s", 6778 shape: wazeroir.ShapeI16x8, 6779 signed: true, 6780 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6781 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6782 exp: [16]byte{ 6783 0, 0, 0, 0, 0, 0, 0, 0, 6784 1, 1, 1, 1, 1, 1, 1, 1, 6785 }, 6786 }, 6787 { 6788 name: "i16x8 s", 6789 shape: wazeroir.ShapeI16x8, 6790 signed: true, 6791 x1: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6792 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6793 exp: [16]byte{ 6794 1, 1, 1, 1, 1, 1, 1, 1, 6795 0, 0, 0, 0, 0, 0, 0, 0, 6796 }, 6797 }, 6798 { 6799 name: "i16x8 s", 6800 shape: wazeroir.ShapeI16x8, 6801 signed: true, 6802 x1: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0), 6803 x2: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 6804 exp: [16]byte{ 6805 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 6806 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 6807 }, 6808 }, 6809 { 6810 name: "i16x8 s", 6811 shape: wazeroir.ShapeI16x8, 6812 signed: true, 6813 x1: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 6814 x2: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0), 6815 exp: [16]byte{ 6816 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 6817 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 6818 }, 6819 }, 6820 { 6821 name: "i16x8 u", 6822 shape: wazeroir.ShapeI16x8, 6823 signed: false, 6824 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6825 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6826 exp: [16]byte{}, 6827 }, 6828 { 6829 name: "i16x8 u", 6830 shape: wazeroir.ShapeI16x8, 6831 signed: false, 6832 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6833 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6834 exp: [16]byte{ 6835 0, 0, 0, 0, 0, 0, 0, 0, 6836 1, 1, 1, 1, 1, 1, 1, 1, 6837 }, 6838 }, 6839 { 6840 name: "i16x8 u", 6841 shape: wazeroir.ShapeI16x8, 6842 signed: false, 6843 x1: i16x8(1, 1, 1, 1, 1, 1, 1, 1), 6844 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6845 exp: [16]byte{ 6846 1, 1, 1, 1, 1, 1, 1, 1, 6847 0, 0, 0, 0, 0, 0, 0, 0, 6848 }, 6849 }, 6850 { 6851 name: "i16x8 u", 6852 shape: wazeroir.ShapeI16x8, 6853 signed: false, 6854 x1: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0), 6855 x2: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 6856 exp: [16]byte{ 6857 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6858 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6859 }, 6860 }, 6861 { 6862 name: "i16x8 u", 6863 shape: wazeroir.ShapeI16x8, 6864 signed: false, 6865 x1: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff), 6866 x2: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0), 6867 exp: [16]byte{ 6868 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6869 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6870 }, 6871 }, 6872 { 6873 name: "i16x8 u", 6874 shape: wazeroir.ShapeI16x8, 6875 signed: false, 6876 x1: i16x8(i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0), 6877 x2: i16x8(0, 0x100, 0, 0x100, 0, 0x100, 0, 0x100), 6878 exp: [16]byte{ 6879 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6880 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 6881 }, 6882 }, 6883 { 6884 name: "i16x8 u", 6885 shape: wazeroir.ShapeI16x8, 6886 signed: false, 6887 x1: i16x8(0, 0x100, 0, 0x100, 0, 0x100, 0, 0x100), 6888 x2: i16x8(i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0), 6889 exp: [16]byte{ 6890 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 6891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6892 }, 6893 }, 6894 { 6895 name: "i32x4 s", 6896 shape: wazeroir.ShapeI32x4, 6897 signed: true, 6898 x1: i32x4(0, 0, 0, 0), 6899 x2: i32x4(0, 0, 0, 0), 6900 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6901 }, 6902 { 6903 name: "i32x4 s", 6904 shape: wazeroir.ShapeI32x4, 6905 signed: true, 6906 x1: i32x4(0, 0, 0, 0), 6907 x2: i32x4(1, 1, 1, 1), 6908 exp: i16x8(0, 0, 0, 0, 1, 1, 1, 1), 6909 }, 6910 { 6911 name: "i32x4 s", 6912 shape: wazeroir.ShapeI32x4, 6913 signed: true, 6914 x1: i32x4(1, 1, 1, 1), 6915 x2: i32x4(0, 0, 0, 0), 6916 exp: i16x8(1, 1, 1, 1, 0, 0, 0, 0), 6917 }, 6918 { 6919 name: "i32x4 s", 6920 shape: wazeroir.ShapeI32x4, 6921 signed: true, 6922 x1: i32x4(0x8000, 0x8000, 0x7fff, 0x7fff), 6923 x2: i32x4(0x7fff, 0x7fff, 0x8000, 0x8000), 6924 exp: i16x8(0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff), 6925 }, 6926 { 6927 name: "i32x4 u", 6928 shape: wazeroir.ShapeI32x4, 6929 signed: false, 6930 x1: i32x4(0, 0, 0, 0), 6931 x2: i32x4(0, 0, 0, 0), 6932 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 6933 }, 6934 { 6935 name: "i32x4 u", 6936 shape: wazeroir.ShapeI32x4, 6937 signed: false, 6938 x1: i32x4(0, 0, 0, 0), 6939 x2: i32x4(1, 1, 1, 1), 6940 exp: i16x8(0, 0, 0, 0, 1, 1, 1, 1), 6941 }, 6942 { 6943 name: "i32x4 u", 6944 shape: wazeroir.ShapeI32x4, 6945 signed: false, 6946 x1: i32x4(1, 1, 1, 1), 6947 x2: i32x4(0, 0, 0, 0), 6948 exp: i16x8(1, 1, 1, 1, 0, 0, 0, 0), 6949 }, 6950 { 6951 name: "i32x4 u", 6952 shape: wazeroir.ShapeI32x4, 6953 signed: false, 6954 x1: i32x4(0x8000, 0x8000, 0x7fff, 0x7fff), 6955 x2: i32x4(0x7fff, 0x7fff, 0x8000, 0x8000), 6956 exp: i16x8(0x8000, 0x8000, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x8000, 0x8000), 6957 }, 6958 } 6959 6960 for _, tc := range tests { 6961 tc := tc 6962 t.Run(tc.name, func(t *testing.T) { 6963 env := newCompilerEnvironment() 6964 compiler := env.requireNewCompiler(t, newCompiler, 6965 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 6966 6967 err := compiler.compilePreamble() 6968 require.NoError(t, err) 6969 6970 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6971 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 6972 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 6973 }) 6974 require.NoError(t, err) 6975 6976 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 6977 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 6978 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 6979 }) 6980 require.NoError(t, err) 6981 6982 err = compiler.compileV128Narrow(&wazeroir.OperationV128Narrow{ 6983 OriginShape: tc.shape, Signed: tc.signed, 6984 }) 6985 require.NoError(t, err) 6986 6987 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 6988 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 6989 6990 err = compiler.compileReturnFunction() 6991 require.NoError(t, err) 6992 6993 // Generate and run the code under test. 6994 code, _, err := compiler.compile() 6995 require.NoError(t, err) 6996 env.exec(code) 6997 6998 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 6999 7000 lo, hi := env.stackTopAsV128() 7001 var actual [16]byte 7002 binary.LittleEndian.PutUint64(actual[:8], lo) 7003 binary.LittleEndian.PutUint64(actual[8:], hi) 7004 require.Equal(t, tc.exp, actual) 7005 }) 7006 } 7007 } 7008 7009 func TestCompiler_compileV128FConvertFromI(t *testing.T) { 7010 tests := []struct { 7011 name string 7012 destShape wazeroir.Shape 7013 signed bool 7014 v, exp [16]byte 7015 }{ 7016 { 7017 name: "f32x4 s", 7018 destShape: wazeroir.ShapeF32x4, 7019 signed: true, 7020 v: i32x4(0, 0, 0, 0), 7021 exp: f32x4(0, 0, 0, 0), 7022 }, 7023 { 7024 name: "f32x4 s", 7025 destShape: wazeroir.ShapeF32x4, 7026 signed: true, 7027 v: i32x4(1, 0, 2, 3), 7028 exp: f32x4(1, 0, 2.0, 3), 7029 }, 7030 { 7031 name: "f32x4 s", 7032 destShape: wazeroir.ShapeF32x4, 7033 signed: true, 7034 v: i32x4(1234567890, i32ToU32(-2147483648.0), 2147483647, 1234567890), 7035 exp: f32x4(0x1.26580cp+30, -2147483648.0, 2147483647, 0x1.26580cp+30), 7036 }, 7037 { 7038 name: "f32x4 s", 7039 destShape: wazeroir.ShapeF32x4, 7040 signed: false, 7041 v: i32x4(0, 0, 0, 0), 7042 exp: f32x4(0, 0, 0, 0), 7043 }, 7044 { 7045 name: "f32x4 s", 7046 destShape: wazeroir.ShapeF32x4, 7047 signed: false, 7048 v: i32x4(1, 0, 2, 3), 7049 exp: f32x4(1, 0, 2.0, 3), 7050 }, 7051 { 7052 name: "f32x4 s", 7053 destShape: wazeroir.ShapeF32x4, 7054 signed: false, 7055 v: i32x4(2147483647, i32ToU32(-2147483648.0), 2147483647, i32ToU32(-1)), 7056 exp: f32x4(2147483648.0, 2147483648.0, 2147483648.0, 4294967295.0), 7057 }, 7058 { 7059 name: "f64x2 s", 7060 destShape: wazeroir.ShapeF64x2, 7061 signed: true, 7062 v: i32x4(0, 0, 0, 0), 7063 exp: f64x2(0, 0), 7064 }, 7065 { 7066 name: "f64x2 s", 7067 destShape: wazeroir.ShapeF64x2, 7068 signed: true, 7069 v: i32x4(0, 0, i32ToU32(-1), i32ToU32(-32)), 7070 exp: f64x2(0, 0), 7071 }, 7072 { 7073 name: "f64x2 s", 7074 destShape: wazeroir.ShapeF64x2, 7075 signed: true, 7076 v: i32x4(2147483647, i32ToU32(-2147483648), 0, 0), 7077 exp: f64x2(2147483647, -2147483648), 7078 }, 7079 { 7080 name: "f64x2 s", 7081 destShape: wazeroir.ShapeF64x2, 7082 signed: false, 7083 v: i32x4(0, 0, 0, 0), 7084 exp: f64x2(0, 0), 7085 }, 7086 { 7087 name: "f64x2 s", 7088 destShape: wazeroir.ShapeF64x2, 7089 signed: false, 7090 v: i32x4(0, 0, i32ToU32(-1), i32ToU32(-32)), 7091 exp: f64x2(0, 0), 7092 }, 7093 { 7094 name: "f64x2 s", 7095 destShape: wazeroir.ShapeF64x2, 7096 signed: false, 7097 v: i32x4(2147483647, i32ToU32(-2147483648), 0, 0), 7098 exp: f64x2(2147483647, 2147483648), 7099 }, 7100 } 7101 7102 for _, tc := range tests { 7103 tc := tc 7104 t.Run(tc.name, func(t *testing.T) { 7105 env := newCompilerEnvironment() 7106 compiler := env.requireNewCompiler(t, newCompiler, 7107 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 7108 7109 err := compiler.compilePreamble() 7110 require.NoError(t, err) 7111 7112 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 7113 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 7114 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 7115 }) 7116 require.NoError(t, err) 7117 7118 err = compiler.compileV128FConvertFromI(&wazeroir.OperationV128FConvertFromI{ 7119 DestinationShape: tc.destShape, 7120 Signed: tc.signed, 7121 }) 7122 require.NoError(t, err) 7123 7124 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 7125 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 7126 7127 err = compiler.compileReturnFunction() 7128 require.NoError(t, err) 7129 7130 // Generate and run the code under test. 7131 code, _, err := compiler.compile() 7132 require.NoError(t, err) 7133 env.exec(code) 7134 7135 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 7136 7137 lo, hi := env.stackTopAsV128() 7138 var actual [16]byte 7139 binary.LittleEndian.PutUint64(actual[:8], lo) 7140 binary.LittleEndian.PutUint64(actual[8:], hi) 7141 require.Equal(t, tc.exp, actual) 7142 }) 7143 } 7144 } 7145 7146 func TestCompiler_compileV128Dot(t *testing.T) { 7147 tests := []struct { 7148 name string 7149 x1, x2, exp [16]byte 7150 }{ 7151 { 7152 name: "1", 7153 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 7154 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0), 7155 exp: i32x4(0, 0, 0, 0), 7156 }, 7157 { 7158 name: "2", 7159 x1: i16x8(1, 1, 1, 1, i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)), 7160 x2: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 2, 2, 2, 2), 7161 exp: i32x4(i32ToU32(-2), i32ToU32(-2), i32ToU32(-4), i32ToU32(-4)), 7162 }, 7163 { 7164 name: "3", 7165 x1: i16x8(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535), 7166 x2: i16x8(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535), 7167 exp: i32x4(2, 2, 2, 2), 7168 }, 7169 } 7170 7171 for _, tc := range tests { 7172 tc := tc 7173 t.Run(tc.name, func(t *testing.T) { 7174 env := newCompilerEnvironment() 7175 compiler := env.requireNewCompiler(t, newCompiler, 7176 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 7177 7178 err := compiler.compilePreamble() 7179 require.NoError(t, err) 7180 7181 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 7182 Lo: binary.LittleEndian.Uint64(tc.x2[:8]), 7183 Hi: binary.LittleEndian.Uint64(tc.x2[8:]), 7184 }) 7185 require.NoError(t, err) 7186 7187 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 7188 Lo: binary.LittleEndian.Uint64(tc.x1[:8]), 7189 Hi: binary.LittleEndian.Uint64(tc.x1[8:]), 7190 }) 7191 require.NoError(t, err) 7192 7193 err = compiler.compileV128Dot(&wazeroir.OperationV128Dot{}) 7194 require.NoError(t, err) 7195 7196 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 7197 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 7198 7199 err = compiler.compileReturnFunction() 7200 require.NoError(t, err) 7201 7202 // Generate and run the code under test. 7203 code, _, err := compiler.compile() 7204 require.NoError(t, err) 7205 env.exec(code) 7206 7207 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 7208 7209 lo, hi := env.stackTopAsV128() 7210 var actual [16]byte 7211 binary.LittleEndian.PutUint64(actual[:8], lo) 7212 binary.LittleEndian.PutUint64(actual[8:], hi) 7213 require.Equal(t, tc.exp, actual) 7214 }) 7215 } 7216 } 7217 7218 func TestCompiler_compileV128ITruncSatFromF(t *testing.T) { 7219 tests := []struct { 7220 name string 7221 originShape wazeroir.Shape 7222 signed bool 7223 v, exp [16]byte 7224 }{ 7225 { 7226 name: "f32x4 s", 7227 originShape: wazeroir.ShapeF32x4, 7228 signed: true, 7229 v: i32x4(0, 0, 0, 0), 7230 exp: f32x4(0, 0, 0, 0), 7231 }, 7232 { 7233 name: "f32x4 s", 7234 originShape: wazeroir.ShapeF32x4, 7235 signed: true, 7236 v: f32x4(1.5, -1.9, -1.9, 1.5), 7237 exp: i32x4(1, i32ToU32(-1), i32ToU32(-1), 1), 7238 }, 7239 { 7240 name: "f32x4 s", 7241 originShape: wazeroir.ShapeF32x4, 7242 signed: true, 7243 v: f32x4(float32(math.NaN()), -4294967294.0, float32(math.Inf(-1)), float32(math.Inf(1))), 7244 exp: i32x4(0, i32ToU32(-2147483648), i32ToU32(-2147483648), 2147483647), 7245 }, 7246 { 7247 name: "f32x4 u", 7248 originShape: wazeroir.ShapeF32x4, 7249 signed: false, 7250 v: i32x4(0, 0, 0, 0), 7251 exp: f32x4(0, 0, 0, 0), 7252 }, 7253 { 7254 name: "f32x4 u", 7255 originShape: wazeroir.ShapeF32x4, 7256 signed: false, 7257 v: f32x4(1.5, -1.9, -1.9, 1.5), 7258 exp: i32x4(1, 0, 0, 1), 7259 }, 7260 { 7261 name: "f32x4 u", 7262 originShape: wazeroir.ShapeF32x4, 7263 signed: false, 7264 v: f32x4(float32(math.NaN()), -4294967294.0, 4294967294.0, float32(math.Inf(1))), 7265 exp: i32x4(0, 0, 4294967295, 4294967295), 7266 }, 7267 { 7268 name: "f64x2 s", 7269 originShape: wazeroir.ShapeF64x2, 7270 signed: true, 7271 v: f64x2(0, 0), 7272 exp: i32x4(0, 0, 0, 0), 7273 }, 7274 { 7275 name: "f64x2 s", 7276 originShape: wazeroir.ShapeF64x2, 7277 signed: true, 7278 v: f64x2(5.123, -2.0), 7279 exp: i32x4(5, i32ToU32(-2), 0, 0), 7280 }, 7281 { 7282 name: "f64x2 s", 7283 originShape: wazeroir.ShapeF64x2, 7284 signed: true, 7285 v: f64x2(math.NaN(), math.Inf(1)), 7286 exp: i32x4(0, 2147483647, 0, 0), 7287 }, 7288 { 7289 name: "f64x2 s", 7290 originShape: wazeroir.ShapeF64x2, 7291 signed: true, 7292 v: f64x2(math.Inf(-1), 4294967294.0), 7293 exp: i32x4(i32ToU32(-2147483648), 2147483647, 0, 0), 7294 }, 7295 { 7296 name: "f64x2 u", 7297 originShape: wazeroir.ShapeF64x2, 7298 signed: false, 7299 v: f64x2(0, 0), 7300 exp: i32x4(0, 0, 0, 0), 7301 }, 7302 { 7303 name: "f64x2 u", 7304 originShape: wazeroir.ShapeF64x2, 7305 signed: false, 7306 v: f64x2(5.123, -2.0), 7307 exp: i32x4(5, 0, 0, 0), 7308 }, 7309 { 7310 name: "f64x2 u", 7311 originShape: wazeroir.ShapeF64x2, 7312 signed: false, 7313 v: f64x2(math.NaN(), math.Inf(1)), 7314 exp: i32x4(0, 4294967295, 0, 0), 7315 }, 7316 { 7317 name: "f64x2 u", 7318 originShape: wazeroir.ShapeF64x2, 7319 signed: false, 7320 v: f64x2(math.Inf(-1), 4294967296.0), 7321 exp: i32x4(0, 4294967295, 0, 0), 7322 }, 7323 } 7324 7325 for _, tc := range tests { 7326 tc := tc 7327 t.Run(tc.name, func(t *testing.T) { 7328 env := newCompilerEnvironment() 7329 compiler := env.requireNewCompiler(t, newCompiler, 7330 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 7331 7332 err := compiler.compilePreamble() 7333 require.NoError(t, err) 7334 7335 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 7336 Lo: binary.LittleEndian.Uint64(tc.v[:8]), 7337 Hi: binary.LittleEndian.Uint64(tc.v[8:]), 7338 }) 7339 require.NoError(t, err) 7340 7341 err = compiler.compileV128ITruncSatFromF(&wazeroir.OperationV128ITruncSatFromF{ 7342 OriginShape: tc.originShape, 7343 Signed: tc.signed, 7344 }) 7345 require.NoError(t, err) 7346 7347 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 7348 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 7349 7350 err = compiler.compileReturnFunction() 7351 require.NoError(t, err) 7352 7353 // Generate and run the code under test. 7354 code, _, err := compiler.compile() 7355 require.NoError(t, err) 7356 env.exec(code) 7357 7358 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 7359 7360 lo, hi := env.stackTopAsV128() 7361 var actual [16]byte 7362 binary.LittleEndian.PutUint64(actual[:8], lo) 7363 binary.LittleEndian.PutUint64(actual[8:], hi) 7364 require.Equal(t, tc.exp, actual) 7365 }) 7366 } 7367 } 7368 7369 // TestCompiler_compileSelect_v128 is for select instructions on vector values. 7370 func TestCompiler_compileSelect_v128(t *testing.T) { 7371 const x1Lo, x1Hi = uint64(0x1), uint64(0x2) 7372 const x2Lo, x2Hi = uint64(0x3), uint64(0x4) 7373 7374 for _, selector := range []uint32{0, 1} { 7375 env := newCompilerEnvironment() 7376 compiler := env.requireNewCompiler(t, newCompiler, 7377 &wazeroir.CompilationResult{HasMemory: true, Signature: &wasm.FunctionType{}}) 7378 7379 err := compiler.compilePreamble() 7380 require.NoError(t, err) 7381 7382 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 7383 Lo: x1Lo, 7384 Hi: x1Hi, 7385 }) 7386 require.NoError(t, err) 7387 7388 err = compiler.compileV128Const(&wazeroir.OperationV128Const{ 7389 Lo: x2Lo, 7390 Hi: x2Hi, 7391 }) 7392 require.NoError(t, err) 7393 7394 err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: selector}) 7395 require.NoError(t, err) 7396 7397 err = compiler.compileSelect(&wazeroir.OperationSelect{IsTargetVector: true}) 7398 require.NoError(t, err) 7399 7400 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 7401 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters)) 7402 7403 err = compiler.compileReturnFunction() 7404 require.NoError(t, err) 7405 7406 // Generate and run the code under test. 7407 code, _, err := compiler.compile() 7408 require.NoError(t, err) 7409 env.exec(code) 7410 7411 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode) 7412 7413 lo, hi := env.stackTopAsV128() 7414 if selector == 0 { 7415 require.Equal(t, x2Lo, lo) 7416 require.Equal(t, x2Hi, hi) 7417 } else { 7418 require.Equal(t, x1Lo, lo) 7419 require.Equal(t, x1Hi, hi) 7420 } 7421 } 7422 }