github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/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/wasilibs/wazerox/internal/asm"
    10  	"github.com/wasilibs/wazerox/internal/moremath"
    11  	"github.com/wasilibs/wazerox/internal/testing/require"
    12  	"github.com/wasilibs/wazerox/internal/wasm"
    13  	"github.com/wasilibs/wazerox/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  }