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