github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/compiler/compiler_numeric_test.go (about) 1 package compiler 2 3 import ( 4 "fmt" 5 "math" 6 "math/bits" 7 "testing" 8 9 "github.com/bananabytelabs/wazero/internal/asm" 10 "github.com/bananabytelabs/wazero/internal/moremath" 11 "github.com/bananabytelabs/wazero/internal/testing/require" 12 "github.com/bananabytelabs/wazero/internal/wasm" 13 "github.com/bananabytelabs/wazero/internal/wazeroir" 14 ) 15 16 func TestCompiler_compileConsts(t *testing.T) { 17 for _, op := range []wazeroir.OperationKind{ 18 wazeroir.OperationKindConstI32, 19 wazeroir.OperationKindConstI64, 20 wazeroir.OperationKindConstF32, 21 wazeroir.OperationKindConstF64, 22 wazeroir.OperationKindV128Const, 23 } { 24 op := op 25 t.Run(op.String(), func(t *testing.T) { 26 for _, val := range []uint64{ 27 0x0, 0x1, 0x1111000, 1 << 16, 1 << 21, 1 << 27, 1 << 32, 1<<32 + 1, 1 << 53, 28 math.Float64bits(math.Inf(1)), 29 math.Float64bits(math.Inf(-1)), 30 math.Float64bits(math.NaN()), 31 math.MaxUint32, 32 math.MaxInt32, 33 math.MaxUint64, 34 math.MaxInt64, 35 uint64(math.Float32bits(float32(math.Inf(1)))), 36 uint64(math.Float32bits(float32(math.Inf(-1)))), 37 uint64(math.Float32bits(float32(math.NaN()))), 38 } { 39 t.Run(fmt.Sprintf("0x%x", val), func(t *testing.T) { 40 env := newCompilerEnvironment() 41 42 // Compile code. 43 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 44 err := compiler.compilePreamble() 45 require.NoError(t, err) 46 47 switch op { 48 case wazeroir.OperationKindConstI32: 49 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(val)))) 50 case wazeroir.OperationKindConstI64: 51 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(val))) 52 case wazeroir.OperationKindConstF32: 53 err = compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(uint32(val))))) 54 case wazeroir.OperationKindConstF64: 55 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(val)))) 56 case wazeroir.OperationKindV128Const: 57 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(val, ^val))) 58 } 59 require.NoError(t, err) 60 61 // After compiling const operations, we must see the register allocated value on the top of value. 62 loc := compiler.runtimeValueLocationStack().peek() 63 require.True(t, loc.onRegister()) 64 65 if op == wazeroir.OperationKindV128Const { 66 require.Equal(t, runtimeValueTypeV128Hi, loc.valueType) 67 } 68 69 err = compiler.compileReturnFunction() 70 require.NoError(t, err) 71 72 code := asm.CodeSegment{} 73 defer func() { require.NoError(t, code.Unmap()) }() 74 75 // Generate the code under test. 76 _, err = compiler.compile(code.NextCodeSection()) 77 require.NoError(t, err) 78 79 // Run native code. 80 env.exec(code.Bytes()) 81 82 // Compiler status must be returned. 83 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus()) 84 if op == wazeroir.OperationKindV128Const { 85 require.Equal(t, uint64(2), env.stackPointer()) // a vector value consists of two uint64. 86 } else { 87 require.Equal(t, uint64(1), env.stackPointer()) 88 } 89 90 switch op { 91 case wazeroir.OperationKindConstI32, wazeroir.OperationKindConstF32: 92 require.Equal(t, uint32(val), env.stackTopAsUint32()) 93 case wazeroir.OperationKindConstI64, wazeroir.OperationKindConstF64: 94 require.Equal(t, val, env.stackTopAsUint64()) 95 case wazeroir.OperationKindV128Const: 96 lo, hi := env.stackTopAsV128() 97 require.Equal(t, val, lo) 98 require.Equal(t, ^val, hi) 99 } 100 }) 101 } 102 }) 103 } 104 } 105 106 func TestCompiler_compile_Add_Sub_Mul(t *testing.T) { 107 for _, kind := range []wazeroir.OperationKind{ 108 wazeroir.OperationKindAdd, 109 wazeroir.OperationKindSub, 110 wazeroir.OperationKindMul, 111 } { 112 kind := kind 113 t.Run(kind.String(), func(t *testing.T) { 114 for _, unsignedType := range []wazeroir.UnsignedType{ 115 wazeroir.UnsignedTypeI32, 116 wazeroir.UnsignedTypeI64, 117 wazeroir.UnsignedTypeF32, 118 wazeroir.UnsignedTypeF64, 119 } { 120 unsignedType := unsignedType 121 t.Run(unsignedType.String(), func(t *testing.T) { 122 for _, values := range [][2]uint64{ 123 {0, 0}, 124 {1, 1}, 125 {2, 1}, 126 {100, 1}, 127 {1, 0}, 128 {0, 1}, 129 {math.MaxInt16, math.MaxInt32}, 130 {1 << 14, 1 << 21}, 131 {1 << 14, 1 << 21}, 132 {0xffff_ffff_ffff_ffff, 0}, 133 {0xffff_ffff_ffff_ffff, 1}, 134 {0, 0xffff_ffff_ffff_ffff}, 135 {1, 0xffff_ffff_ffff_ffff}, 136 {0, math.Float64bits(math.Inf(1))}, 137 {0, math.Float64bits(math.Inf(-1))}, 138 {math.Float64bits(math.Inf(1)), 1}, 139 {math.Float64bits(math.Inf(-1)), 1}, 140 {math.Float64bits(1.11231), math.Float64bits(math.Inf(1))}, 141 {math.Float64bits(1.11231), math.Float64bits(math.Inf(-1))}, 142 {math.Float64bits(math.Inf(1)), math.Float64bits(1.11231)}, 143 {math.Float64bits(math.Inf(-1)), math.Float64bits(1.11231)}, 144 {math.Float64bits(math.Inf(1)), math.Float64bits(math.NaN())}, 145 {math.Float64bits(math.Inf(-1)), math.Float64bits(math.NaN())}, 146 {math.Float64bits(math.NaN()), math.Float64bits(math.Inf(1))}, 147 {math.Float64bits(math.NaN()), math.Float64bits(math.Inf(-1))}, 148 } { 149 x1, x2 := values[0], values[1] 150 t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", x1, x2), func(t *testing.T) { 151 env := newCompilerEnvironment() 152 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 153 err := compiler.compilePreamble() 154 require.NoError(t, err) 155 156 // Emit consts operands. 157 for _, v := range []uint64{x1, x2} { 158 switch unsignedType { 159 case wazeroir.UnsignedTypeI32: 160 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(v)))) 161 case wazeroir.UnsignedTypeI64: 162 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(v))) 163 case wazeroir.UnsignedTypeF32: 164 err = compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(uint32(v))))) 165 case wazeroir.UnsignedTypeF64: 166 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(v)))) 167 } 168 require.NoError(t, err) 169 } 170 171 // At this point, two values exist. 172 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 173 174 // Emit the operation. 175 switch kind { 176 case wazeroir.OperationKindAdd: 177 err = compiler.compileAdd(operationPtr(wazeroir.NewOperationAdd(unsignedType))) 178 case wazeroir.OperationKindSub: 179 err = compiler.compileSub(operationPtr(wazeroir.NewOperationSub(unsignedType))) 180 case wazeroir.OperationKindMul: 181 err = compiler.compileMul(operationPtr(wazeroir.NewOperationMul(unsignedType))) 182 } 183 require.NoError(t, err) 184 185 // We consumed two values, but push the result back. 186 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 187 resultLocation := compiler.runtimeValueLocationStack().peek() 188 // Plus the result must be located on a register. 189 require.True(t, resultLocation.onRegister()) 190 // Also, the result must have an appropriate register type. 191 if unsignedType == wazeroir.UnsignedTypeF32 || unsignedType == wazeroir.UnsignedTypeF64 { 192 require.Equal(t, registerTypeVector, resultLocation.getRegisterType()) 193 } else { 194 require.Equal(t, registerTypeGeneralPurpose, resultLocation.getRegisterType()) 195 } 196 197 err = compiler.compileReturnFunction() 198 require.NoError(t, err) 199 200 code := asm.CodeSegment{} 201 defer func() { require.NoError(t, code.Unmap()) }() 202 203 // Compile and execute the code under test. 204 _, err = compiler.compile(code.NextCodeSection()) 205 require.NoError(t, err) 206 env.exec(code.Bytes()) 207 208 // Check the stack. 209 require.Equal(t, uint64(1), env.stackPointer()) 210 211 switch kind { 212 case wazeroir.OperationKindAdd: 213 switch unsignedType { 214 case wazeroir.UnsignedTypeI32: 215 require.Equal(t, uint32(x1)+uint32(x2), env.stackTopAsUint32()) 216 case wazeroir.UnsignedTypeI64: 217 require.Equal(t, x1+x2, env.stackTopAsUint64()) 218 case wazeroir.UnsignedTypeF32: 219 exp := math.Float32frombits(uint32(x1)) + math.Float32frombits(uint32(x2)) 220 // NaN cannot be compared with themselves, so we have to use IsNaN 221 if math.IsNaN(float64(exp)) { 222 require.True(t, math.IsNaN(float64(env.stackTopAsFloat32()))) 223 } else { 224 require.Equal(t, exp, env.stackTopAsFloat32()) 225 } 226 case wazeroir.UnsignedTypeF64: 227 exp := math.Float64frombits(x1) + math.Float64frombits(x2) 228 // NaN cannot be compared with themselves, so we have to use IsNaN 229 if math.IsNaN(exp) { 230 require.True(t, math.IsNaN(env.stackTopAsFloat64())) 231 } else { 232 require.Equal(t, exp, env.stackTopAsFloat64()) 233 } 234 } 235 case wazeroir.OperationKindSub: 236 switch unsignedType { 237 case wazeroir.UnsignedTypeI32: 238 require.Equal(t, uint32(x1)-uint32(x2), env.stackTopAsUint32()) 239 case wazeroir.UnsignedTypeI64: 240 require.Equal(t, x1-x2, env.stackTopAsUint64()) 241 case wazeroir.UnsignedTypeF32: 242 exp := math.Float32frombits(uint32(x1)) - math.Float32frombits(uint32(x2)) 243 // NaN cannot be compared with themselves, so we have to use IsNaN 244 if math.IsNaN(float64(exp)) { 245 require.True(t, math.IsNaN(float64(env.stackTopAsFloat32()))) 246 } else { 247 require.Equal(t, exp, env.stackTopAsFloat32()) 248 } 249 case wazeroir.UnsignedTypeF64: 250 exp := math.Float64frombits(x1) - math.Float64frombits(x2) 251 // NaN cannot be compared with themselves, so we have to use IsNaN 252 if math.IsNaN(exp) { 253 require.True(t, math.IsNaN(env.stackTopAsFloat64())) 254 } else { 255 require.Equal(t, exp, env.stackTopAsFloat64()) 256 } 257 } 258 case wazeroir.OperationKindMul: 259 switch unsignedType { 260 case wazeroir.UnsignedTypeI32: 261 require.Equal(t, uint32(x1)*uint32(x2), env.stackTopAsUint32()) 262 case wazeroir.UnsignedTypeI64: 263 require.Equal(t, x1*x2, env.stackTopAsUint64()) 264 case wazeroir.UnsignedTypeF32: 265 exp := math.Float32frombits(uint32(x1)) * math.Float32frombits(uint32(x2)) 266 // NaN cannot be compared with themselves, so we have to use IsNaN 267 if math.IsNaN(float64(exp)) { 268 require.True(t, math.IsNaN(float64(env.stackTopAsFloat32()))) 269 } else { 270 require.Equal(t, exp, env.stackTopAsFloat32()) 271 } 272 case wazeroir.UnsignedTypeF64: 273 exp := math.Float64frombits(x1) * math.Float64frombits(x2) 274 // NaN cannot be compared with themselves, so we have to use IsNaN 275 if math.IsNaN(exp) { 276 require.True(t, math.IsNaN(env.stackTopAsFloat64())) 277 } else { 278 require.Equal(t, exp, env.stackTopAsFloat64()) 279 } 280 } 281 } 282 }) 283 } 284 }) 285 } 286 }) 287 } 288 } 289 290 func TestCompiler_compile_And_Or_Xor_Shl_Rotl_Rotr(t *testing.T) { 291 for _, kind := range []wazeroir.OperationKind{ 292 wazeroir.OperationKindAnd, 293 wazeroir.OperationKindOr, 294 wazeroir.OperationKindXor, 295 wazeroir.OperationKindShl, 296 wazeroir.OperationKindRotl, 297 wazeroir.OperationKindRotr, 298 } { 299 kind := kind 300 t.Run(kind.String(), func(t *testing.T) { 301 for _, unsignedInt := range []wazeroir.UnsignedInt{ 302 wazeroir.UnsignedInt32, 303 wazeroir.UnsignedInt64, 304 } { 305 unsignedInt := unsignedInt 306 t.Run(unsignedInt.String(), func(t *testing.T) { 307 for _, values := range [][2]uint64{ 308 {0, 0}, 309 {0, 1}, 310 {1, 0}, 311 {1, 1}, 312 {1 << 31, 1}, 313 {1, 1 << 31}, 314 {1 << 31, 1 << 31}, 315 {1 << 63, 1}, 316 {1, 1 << 63}, 317 {1 << 63, 1 << 63}, 318 } { 319 x1, x2 := values[0], values[1] 320 for _, x1OnRegister := range []bool{ 321 true, false, 322 } { 323 x1OnRegister := x1OnRegister 324 t.Run(fmt.Sprintf("x1=0x%x(on_register=%v),x2=0x%x", x1, x1OnRegister, x2), func(t *testing.T) { 325 env := newCompilerEnvironment() 326 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 327 err := compiler.compilePreamble() 328 require.NoError(t, err) 329 330 // Emit consts operands. 331 var x1Location *runtimeValueLocation 332 switch unsignedInt { 333 case wazeroir.UnsignedInt32: 334 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(x1)))) 335 require.NoError(t, err) 336 x1Location = compiler.runtimeValueLocationStack().peek() 337 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(x2))) 338 require.NoError(t, err) 339 case wazeroir.UnsignedInt64: 340 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(x1))) 341 require.NoError(t, err) 342 x1Location = compiler.runtimeValueLocationStack().peek() 343 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(x2))) 344 require.NoError(t, err) 345 } 346 347 if !x1OnRegister { 348 compiler.compileReleaseRegisterToStack(x1Location) 349 } 350 351 // At this point, two values exist. 352 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 353 354 // Emit the operation. 355 switch kind { 356 case wazeroir.OperationKindAnd: 357 err = compiler.compileAnd(operationPtr(wazeroir.NewOperationAnd(unsignedInt))) 358 case wazeroir.OperationKindOr: 359 err = compiler.compileOr(operationPtr(wazeroir.NewOperationOr(unsignedInt))) 360 case wazeroir.OperationKindXor: 361 err = compiler.compileXor(operationPtr(wazeroir.NewOperationXor(unsignedInt))) 362 case wazeroir.OperationKindShl: 363 err = compiler.compileShl(operationPtr(wazeroir.NewOperationShl(unsignedInt))) 364 case wazeroir.OperationKindRotl: 365 err = compiler.compileRotl(operationPtr(wazeroir.NewOperationRotl(unsignedInt))) 366 case wazeroir.OperationKindRotr: 367 err = compiler.compileRotr(operationPtr(wazeroir.NewOperationRotr(unsignedInt))) 368 } 369 require.NoError(t, err) 370 371 // We consumed two values, but push the result back. 372 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 373 resultLocation := compiler.runtimeValueLocationStack().peek() 374 // Also, the result must have an appropriate register type. 375 require.Equal(t, registerTypeGeneralPurpose, resultLocation.getRegisterType()) 376 377 err = compiler.compileReturnFunction() 378 require.NoError(t, err) 379 380 code := asm.CodeSegment{} 381 defer func() { require.NoError(t, code.Unmap()) }() 382 383 // Compile and execute the code under test. 384 _, err = compiler.compile(code.NextCodeSection()) 385 require.NoError(t, err) 386 env.exec(code.Bytes()) 387 388 // Check the stack. 389 require.Equal(t, uint64(1), env.stackPointer()) 390 391 switch kind { 392 case wazeroir.OperationKindAnd: 393 switch unsignedInt { 394 case wazeroir.UnsignedInt32: 395 require.Equal(t, uint32(x1)&uint32(x2), env.stackTopAsUint32()) 396 case wazeroir.UnsignedInt64: 397 require.Equal(t, x1&x2, env.stackTopAsUint64()) 398 } 399 case wazeroir.OperationKindOr: 400 switch unsignedInt { 401 case wazeroir.UnsignedInt32: 402 require.Equal(t, uint32(x1)|uint32(x2), env.stackTopAsUint32()) 403 case wazeroir.UnsignedInt64: 404 require.Equal(t, x1|x2, env.stackTopAsUint64()) 405 } 406 case wazeroir.OperationKindXor: 407 switch unsignedInt { 408 case wazeroir.UnsignedInt32: 409 require.Equal(t, uint32(x1)^uint32(x2), env.stackTopAsUint32()) 410 case wazeroir.UnsignedInt64: 411 require.Equal(t, x1^x2, env.stackTopAsUint64()) 412 } 413 case wazeroir.OperationKindShl: 414 switch unsignedInt { 415 case wazeroir.UnsignedInt32: 416 require.Equal(t, uint32(x1)<<uint32(x2%32), env.stackTopAsUint32()) 417 case wazeroir.UnsignedInt64: 418 require.Equal(t, x1<<(x2%64), env.stackTopAsUint64()) 419 } 420 case wazeroir.OperationKindRotl: 421 switch unsignedInt { 422 case wazeroir.UnsignedInt32: 423 require.Equal(t, bits.RotateLeft32(uint32(x1), int(x2)), env.stackTopAsUint32()) 424 case wazeroir.UnsignedInt64: 425 require.Equal(t, bits.RotateLeft64(x1, int(x2)), env.stackTopAsUint64()) 426 } 427 case wazeroir.OperationKindRotr: 428 switch unsignedInt { 429 case wazeroir.UnsignedInt32: 430 require.Equal(t, bits.RotateLeft32(uint32(x1), -int(x2)), env.stackTopAsUint32()) 431 case wazeroir.UnsignedInt64: 432 require.Equal(t, bits.RotateLeft64(x1, -int(x2)), env.stackTopAsUint64()) 433 } 434 } 435 }) 436 } 437 } 438 }) 439 } 440 }) 441 } 442 } 443 444 func TestCompiler_compileShr(t *testing.T) { 445 kind := wazeroir.OperationKindShr 446 t.Run(kind.String(), func(t *testing.T) { 447 for _, signedInt := range []wazeroir.SignedInt{ 448 wazeroir.SignedInt32, 449 wazeroir.SignedInt64, 450 wazeroir.SignedUint32, 451 wazeroir.SignedUint64, 452 } { 453 signedInt := signedInt 454 t.Run(signedInt.String(), func(t *testing.T) { 455 for _, values := range [][2]uint64{ 456 {0, 0}, 457 {0, 1}, 458 {1, 0}, 459 {1, 1}, 460 {1 << 31, 1}, 461 {1, 1 << 31}, 462 {1 << 31, 1 << 31}, 463 {1 << 63, 1}, 464 {1, 1 << 63}, 465 {1 << 63, 1 << 63}, 466 } { 467 x1, x2 := values[0], values[1] 468 t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", x1, x2), func(t *testing.T) { 469 env := newCompilerEnvironment() 470 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 471 err := compiler.compilePreamble() 472 require.NoError(t, err) 473 474 // Emit consts operands. 475 for _, v := range []uint64{x1, x2} { 476 switch signedInt { 477 case wazeroir.SignedInt32: 478 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(int32(v))))) 479 case wazeroir.SignedInt64: 480 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(v))) 481 case wazeroir.SignedUint32: 482 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(v)))) 483 case wazeroir.SignedUint64: 484 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(v))) 485 } 486 require.NoError(t, err) 487 } 488 489 // At this point, two values exist. 490 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 491 492 // Emit the operation. 493 err = compiler.compileShr(operationPtr(wazeroir.NewOperationShr(signedInt))) 494 require.NoError(t, err) 495 496 // We consumed two values, but push the result back. 497 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 498 resultLocation := compiler.runtimeValueLocationStack().peek() 499 // Plus the result must be located on a register. 500 require.True(t, resultLocation.onRegister()) 501 // Also, the result must have an appropriate register type. 502 require.Equal(t, registerTypeGeneralPurpose, resultLocation.getRegisterType()) 503 504 err = compiler.compileReturnFunction() 505 require.NoError(t, err) 506 507 code := asm.CodeSegment{} 508 defer func() { require.NoError(t, code.Unmap()) }() 509 510 // Compile and execute the code under test. 511 _, err = compiler.compile(code.NextCodeSection()) 512 require.NoError(t, err) 513 env.exec(code.Bytes()) 514 515 // Check the stack. 516 require.Equal(t, uint64(1), env.stackPointer()) 517 518 switch signedInt { 519 case wazeroir.SignedInt32: 520 require.Equal(t, int32(x1)>>(uint32(x2)%32), env.stackTopAsInt32()) 521 case wazeroir.SignedInt64: 522 require.Equal(t, int64(x1)>>(x2%64), env.stackTopAsInt64()) 523 case wazeroir.SignedUint32: 524 require.Equal(t, uint32(x1)>>(uint32(x2)%32), env.stackTopAsUint32()) 525 case wazeroir.SignedUint64: 526 require.Equal(t, x1>>(x2%64), env.stackTopAsUint64()) 527 } 528 }) 529 } 530 }) 531 } 532 }) 533 } 534 535 func TestCompiler_compile_Le_Lt_Gt_Ge_Eq_Eqz_Ne(t *testing.T) { 536 for _, kind := range []wazeroir.OperationKind{ 537 wazeroir.OperationKindEq, 538 wazeroir.OperationKindEqz, 539 wazeroir.OperationKindNe, 540 wazeroir.OperationKindLe, 541 wazeroir.OperationKindLt, 542 wazeroir.OperationKindGe, 543 wazeroir.OperationKindGt, 544 } { 545 kind := kind 546 t.Run(kind.String(), func(t *testing.T) { 547 for _, signedType := range []wazeroir.SignedType{ 548 wazeroir.SignedTypeUint32, 549 wazeroir.SignedTypeUint64, 550 wazeroir.SignedTypeInt32, 551 wazeroir.SignedTypeInt64, 552 wazeroir.SignedTypeFloat32, 553 wazeroir.SignedTypeFloat64, 554 } { 555 signedType := signedType 556 t.Run(signedType.String(), func(t *testing.T) { 557 for _, values := range [][2]uint64{ 558 {0, 0}, 559 {1, 1}, 560 {2, 1}, 561 {100, 1}, 562 {1, 0}, 563 {0, 1}, 564 {math.MaxInt16, math.MaxInt32}, 565 {1 << 14, 1 << 21}, 566 {1 << 14, 1 << 21}, 567 {0xffff_ffff_ffff_ffff, 0}, 568 {0xffff_ffff_ffff_ffff, 1}, 569 {0, 0xffff_ffff_ffff_ffff}, 570 {1, 0xffff_ffff_ffff_ffff}, 571 {1, math.Float64bits(math.NaN())}, 572 {math.Float64bits(math.NaN()), 1}, 573 {0xffff_ffff_ffff_ffff, math.Float64bits(math.NaN())}, 574 {math.Float64bits(math.NaN()), 0xffff_ffff_ffff_ffff}, 575 {math.Float64bits(math.MaxFloat32), 1}, 576 {math.Float64bits(math.SmallestNonzeroFloat32), 1}, 577 {math.Float64bits(math.MaxFloat64), 1}, 578 {math.Float64bits(math.SmallestNonzeroFloat64), 1}, 579 {0, math.Float64bits(math.Inf(1))}, 580 {0, math.Float64bits(math.Inf(-1))}, 581 {math.Float64bits(math.Inf(1)), 0}, 582 {math.Float64bits(math.Inf(-1)), 0}, 583 {math.Float64bits(math.Inf(1)), 1}, 584 {math.Float64bits(math.Inf(-1)), 1}, 585 {math.Float64bits(1.11231), math.Float64bits(math.Inf(1))}, 586 {math.Float64bits(1.11231), math.Float64bits(math.Inf(-1))}, 587 {math.Float64bits(math.Inf(1)), math.Float64bits(1.11231)}, 588 {math.Float64bits(math.Inf(-1)), math.Float64bits(1.11231)}, 589 {math.Float64bits(math.Inf(1)), math.Float64bits(math.NaN())}, 590 {math.Float64bits(math.Inf(-1)), math.Float64bits(math.NaN())}, 591 {math.Float64bits(math.NaN()), math.Float64bits(math.Inf(1))}, 592 {math.Float64bits(math.NaN()), math.Float64bits(math.Inf(-1))}, 593 } { 594 x1, x2 := values[0], values[1] 595 isEqz := kind == wazeroir.OperationKindEqz 596 if isEqz && (signedType == wazeroir.SignedTypeFloat32 || signedType == wazeroir.SignedTypeFloat64) { 597 // Eqz isn't defined for float. 598 return 599 } 600 t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", x1, x2), func(t *testing.T) { 601 env := newCompilerEnvironment() 602 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 603 err := compiler.compilePreamble() 604 require.NoError(t, err) 605 606 // Emit consts operands. 607 for _, v := range []uint64{x1, x2} { 608 switch signedType { 609 case wazeroir.SignedTypeUint32: 610 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(v)))) 611 case wazeroir.SignedTypeInt32: 612 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(int32(v))))) 613 case wazeroir.SignedTypeInt64, wazeroir.SignedTypeUint64: 614 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(v))) 615 case wazeroir.SignedTypeFloat32: 616 err = compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(uint32(v))))) 617 case wazeroir.SignedTypeFloat64: 618 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(v)))) 619 } 620 require.NoError(t, err) 621 } 622 623 if isEqz { 624 // Eqz only needs one value, so pop the top one (x2). 625 compiler.runtimeValueLocationStack().pop() 626 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 627 } else { 628 // At this point, two values exist for comparison. 629 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 630 } 631 632 // Emit the operation. 633 switch kind { 634 case wazeroir.OperationKindLe: 635 err = compiler.compileLe(operationPtr(wazeroir.NewOperationLe(signedType))) 636 case wazeroir.OperationKindLt: 637 err = compiler.compileLt(operationPtr(wazeroir.NewOperationLt(signedType))) 638 case wazeroir.OperationKindGe: 639 err = compiler.compileGe(operationPtr(wazeroir.NewOperationGe(signedType))) 640 case wazeroir.OperationKindGt: 641 err = compiler.compileGt(operationPtr(wazeroir.NewOperationGt(signedType))) 642 case wazeroir.OperationKindEq: 643 // Eq uses UnsignedType instead, so we translate the signed one. 644 switch signedType { 645 case wazeroir.SignedTypeUint32, wazeroir.SignedTypeInt32: 646 err = compiler.compileEq(operationPtr(wazeroir.NewOperationEq(wazeroir.UnsignedTypeI32))) 647 case wazeroir.SignedTypeUint64, wazeroir.SignedTypeInt64: 648 err = compiler.compileEq(operationPtr(wazeroir.NewOperationEq(wazeroir.UnsignedTypeI64))) 649 case wazeroir.SignedTypeFloat32: 650 err = compiler.compileEq(operationPtr(wazeroir.NewOperationEq(wazeroir.UnsignedTypeF32))) 651 case wazeroir.SignedTypeFloat64: 652 err = compiler.compileEq(operationPtr(wazeroir.NewOperationEq(wazeroir.UnsignedTypeF64))) 653 } 654 case wazeroir.OperationKindNe: 655 // Ne uses UnsignedType, so we translate the signed one. 656 switch signedType { 657 case wazeroir.SignedTypeUint32, wazeroir.SignedTypeInt32: 658 err = compiler.compileNe(operationPtr(wazeroir.NewOperationNe(wazeroir.UnsignedTypeI32))) 659 case wazeroir.SignedTypeUint64, wazeroir.SignedTypeInt64: 660 err = compiler.compileNe(operationPtr(wazeroir.NewOperationNe(wazeroir.UnsignedTypeI64))) 661 case wazeroir.SignedTypeFloat32: 662 err = compiler.compileNe(operationPtr(wazeroir.NewOperationNe(wazeroir.UnsignedTypeF32))) 663 case wazeroir.SignedTypeFloat64: 664 err = compiler.compileNe(operationPtr(wazeroir.NewOperationNe(wazeroir.UnsignedTypeF64))) 665 } 666 case wazeroir.OperationKindEqz: 667 // Eqz uses UnsignedInt, so we translate the signed one. 668 switch signedType { 669 case wazeroir.SignedTypeUint32, wazeroir.SignedTypeInt32: 670 err = compiler.compileEqz(operationPtr(wazeroir.NewOperationEqz(wazeroir.UnsignedInt32))) 671 case wazeroir.SignedTypeUint64, wazeroir.SignedTypeInt64: 672 err = compiler.compileEqz(operationPtr(wazeroir.NewOperationEqz(wazeroir.UnsignedInt64))) 673 } 674 } 675 require.NoError(t, err) 676 677 // We consumed two values, but push the result back. 678 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 679 680 err = compiler.compileReturnFunction() 681 require.NoError(t, err) 682 683 code := asm.CodeSegment{} 684 defer func() { require.NoError(t, code.Unmap()) }() 685 686 // Compile and execute the code under test. 687 _, err = compiler.compile(code.NextCodeSection()) 688 require.NoError(t, err) 689 env.exec(code.Bytes()) 690 691 // There should only be one value on the stack 692 require.Equal(t, uint64(1), env.stackPointer()) 693 694 actual := env.stackTopAsUint32() == 1 695 696 switch kind { 697 case wazeroir.OperationKindLe: 698 switch signedType { 699 case wazeroir.SignedTypeInt32: 700 require.Equal(t, int32(x1) <= int32(x2), actual) 701 case wazeroir.SignedTypeUint32: 702 require.Equal(t, uint32(x1) <= uint32(x2), actual) 703 case wazeroir.SignedTypeInt64: 704 require.Equal(t, int64(x1) <= int64(x2), actual) 705 case wazeroir.SignedTypeUint64: 706 require.Equal(t, x1 <= x2, actual) 707 case wazeroir.SignedTypeFloat32: 708 require.Equal(t, math.Float32frombits(uint32(x1)) <= math.Float32frombits(uint32(x2)), actual) 709 case wazeroir.SignedTypeFloat64: 710 require.Equal(t, math.Float64frombits(x1) <= math.Float64frombits(x2), actual) 711 } 712 case wazeroir.OperationKindLt: 713 switch signedType { 714 case wazeroir.SignedTypeInt32: 715 require.Equal(t, int32(x1) < int32(x2), actual) 716 case wazeroir.SignedTypeUint32: 717 require.Equal(t, uint32(x1) < uint32(x2), actual) 718 case wazeroir.SignedTypeInt64: 719 require.Equal(t, int64(x1) < int64(x2), actual) 720 case wazeroir.SignedTypeUint64: 721 require.Equal(t, x1 < x2, actual) 722 case wazeroir.SignedTypeFloat32: 723 require.Equal(t, math.Float32frombits(uint32(x1)) < math.Float32frombits(uint32(x2)), actual) 724 case wazeroir.SignedTypeFloat64: 725 require.Equal(t, math.Float64frombits(x1) < math.Float64frombits(x2), actual) 726 } 727 case wazeroir.OperationKindGe: 728 switch signedType { 729 case wazeroir.SignedTypeInt32: 730 require.Equal(t, int32(x1) >= int32(x2), actual) 731 case wazeroir.SignedTypeUint32: 732 require.Equal(t, uint32(x1) >= uint32(x2), actual) 733 case wazeroir.SignedTypeInt64: 734 require.Equal(t, int64(x1) >= int64(x2), actual) 735 case wazeroir.SignedTypeUint64: 736 require.Equal(t, x1 >= x2, actual) 737 case wazeroir.SignedTypeFloat32: 738 require.Equal(t, math.Float32frombits(uint32(x1)) >= math.Float32frombits(uint32(x2)), actual) 739 case wazeroir.SignedTypeFloat64: 740 require.Equal(t, math.Float64frombits(x1) >= math.Float64frombits(x2), actual) 741 } 742 case wazeroir.OperationKindGt: 743 switch signedType { 744 case wazeroir.SignedTypeInt32: 745 require.Equal(t, int32(x1) > int32(x2), actual) 746 case wazeroir.SignedTypeUint32: 747 require.Equal(t, uint32(x1) > uint32(x2), actual) 748 case wazeroir.SignedTypeInt64: 749 require.Equal(t, int64(x1) > int64(x2), actual) 750 case wazeroir.SignedTypeUint64: 751 require.Equal(t, x1 > x2, actual) 752 case wazeroir.SignedTypeFloat32: 753 require.Equal(t, math.Float32frombits(uint32(x1)) > math.Float32frombits(uint32(x2)), actual) 754 case wazeroir.SignedTypeFloat64: 755 require.Equal(t, math.Float64frombits(x1) > math.Float64frombits(x2), actual) 756 } 757 case wazeroir.OperationKindEq: 758 switch signedType { 759 case wazeroir.SignedTypeInt32, wazeroir.SignedTypeUint32: 760 require.Equal(t, uint32(x1) == uint32(x2), actual) 761 case wazeroir.SignedTypeInt64, wazeroir.SignedTypeUint64: 762 require.Equal(t, x1 == x2, actual) 763 case wazeroir.SignedTypeFloat32: 764 require.Equal(t, math.Float32frombits(uint32(x1)) == math.Float32frombits(uint32(x2)), actual) 765 case wazeroir.SignedTypeFloat64: 766 require.Equal(t, math.Float64frombits(x1) == math.Float64frombits(x2), actual) 767 } 768 case wazeroir.OperationKindNe: 769 switch signedType { 770 case wazeroir.SignedTypeInt32, wazeroir.SignedTypeUint32: 771 require.Equal(t, uint32(x1) != uint32(x2), actual) 772 case wazeroir.SignedTypeInt64, wazeroir.SignedTypeUint64: 773 require.Equal(t, x1 != x2, actual) 774 case wazeroir.SignedTypeFloat32: 775 require.Equal(t, math.Float32frombits(uint32(x1)) != math.Float32frombits(uint32(x2)), actual) 776 case wazeroir.SignedTypeFloat64: 777 require.Equal(t, math.Float64frombits(x1) != math.Float64frombits(x2), actual) 778 } 779 case wazeroir.OperationKindEqz: 780 switch signedType { 781 case wazeroir.SignedTypeInt32, wazeroir.SignedTypeUint32: 782 require.Equal(t, uint32(x1) == 0, actual) 783 case wazeroir.SignedTypeInt64, wazeroir.SignedTypeUint64: 784 require.Equal(t, x1 == 0, actual) 785 } 786 } 787 }) 788 } 789 }) 790 } 791 }) 792 } 793 } 794 795 func TestCompiler_compile_Clz_Ctz_Popcnt(t *testing.T) { 796 for _, kind := range []wazeroir.OperationKind{ 797 wazeroir.OperationKindClz, 798 wazeroir.OperationKindCtz, 799 wazeroir.OperationKindPopcnt, 800 } { 801 kind := kind 802 t.Run(kind.String(), func(t *testing.T) { 803 for _, tp := range []wazeroir.UnsignedInt{wazeroir.UnsignedInt32, wazeroir.UnsignedInt64} { 804 tp := tp 805 is32bit := tp == wazeroir.UnsignedInt32 806 t.Run(tp.String(), func(t *testing.T) { 807 for _, v := range []uint64{ 808 0, 1, 1 << 4, 1 << 6, 1 << 31, 809 0b11111111110000, 0b010101010, 0b1111111111111, math.MaxUint64, 810 } { 811 name := fmt.Sprintf("%064b", v) 812 if is32bit { 813 name = fmt.Sprintf("%032b", v) 814 } 815 t.Run(name, func(t *testing.T) { 816 env := newCompilerEnvironment() 817 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 818 err := compiler.compilePreamble() 819 require.NoError(t, err) 820 821 if is32bit { 822 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(v)))) 823 } else { 824 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(v))) 825 } 826 require.NoError(t, err) 827 828 switch kind { 829 case wazeroir.OperationKindClz: 830 err = compiler.compileClz(operationPtr(wazeroir.NewOperationClz(tp))) 831 case wazeroir.OperationKindCtz: 832 err = compiler.compileCtz(operationPtr(wazeroir.NewOperationCtz(tp))) 833 case wazeroir.OperationKindPopcnt: 834 err = compiler.compilePopcnt(operationPtr(wazeroir.NewOperationPopcnt(tp))) 835 } 836 require.NoError(t, err) 837 838 err = compiler.compileReturnFunction() 839 require.NoError(t, err) 840 841 code := asm.CodeSegment{} 842 defer func() { require.NoError(t, code.Unmap()) }() 843 844 // Generate and run the code under test. 845 _, err = compiler.compile(code.NextCodeSection()) 846 require.NoError(t, err) 847 env.exec(code.Bytes()) 848 849 // One value must be pushed as a result. 850 require.Equal(t, uint64(1), env.stackPointer()) 851 852 switch kind { 853 case wazeroir.OperationKindClz: 854 if is32bit { 855 require.Equal(t, bits.LeadingZeros32(uint32(v)), int(env.stackTopAsUint32())) 856 } else { 857 require.Equal(t, bits.LeadingZeros64(v), int(env.stackTopAsUint32())) 858 } 859 case wazeroir.OperationKindCtz: 860 if is32bit { 861 require.Equal(t, bits.TrailingZeros32(uint32(v)), int(env.stackTopAsUint32())) 862 } else { 863 require.Equal(t, bits.TrailingZeros64(v), int(env.stackTopAsUint32())) 864 } 865 case wazeroir.OperationKindPopcnt: 866 if is32bit { 867 require.Equal(t, bits.OnesCount32(uint32(v)), int(env.stackTopAsUint32())) 868 } else { 869 require.Equal(t, bits.OnesCount64(v), int(env.stackTopAsUint32())) 870 } 871 } 872 }) 873 } 874 }) 875 } 876 }) 877 } 878 } 879 880 func TestCompiler_compile_Min_Max_Copysign(t *testing.T) { 881 tests := []struct { 882 name string 883 is32bit bool 884 setupFunc func(t *testing.T, compiler compilerImpl) 885 verifyFunc func(t *testing.T, x1, x2 float64, raw uint64) 886 }{ 887 { 888 name: "min-32-bit", 889 is32bit: true, 890 setupFunc: func(t *testing.T, compiler compilerImpl) { 891 err := compiler.compileMin(operationPtr(wazeroir.NewOperationMin(wazeroir.Float32))) 892 require.NoError(t, err) 893 }, 894 verifyFunc: func(t *testing.T, x1, x2 float64, raw uint64) { 895 exp := moremath.WasmCompatMin32(float32(x1), float32(x2)) 896 actual := math.Float32frombits(uint32(raw)) 897 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 898 require.True(t, math.IsNaN(float64(actual))) 899 } else { 900 require.Equal(t, math.Float32bits(exp), math.Float32bits(actual)) 901 } 902 }, 903 }, 904 { 905 name: "min-64-bit", 906 is32bit: false, 907 setupFunc: func(t *testing.T, compiler compilerImpl) { 908 err := compiler.compileMin(operationPtr(wazeroir.NewOperationMin(wazeroir.Float64))) 909 require.NoError(t, err) 910 }, 911 verifyFunc: func(t *testing.T, x1, x2 float64, raw uint64) { 912 exp := moremath.WasmCompatMin64(x1, x2) 913 actual := math.Float64frombits(raw) 914 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 915 require.True(t, math.IsNaN(actual)) 916 } else { 917 require.Equal(t, math.Float64bits(exp), math.Float64bits(actual)) 918 } 919 }, 920 }, 921 { 922 name: "max-32-bit", 923 is32bit: true, 924 setupFunc: func(t *testing.T, compiler compilerImpl) { 925 err := compiler.compileMax(operationPtr(wazeroir.NewOperationMax(wazeroir.Float32))) 926 require.NoError(t, err) 927 }, 928 verifyFunc: func(t *testing.T, x1, x2 float64, raw uint64) { 929 exp := moremath.WasmCompatMax32(float32(x1), float32(x2)) 930 actual := math.Float32frombits(uint32(raw)) 931 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 932 require.True(t, math.IsNaN(float64(actual))) 933 } else { 934 require.Equal(t, math.Float32bits(exp), math.Float32bits(actual)) 935 } 936 }, 937 }, 938 { 939 name: "max-64-bit", 940 is32bit: false, 941 setupFunc: func(t *testing.T, compiler compilerImpl) { 942 err := compiler.compileMax(operationPtr(wazeroir.NewOperationMax(wazeroir.Float64))) 943 require.NoError(t, err) 944 }, 945 verifyFunc: func(t *testing.T, x1, x2 float64, raw uint64) { 946 exp := moremath.WasmCompatMax64(x1, x2) 947 actual := math.Float64frombits(raw) 948 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 949 require.True(t, math.IsNaN(actual)) 950 } else { 951 require.Equal(t, math.Float64bits(exp), math.Float64bits(actual)) 952 } 953 }, 954 }, 955 { 956 name: "copysign-32-bit", 957 is32bit: true, 958 setupFunc: func(t *testing.T, compiler compilerImpl) { 959 err := compiler.compileCopysign(operationPtr(wazeroir.NewOperationCopysign(wazeroir.Float32))) 960 require.NoError(t, err) 961 }, 962 verifyFunc: func(t *testing.T, x1, x2 float64, raw uint64) { 963 exp := float32(math.Copysign(float64(float32(x1)), float64(float32(x2)))) 964 actual := math.Float32frombits(uint32(raw)) 965 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 966 require.True(t, math.IsNaN(float64(actual))) 967 } else { 968 require.Equal(t, exp, actual) 969 } 970 }, 971 }, 972 { 973 name: "copysign-64-bit", 974 is32bit: false, 975 setupFunc: func(t *testing.T, compiler compilerImpl) { 976 err := compiler.compileCopysign(operationPtr(wazeroir.NewOperationCopysign(wazeroir.Float64))) 977 require.NoError(t, err) 978 }, 979 verifyFunc: func(t *testing.T, x1, x2 float64, raw uint64) { 980 exp := math.Copysign(x1, x2) 981 actual := math.Float64frombits(raw) 982 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 983 require.True(t, math.IsNaN(actual)) 984 } else { 985 require.Equal(t, exp, actual) 986 } 987 }, 988 }, 989 } 990 991 for _, tt := range tests { 992 tc := tt 993 t.Run(tc.name, func(t *testing.T) { 994 for _, vs := range [][2]float64{ 995 {math.Copysign(0, 1), math.Copysign(0, 1)}, 996 {math.Copysign(0, -1), math.Copysign(0, 1)}, 997 {math.Copysign(0, 1), math.Copysign(0, -1)}, 998 {math.Copysign(0, -1), math.Copysign(0, -1)}, 999 {100, -1.1}, 1000 {100, 0}, 1001 {0, 0}, 1002 {1, 1}, 1003 {-1, 100}, 1004 {100, 200}, 1005 {100.01234124, 100.01234124}, 1006 {100.01234124, -100.01234124}, 1007 {200.12315, 100}, 1008 {6.8719476736e+10 /* = 1 << 36 */, 100}, 1009 {6.8719476736e+10 /* = 1 << 36 */, 1.37438953472e+11 /* = 1 << 37*/}, 1010 {math.Inf(1), 100}, 1011 {math.Inf(1), -100}, 1012 {100, math.Inf(1)}, 1013 {-100, math.Inf(1)}, 1014 {math.Inf(-1), 100}, 1015 {math.Inf(-1), -100}, 1016 {100, math.Inf(-1)}, 1017 {-100, math.Inf(-1)}, 1018 {math.Inf(1), 0}, 1019 {math.Inf(-1), 0}, 1020 {0, math.Inf(1)}, 1021 {0, math.Inf(-1)}, 1022 {math.NaN(), 0}, 1023 {0, math.NaN()}, 1024 {math.NaN(), 12321}, 1025 {12313, math.NaN()}, 1026 {math.NaN(), math.NaN()}, 1027 } { 1028 x1, x2 := vs[0], vs[1] 1029 t.Run(fmt.Sprintf("x1=%f_x2=%f", x1, x2), func(t *testing.T) { 1030 env := newCompilerEnvironment() 1031 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 1032 err := compiler.compilePreamble() 1033 require.NoError(t, err) 1034 1035 // Setup the target values. 1036 if tc.is32bit { 1037 err := compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(float32(x1)))) 1038 require.NoError(t, err) 1039 err = compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(float32(x2)))) 1040 require.NoError(t, err) 1041 } else { 1042 err := compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(x1))) 1043 require.NoError(t, err) 1044 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(x2))) 1045 require.NoError(t, err) 1046 } 1047 1048 // At this point two values are pushed. 1049 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 1050 require.Equal(t, 2, len(compiler.runtimeValueLocationStack().usedRegisters.list())) 1051 1052 tc.setupFunc(t, compiler) 1053 1054 // We consumed two values, but push one value after operation. 1055 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 1056 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list())) 1057 1058 err = compiler.compileReturnFunction() 1059 require.NoError(t, err) 1060 1061 code := asm.CodeSegment{} 1062 defer func() { require.NoError(t, code.Unmap()) }() 1063 1064 // Generate and run the code under test. 1065 _, err = compiler.compile(code.NextCodeSection()) 1066 require.NoError(t, err) 1067 env.exec(code.Bytes()) 1068 1069 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus()) 1070 require.Equal(t, uint64(1), env.stackPointer()) // Result must be pushed! 1071 1072 tc.verifyFunc(t, x1, x2, env.stackTopAsUint64()) 1073 }) 1074 } 1075 }) 1076 } 1077 } 1078 1079 func TestCompiler_compile_Abs_Neg_Ceil_Floor_Trunc_Nearest_Sqrt(t *testing.T) { 1080 tests := []struct { 1081 name string 1082 is32bit bool 1083 setupFunc func(t *testing.T, compiler compilerImpl) 1084 verifyFunc func(t *testing.T, v float64, raw uint64) 1085 }{ 1086 { 1087 name: "abs-32-bit", 1088 is32bit: true, 1089 setupFunc: func(t *testing.T, compiler compilerImpl) { 1090 err := compiler.compileAbs(operationPtr(wazeroir.NewOperationAbs(wazeroir.Float32))) 1091 require.NoError(t, err) 1092 }, 1093 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1094 exp := float32(math.Abs(float64(v))) 1095 actual := math.Float32frombits(uint32(raw)) 1096 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1097 require.True(t, math.IsNaN(float64(actual))) 1098 } else { 1099 require.Equal(t, exp, actual) 1100 } 1101 }, 1102 }, 1103 { 1104 name: "abs-64-bit", 1105 is32bit: false, 1106 setupFunc: func(t *testing.T, compiler compilerImpl) { 1107 err := compiler.compileAbs(operationPtr(wazeroir.NewOperationAbs(wazeroir.Float64))) 1108 require.NoError(t, err) 1109 }, 1110 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1111 exp := math.Abs(v) 1112 actual := math.Float64frombits(raw) 1113 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1114 require.True(t, math.IsNaN(actual)) 1115 } else { 1116 require.Equal(t, exp, actual) 1117 } 1118 }, 1119 }, 1120 { 1121 name: "neg-32-bit", 1122 is32bit: true, 1123 setupFunc: func(t *testing.T, compiler compilerImpl) { 1124 err := compiler.compileNeg(operationPtr(wazeroir.NewOperationNeg(wazeroir.Float32))) 1125 require.NoError(t, err) 1126 }, 1127 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1128 exp := -float32(v) 1129 actual := math.Float32frombits(uint32(raw)) 1130 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1131 require.True(t, math.IsNaN(float64(actual))) 1132 } else { 1133 require.Equal(t, exp, actual) 1134 } 1135 }, 1136 }, 1137 { 1138 name: "neg-64-bit", 1139 is32bit: false, 1140 setupFunc: func(t *testing.T, compiler compilerImpl) { 1141 err := compiler.compileNeg(operationPtr(wazeroir.NewOperationNeg(wazeroir.Float64))) 1142 require.NoError(t, err) 1143 }, 1144 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1145 exp := -v 1146 actual := math.Float64frombits(raw) 1147 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1148 require.True(t, math.IsNaN(actual)) 1149 } else { 1150 require.Equal(t, exp, actual) 1151 } 1152 }, 1153 }, 1154 { 1155 name: "ceil-32-bit", 1156 is32bit: true, 1157 setupFunc: func(t *testing.T, compiler compilerImpl) { 1158 err := compiler.compileCeil(operationPtr(wazeroir.NewOperationCeil(wazeroir.Float32))) 1159 require.NoError(t, err) 1160 }, 1161 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1162 exp := float32(math.Ceil(float64(v))) 1163 actual := math.Float32frombits(uint32(raw)) 1164 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1165 require.True(t, math.IsNaN(float64(actual))) 1166 } else { 1167 require.Equal(t, exp, actual) 1168 } 1169 }, 1170 }, 1171 { 1172 name: "ceil-64-bit", 1173 is32bit: false, 1174 setupFunc: func(t *testing.T, compiler compilerImpl) { 1175 err := compiler.compileCeil(operationPtr(wazeroir.NewOperationCeil(wazeroir.Float64))) 1176 require.NoError(t, err) 1177 }, 1178 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1179 exp := math.Ceil(v) 1180 actual := math.Float64frombits(raw) 1181 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1182 require.True(t, math.IsNaN(actual)) 1183 } else { 1184 require.Equal(t, exp, actual) 1185 } 1186 }, 1187 }, 1188 { 1189 name: "floor-32-bit", 1190 is32bit: true, 1191 setupFunc: func(t *testing.T, compiler compilerImpl) { 1192 err := compiler.compileFloor(operationPtr(wazeroir.NewOperationFloor(wazeroir.Float32))) 1193 require.NoError(t, err) 1194 }, 1195 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1196 exp := float32(math.Floor(float64(v))) 1197 actual := math.Float32frombits(uint32(raw)) 1198 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1199 require.True(t, math.IsNaN(float64(actual))) 1200 } else { 1201 require.Equal(t, exp, actual) 1202 } 1203 }, 1204 }, 1205 { 1206 name: "floor-64-bit", 1207 is32bit: false, 1208 setupFunc: func(t *testing.T, compiler compilerImpl) { 1209 err := compiler.compileFloor(operationPtr(wazeroir.NewOperationFloor(wazeroir.Float64))) 1210 require.NoError(t, err) 1211 }, 1212 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1213 exp := math.Floor(v) 1214 actual := math.Float64frombits(raw) 1215 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1216 require.True(t, math.IsNaN(actual)) 1217 } else { 1218 require.Equal(t, exp, actual) 1219 } 1220 }, 1221 }, 1222 { 1223 name: "trunc-32-bit", 1224 is32bit: true, 1225 setupFunc: func(t *testing.T, compiler compilerImpl) { 1226 err := compiler.compileTrunc(operationPtr(wazeroir.NewOperationTrunc(wazeroir.Float32))) 1227 require.NoError(t, err) 1228 }, 1229 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1230 exp := float32(math.Trunc(float64(v))) 1231 actual := math.Float32frombits(uint32(raw)) 1232 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1233 require.True(t, math.IsNaN(float64(actual))) 1234 } else { 1235 require.Equal(t, exp, actual) 1236 } 1237 }, 1238 }, 1239 { 1240 name: "trunc-64-bit", 1241 is32bit: false, 1242 setupFunc: func(t *testing.T, compiler compilerImpl) { 1243 err := compiler.compileTrunc(operationPtr(wazeroir.NewOperationTrunc(wazeroir.Float64))) 1244 require.NoError(t, err) 1245 }, 1246 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1247 exp := math.Trunc(v) 1248 actual := math.Float64frombits(raw) 1249 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1250 require.True(t, math.IsNaN(actual)) 1251 } else { 1252 require.Equal(t, exp, actual) 1253 } 1254 }, 1255 }, 1256 { 1257 name: "nearest-32-bit", 1258 is32bit: true, 1259 setupFunc: func(t *testing.T, compiler compilerImpl) { 1260 err := compiler.compileNearest(operationPtr(wazeroir.NewOperationNearest(wazeroir.Float32))) 1261 require.NoError(t, err) 1262 }, 1263 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1264 exp := moremath.WasmCompatNearestF32(float32(v)) 1265 actual := math.Float32frombits(uint32(raw)) 1266 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1267 require.True(t, math.IsNaN(float64(actual))) 1268 } else { 1269 require.Equal(t, exp, actual) 1270 } 1271 }, 1272 }, 1273 { 1274 name: "nearest-64-bit", 1275 is32bit: false, 1276 setupFunc: func(t *testing.T, compiler compilerImpl) { 1277 err := compiler.compileNearest(operationPtr(wazeroir.NewOperationNearest(wazeroir.Float64))) 1278 require.NoError(t, err) 1279 }, 1280 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1281 exp := moremath.WasmCompatNearestF64(v) 1282 actual := math.Float64frombits(raw) 1283 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1284 require.True(t, math.IsNaN(actual)) 1285 } else { 1286 require.Equal(t, exp, actual) 1287 } 1288 }, 1289 }, 1290 { 1291 name: "sqrt-32-bit", 1292 is32bit: true, 1293 setupFunc: func(t *testing.T, compiler compilerImpl) { 1294 err := compiler.compileSqrt(operationPtr(wazeroir.NewOperationSqrt(wazeroir.Float32))) 1295 require.NoError(t, err) 1296 }, 1297 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1298 exp := float32(math.Sqrt(float64(v))) 1299 actual := math.Float32frombits(uint32(raw)) 1300 if math.IsNaN(float64(exp)) { // NaN cannot be compared with themselves, so we have to use IsNaN 1301 require.True(t, math.IsNaN(float64(actual))) 1302 } else { 1303 require.Equal(t, exp, actual) 1304 } 1305 }, 1306 }, 1307 { 1308 name: "sqrt-64-bit", 1309 is32bit: false, 1310 setupFunc: func(t *testing.T, compiler compilerImpl) { 1311 err := compiler.compileSqrt(operationPtr(wazeroir.NewOperationSqrt(wazeroir.Float64))) 1312 require.NoError(t, err) 1313 }, 1314 verifyFunc: func(t *testing.T, v float64, raw uint64) { 1315 exp := math.Sqrt(v) 1316 actual := math.Float64frombits(raw) 1317 if math.IsNaN(exp) { // NaN cannot be compared with themselves, so we have to use IsNaN 1318 require.True(t, math.IsNaN(actual)) 1319 } else { 1320 require.Equal(t, exp, actual) 1321 } 1322 }, 1323 }, 1324 } 1325 1326 for _, tt := range tests { 1327 tc := tt 1328 t.Run(tc.name, func(t *testing.T) { 1329 for _, v := range []float64{ 1330 0, 1 << 63, 1<<63 | 12345, 1 << 31, 1331 1<<31 | 123455, 6.8719476736e+10, 1332 // This verifies that the impl is Wasm compatible in nearest, rather than being equivalent of math.Round. 1333 // See moremath.WasmCompatNearestF32 and moremath.WasmCompatNearestF64 1334 -4.5, 1335 1.37438953472e+11, -1.3, 1336 -1231.123, 1.3, 100.3, -100.3, 1231.123, 1337 math.Inf(1), math.Inf(-1), math.NaN(), 1338 } { 1339 v := v 1340 t.Run(fmt.Sprintf("%f", v), func(t *testing.T) { 1341 env := newCompilerEnvironment() 1342 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 1343 err := compiler.compilePreamble() 1344 require.NoError(t, err) 1345 1346 if tc.is32bit { 1347 err := compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(float32(v)))) 1348 require.NoError(t, err) 1349 } else { 1350 err := compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(v))) 1351 require.NoError(t, err) 1352 } 1353 1354 // At this point two values are pushed. 1355 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 1356 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list())) 1357 1358 tc.setupFunc(t, compiler) 1359 1360 // We consumed one value, but push the result after operation. 1361 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler) 1362 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list())) 1363 1364 err = compiler.compileReturnFunction() 1365 require.NoError(t, err) 1366 1367 code := asm.CodeSegment{} 1368 defer func() { require.NoError(t, code.Unmap()) }() 1369 1370 // Generate and run the code under test. 1371 _, err = compiler.compile(code.NextCodeSection()) 1372 require.NoError(t, err) 1373 env.exec(code.Bytes()) 1374 1375 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus()) 1376 require.Equal(t, uint64(1), env.stackPointer()) // Result must be pushed! 1377 1378 tc.verifyFunc(t, v, env.stackTopAsUint64()) 1379 }) 1380 } 1381 }) 1382 } 1383 } 1384 1385 func TestCompiler_compile_Div_Rem(t *testing.T) { 1386 for _, kind := range []wazeroir.OperationKind{ 1387 wazeroir.OperationKindDiv, 1388 wazeroir.OperationKindRem, 1389 } { 1390 kind := kind 1391 t.Run(kind.String(), func(t *testing.T) { 1392 for _, signedType := range []wazeroir.SignedType{ 1393 wazeroir.SignedTypeUint32, 1394 wazeroir.SignedTypeUint64, 1395 wazeroir.SignedTypeInt32, 1396 wazeroir.SignedTypeInt64, 1397 wazeroir.SignedTypeFloat32, 1398 wazeroir.SignedTypeFloat64, 1399 } { 1400 signedType := signedType 1401 t.Run(signedType.String(), func(t *testing.T) { 1402 for _, values := range [][2]uint64{ 1403 {0, 0}, 1404 {1, 1}, 1405 {2, 1}, 1406 {100, 1}, 1407 {1, 0}, 1408 {0, 1}, 1409 {math.MaxInt16, math.MaxInt32}, 1410 {1234, 5}, 1411 {5, 1234}, 1412 {4, 2}, 1413 {40, 4}, 1414 {123456, 4}, 1415 {1 << 14, 1 << 21}, 1416 {1 << 14, 1 << 21}, 1417 {0xffff_ffff_ffff_ffff, 0}, 1418 {0xffff_ffff_ffff_ffff, 1}, 1419 {0, 0xffff_ffff_ffff_ffff}, 1420 {1, 0xffff_ffff_ffff_ffff}, 1421 {0x80000000, 0xffffffff}, // This is equivalent to (-2^31 / -1) and results in overflow for 32-bit signed div. 1422 {0x8000000000000000, 0xffffffffffffffff}, // This is equivalent to (-2^63 / -1) and results in overflow for 64-bit signed div. 1423 {0xffffffff /* -1 in signed 32bit */, 0xfffffffe /* -2 in signed 32bit */}, 1424 {0xffffffffffffffff /* -1 in signed 64bit */, 0xfffffffffffffffe /* -2 in signed 64bit */}, 1425 {1, 0xffff_ffff_ffff_ffff}, 1426 {math.Float64bits(1.11231), math.Float64bits(12312312.12312)}, 1427 {math.Float64bits(1.11231), math.Float64bits(-12312312.12312)}, 1428 {math.Float64bits(-1.11231), math.Float64bits(12312312.12312)}, 1429 {math.Float64bits(-1.11231), math.Float64bits(-12312312.12312)}, 1430 {math.Float64bits(1.11231), math.Float64bits(12312312.12312)}, 1431 {math.Float64bits(-12312312.12312), math.Float64bits(1.11231)}, 1432 {math.Float64bits(12312312.12312), math.Float64bits(-1.11231)}, 1433 {math.Float64bits(-12312312.12312), math.Float64bits(-1.11231)}, 1434 {1, math.Float64bits(math.NaN())}, 1435 {math.Float64bits(math.NaN()), 1}, 1436 {0xffff_ffff_ffff_ffff, math.Float64bits(math.NaN())}, 1437 {math.Float64bits(math.NaN()), 0xffff_ffff_ffff_ffff}, 1438 {math.Float64bits(math.MaxFloat32), 1}, 1439 {math.Float64bits(math.SmallestNonzeroFloat32), 1}, 1440 {math.Float64bits(math.MaxFloat64), 1}, 1441 {math.Float64bits(math.SmallestNonzeroFloat64), 1}, 1442 {0, math.Float64bits(math.Inf(1))}, 1443 {0, math.Float64bits(math.Inf(-1))}, 1444 {math.Float64bits(math.Inf(1)), 0}, 1445 {math.Float64bits(math.Inf(-1)), 0}, 1446 {math.Float64bits(math.Inf(1)), 1}, 1447 {math.Float64bits(math.Inf(-1)), 1}, 1448 {math.Float64bits(1.11231), math.Float64bits(math.Inf(1))}, 1449 {math.Float64bits(1.11231), math.Float64bits(math.Inf(-1))}, 1450 {math.Float64bits(math.Inf(1)), math.Float64bits(1.11231)}, 1451 {math.Float64bits(math.Inf(-1)), math.Float64bits(1.11231)}, 1452 {math.Float64bits(math.Inf(1)), math.Float64bits(math.NaN())}, 1453 {math.Float64bits(math.Inf(-1)), math.Float64bits(math.NaN())}, 1454 {math.Float64bits(math.NaN()), math.Float64bits(math.Inf(1))}, 1455 {math.Float64bits(math.NaN()), math.Float64bits(math.Inf(-1))}, 1456 } { 1457 x1, x2 := values[0], values[1] 1458 t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", x1, x2), func(t *testing.T) { 1459 env := newCompilerEnvironment() 1460 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil) 1461 err := compiler.compilePreamble() 1462 require.NoError(t, err) 1463 1464 // Emit consts operands. 1465 for _, v := range []uint64{x1, x2} { 1466 switch signedType { 1467 case wazeroir.SignedTypeUint32: 1468 // In order to test zero value on non-zero register, we directly assign an register. 1469 loc := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack() 1470 loc.valueType = runtimeValueTypeI32 1471 err = compiler.compileEnsureOnRegister(loc) 1472 require.NoError(t, err) 1473 env.stack()[loc.stackPointer] = uint64(v) 1474 case wazeroir.SignedTypeInt32: 1475 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(uint32(int32(v))))) 1476 case wazeroir.SignedTypeInt64, wazeroir.SignedTypeUint64: 1477 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(v))) 1478 case wazeroir.SignedTypeFloat32: 1479 err = compiler.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(uint32(v))))) 1480 case wazeroir.SignedTypeFloat64: 1481 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(v)))) 1482 } 1483 require.NoError(t, err) 1484 } 1485 1486 // At this point, two values exist for comparison. 1487 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler) 1488 1489 switch kind { 1490 case wazeroir.OperationKindDiv: 1491 err = compiler.compileDiv(operationPtr(wazeroir.NewOperationDiv(signedType))) 1492 case wazeroir.OperationKindRem: 1493 switch signedType { 1494 case wazeroir.SignedTypeInt32: 1495 err = compiler.compileRem(operationPtr(wazeroir.NewOperationRem(wazeroir.SignedInt32))) 1496 case wazeroir.SignedTypeInt64: 1497 err = compiler.compileRem(operationPtr(wazeroir.NewOperationRem(wazeroir.SignedInt64))) 1498 case wazeroir.SignedTypeUint32: 1499 err = compiler.compileRem(operationPtr(wazeroir.NewOperationRem(wazeroir.SignedUint32))) 1500 case wazeroir.SignedTypeUint64: 1501 err = compiler.compileRem(operationPtr(wazeroir.NewOperationRem(wazeroir.SignedUint64))) 1502 case wazeroir.SignedTypeFloat32: 1503 // Rem undefined for float32. 1504 return 1505 case wazeroir.SignedTypeFloat64: 1506 // Rem undefined for float64. 1507 return 1508 } 1509 } 1510 require.NoError(t, err) 1511 1512 err = compiler.compileReturnFunction() 1513 require.NoError(t, err) 1514 1515 code := asm.CodeSegment{} 1516 defer func() { require.NoError(t, code.Unmap()) }() 1517 1518 // Compile and execute the code under test. 1519 _, err = compiler.compile(code.NextCodeSection()) 1520 require.NoError(t, err) 1521 env.exec(code.Bytes()) 1522 1523 switch kind { 1524 case wazeroir.OperationKindDiv: 1525 switch signedType { 1526 case wazeroir.SignedTypeUint32: 1527 if uint32(x2) == 0 { 1528 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1529 } else { 1530 require.Equal(t, uint32(x1)/uint32(x2), env.stackTopAsUint32()) 1531 } 1532 case wazeroir.SignedTypeInt32: 1533 v1, v2 := int32(x1), int32(x2) 1534 if v2 == 0 { 1535 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1536 } else if v1 == math.MinInt32 && v2 == -1 { 1537 require.Equal(t, nativeCallStatusIntegerOverflow, env.compilerStatus()) 1538 } else { 1539 require.Equal(t, v1/v2, env.stackTopAsInt32()) 1540 } 1541 case wazeroir.SignedTypeUint64: 1542 if x2 == 0 { 1543 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1544 } else { 1545 require.Equal(t, x1/x2, env.stackTopAsUint64()) 1546 } 1547 case wazeroir.SignedTypeInt64: 1548 v1, v2 := int64(x1), int64(x2) 1549 if v2 == 0 { 1550 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1551 } else if v1 == math.MinInt64 && v2 == -1 { 1552 require.Equal(t, nativeCallStatusIntegerOverflow, env.compilerStatus()) 1553 } else { 1554 require.Equal(t, v1/v2, env.stackTopAsInt64()) 1555 } 1556 case wazeroir.SignedTypeFloat32: 1557 exp := math.Float32frombits(uint32(x1)) / math.Float32frombits(uint32(x2)) 1558 // NaN cannot be compared with themselves, so we have to use IsNaN 1559 if math.IsNaN(float64(exp)) { 1560 require.True(t, math.IsNaN(float64(env.stackTopAsFloat32()))) 1561 } else { 1562 require.Equal(t, exp, env.stackTopAsFloat32()) 1563 } 1564 case wazeroir.SignedTypeFloat64: 1565 exp := math.Float64frombits(x1) / math.Float64frombits(x2) 1566 // NaN cannot be compared with themselves, so we have to use IsNaN 1567 if math.IsNaN(exp) { 1568 require.True(t, math.IsNaN(env.stackTopAsFloat64())) 1569 } else { 1570 require.Equal(t, exp, env.stackTopAsFloat64()) 1571 } 1572 } 1573 case wazeroir.OperationKindRem: 1574 switch signedType { 1575 case wazeroir.SignedTypeInt32: 1576 v1, v2 := int32(x1), int32(x2) 1577 if v2 == 0 { 1578 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1579 } else { 1580 require.Equal(t, v1%v2, env.stackTopAsInt32()) 1581 } 1582 case wazeroir.SignedTypeInt64: 1583 v1, v2 := int64(x1), int64(x2) 1584 if v2 == 0 { 1585 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1586 } else { 1587 require.Equal(t, v1%v2, env.stackTopAsInt64()) 1588 } 1589 case wazeroir.SignedTypeUint32: 1590 v1, v2 := uint32(x1), uint32(x2) 1591 if v2 == 0 { 1592 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1593 } else { 1594 require.Equal(t, v1%v2, env.stackTopAsUint32()) 1595 } 1596 case wazeroir.SignedTypeUint64: 1597 if x2 == 0 { 1598 require.Equal(t, nativeCallStatusIntegerDivisionByZero, env.compilerStatus()) 1599 } else { 1600 require.Equal(t, x1%x2, env.stackTopAsUint64()) 1601 } 1602 1603 } 1604 } 1605 }) 1606 } 1607 }) 1608 } 1609 }) 1610 } 1611 }