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