wa-lang.org/wazero@v1.0.2/internal/engine/compiler/compiler_conversion_test.go (about)

     1  package compiler
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  
     8  	"wa-lang.org/wazero/internal/testing/require"
     9  	"wa-lang.org/wazero/internal/wazeroir"
    10  )
    11  
    12  func TestCompiler_compileReinterpret(t *testing.T) {
    13  	for _, kind := range []wazeroir.OperationKind{
    14  		wazeroir.OperationKindF32ReinterpretFromI32,
    15  		wazeroir.OperationKindF64ReinterpretFromI64,
    16  		wazeroir.OperationKindI32ReinterpretFromF32,
    17  		wazeroir.OperationKindI64ReinterpretFromF64,
    18  	} {
    19  		kind := kind
    20  		t.Run(kind.String(), func(t *testing.T) {
    21  			for _, originOnStack := range []bool{false, true} {
    22  				originOnStack := originOnStack
    23  				t.Run(fmt.Sprintf("%v", originOnStack), func(t *testing.T) {
    24  					for _, v := range []uint64{
    25  						0, 1, 1 << 16, 1 << 31, 1 << 32, 1 << 63,
    26  						math.MaxInt32, math.MaxUint32, math.MaxUint64,
    27  					} {
    28  						v := v
    29  						t.Run(fmt.Sprintf("%d", v), func(t *testing.T) {
    30  							env := newCompilerEnvironment()
    31  							compiler := env.requireNewCompiler(t, newCompiler, nil)
    32  							err := compiler.compilePreamble()
    33  							require.NoError(t, err)
    34  
    35  							if originOnStack {
    36  								loc := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
    37  								env.stack()[loc.stackPointer] = v
    38  								env.setStackPointer(1)
    39  							}
    40  
    41  							var is32Bit bool
    42  							switch kind {
    43  							case wazeroir.OperationKindF32ReinterpretFromI32:
    44  								is32Bit = true
    45  								if !originOnStack {
    46  									err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(v)})
    47  									require.NoError(t, err)
    48  								}
    49  								err = compiler.compileF32ReinterpretFromI32()
    50  								require.NoError(t, err)
    51  							case wazeroir.OperationKindF64ReinterpretFromI64:
    52  								if !originOnStack {
    53  									err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: v})
    54  									require.NoError(t, err)
    55  								}
    56  								err = compiler.compileF64ReinterpretFromI64()
    57  								require.NoError(t, err)
    58  							case wazeroir.OperationKindI32ReinterpretFromF32:
    59  								is32Bit = true
    60  								if !originOnStack {
    61  									err = compiler.compileConstF32(&wazeroir.OperationConstF32{Value: math.Float32frombits(uint32(v))})
    62  									require.NoError(t, err)
    63  								}
    64  								err = compiler.compileI32ReinterpretFromF32()
    65  								require.NoError(t, err)
    66  							case wazeroir.OperationKindI64ReinterpretFromF64:
    67  								if !originOnStack {
    68  									err = compiler.compileConstF64(&wazeroir.OperationConstF64{Value: math.Float64frombits(v)})
    69  									require.NoError(t, err)
    70  								}
    71  								err = compiler.compileI64ReinterpretFromF64()
    72  								require.NoError(t, err)
    73  							default:
    74  								t.Fail()
    75  							}
    76  
    77  							err = compiler.compileReturnFunction()
    78  							require.NoError(t, err)
    79  
    80  							// Generate and run the code under test.
    81  							code, _, err := compiler.compile()
    82  							require.NoError(t, err)
    83  							env.exec(code)
    84  
    85  							// Reinterpret must preserve the bit-pattern.
    86  							if is32Bit {
    87  								require.Equal(t, uint32(v), env.stackTopAsUint32())
    88  							} else {
    89  								require.Equal(t, v, env.stackTopAsUint64())
    90  							}
    91  						})
    92  					}
    93  				})
    94  			}
    95  		})
    96  	}
    97  }
    98  
    99  func TestCompiler_compileExtend(t *testing.T) {
   100  	for _, signed := range []bool{false, true} {
   101  		signed := signed
   102  		t.Run(fmt.Sprintf("signed=%v", signed), func(t *testing.T) {
   103  			for _, v := range []uint32{
   104  				0, 1, 1 << 14, 1 << 31, math.MaxUint32, 0xFFFFFFFF, math.MaxInt32,
   105  			} {
   106  				v := v
   107  				t.Run(fmt.Sprintf("%v", v), func(t *testing.T) {
   108  					env := newCompilerEnvironment()
   109  					compiler := env.requireNewCompiler(t, newCompiler, nil)
   110  					err := compiler.compilePreamble()
   111  					require.NoError(t, err)
   112  
   113  					// Setup the promote target.
   114  					err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: v})
   115  					require.NoError(t, err)
   116  
   117  					err = compiler.compileExtend(&wazeroir.OperationExtend{Signed: signed})
   118  					require.NoError(t, err)
   119  
   120  					err = compiler.compileReturnFunction()
   121  					require.NoError(t, err)
   122  
   123  					// Generate and run the code under test.
   124  					code, _, err := compiler.compile()
   125  					require.NoError(t, err)
   126  					env.exec(code)
   127  
   128  					require.Equal(t, uint64(1), env.stackPointer())
   129  					if signed {
   130  						expected := int64(int32(v))
   131  						require.Equal(t, expected, env.stackTopAsInt64())
   132  					} else {
   133  						expected := uint64(uint32(v))
   134  						require.Equal(t, expected, env.stackTopAsUint64())
   135  					}
   136  				})
   137  			}
   138  		})
   139  	}
   140  }
   141  
   142  func TestCompiler_compileITruncFromF(t *testing.T) {
   143  	tests := []struct {
   144  		outputType  wazeroir.SignedInt
   145  		inputType   wazeroir.Float
   146  		nonTrapping bool
   147  	}{
   148  		{outputType: wazeroir.SignedInt32, inputType: wazeroir.Float32},
   149  		{outputType: wazeroir.SignedInt32, inputType: wazeroir.Float64},
   150  		{outputType: wazeroir.SignedInt64, inputType: wazeroir.Float32},
   151  		{outputType: wazeroir.SignedInt64, inputType: wazeroir.Float64},
   152  		{outputType: wazeroir.SignedUint32, inputType: wazeroir.Float32},
   153  		{outputType: wazeroir.SignedUint32, inputType: wazeroir.Float64},
   154  		{outputType: wazeroir.SignedUint64, inputType: wazeroir.Float32},
   155  		{outputType: wazeroir.SignedUint64, inputType: wazeroir.Float64},
   156  		{outputType: wazeroir.SignedInt32, inputType: wazeroir.Float32, nonTrapping: true},
   157  		{outputType: wazeroir.SignedInt32, inputType: wazeroir.Float64, nonTrapping: true},
   158  		{outputType: wazeroir.SignedInt64, inputType: wazeroir.Float32, nonTrapping: true},
   159  		{outputType: wazeroir.SignedInt64, inputType: wazeroir.Float64, nonTrapping: true},
   160  		{outputType: wazeroir.SignedUint32, inputType: wazeroir.Float32, nonTrapping: true},
   161  		{outputType: wazeroir.SignedUint32, inputType: wazeroir.Float64, nonTrapping: true},
   162  		{outputType: wazeroir.SignedUint64, inputType: wazeroir.Float32, nonTrapping: true},
   163  		{outputType: wazeroir.SignedUint64, inputType: wazeroir.Float64, nonTrapping: true},
   164  	}
   165  
   166  	for _, tt := range tests {
   167  		tc := tt
   168  		t.Run(fmt.Sprintf("%s from %s (non-trapping=%v)", tc.outputType, tc.inputType, tc.nonTrapping), func(t *testing.T) {
   169  			for _, v := range []float64{
   170  				1.0, 100, -100, 1, -1, 100.01234124, -100.01234124, 200.12315,
   171  				6.8719476736e+10,                     /* = 1 << 36 */
   172  				-6.8719476736e+10, 1.37438953472e+11, /* = 1 << 37 */
   173  				-1.37438953472e+11, -2147483649.0, 2147483648.0, math.MinInt32,
   174  				math.MaxInt32, math.MaxUint32, math.MinInt64, math.MaxInt64,
   175  				math.MaxUint64, math.MaxFloat32, math.SmallestNonzeroFloat32, math.MaxFloat64,
   176  				math.SmallestNonzeroFloat64, math.Inf(1), math.Inf(-1), math.NaN(),
   177  			} {
   178  				v := v
   179  				if v == math.MaxInt32 {
   180  					// Note that math.MaxInt32 is rounded up to math.MaxInt32+1 in 32-bit float representation.
   181  					require.Equal(t, float32(2147483648.0) /* = math.MaxInt32+1 */, float32(v))
   182  				} else if v == math.MaxUint32 {
   183  					// Note that math.MaxUint32 is rounded up to math.MaxUint32+1 in 32-bit float representation.
   184  					require.Equal(t, float32(4294967296 /* = math.MaxUint32+1 */), float32(v))
   185  				} else if v == math.MaxInt64 {
   186  					// Note that math.MaxInt64 is rounded up to math.MaxInt64+1 in 32/64-bit float representation.
   187  					require.Equal(t, float32(9223372036854775808.0) /* = math.MaxInt64+1 */, float32(v))
   188  					require.Equal(t, float64(9223372036854775808.0) /* = math.MaxInt64+1 */, float64(v))
   189  				} else if v == math.MaxUint64 {
   190  					// Note that math.MaxUint64 is rounded up to math.MaxUint64+1 in 32/64-bit float representation.
   191  					require.Equal(t, float32(18446744073709551616.0) /* = math.MaxInt64+1 */, float32(v))
   192  					require.Equal(t, float64(18446744073709551616.0) /* = math.MaxInt64+1 */, float64(v))
   193  				}
   194  
   195  				t.Run(fmt.Sprintf("%v", v), func(t *testing.T) {
   196  					env := newCompilerEnvironment()
   197  					compiler := env.requireNewCompiler(t, newCompiler, nil)
   198  					err := compiler.compilePreamble()
   199  					require.NoError(t, err)
   200  
   201  					// Setup the conversion target.
   202  					if tc.inputType == wazeroir.Float32 {
   203  						err = compiler.compileConstF32(&wazeroir.OperationConstF32{Value: float32(v)})
   204  					} else {
   205  						err = compiler.compileConstF64(&wazeroir.OperationConstF64{Value: v})
   206  					}
   207  					require.NoError(t, err)
   208  
   209  					err = compiler.compileITruncFromF(&wazeroir.OperationITruncFromF{
   210  						InputType: tc.inputType, OutputType: tc.outputType, NonTrapping: tc.nonTrapping,
   211  					})
   212  					require.NoError(t, err)
   213  
   214  					err = compiler.compileReturnFunction()
   215  					require.NoError(t, err)
   216  
   217  					// Generate and run the code under test.
   218  					code, _, err := compiler.compile()
   219  					require.NoError(t, err)
   220  					env.exec(code)
   221  
   222  					// Check the result.
   223  					expStatus := nativeCallStatusCodeReturned
   224  					if math.IsNaN(v) {
   225  						if tc.nonTrapping {
   226  							v = 0
   227  						} else {
   228  							expStatus = nativeCallStatusCodeInvalidFloatToIntConversion
   229  						}
   230  					}
   231  					if tc.inputType == wazeroir.Float32 && tc.outputType == wazeroir.SignedInt32 {
   232  						f32 := float32(v)
   233  						exp := int32(math.Trunc(float64(f32)))
   234  						if f32 < math.MinInt32 || f32 >= math.MaxInt32 {
   235  							if tc.nonTrapping {
   236  								if f32 < 0 {
   237  									exp = math.MinInt32
   238  								} else {
   239  									exp = math.MaxInt32
   240  								}
   241  							} else {
   242  								expStatus = nativeCallStatusIntegerOverflow
   243  							}
   244  						}
   245  						if expStatus == nativeCallStatusCodeReturned {
   246  							require.Equal(t, exp, env.stackTopAsInt32())
   247  						}
   248  					} else if tc.inputType == wazeroir.Float32 && tc.outputType == wazeroir.SignedInt64 {
   249  						f32 := float32(v)
   250  						exp := int64(math.Trunc(float64(f32)))
   251  						if f32 < math.MinInt64 || f32 >= math.MaxInt64 {
   252  							if tc.nonTrapping {
   253  								if f32 < 0 {
   254  									exp = math.MinInt64
   255  								} else {
   256  									exp = math.MaxInt64
   257  								}
   258  							} else {
   259  								expStatus = nativeCallStatusIntegerOverflow
   260  							}
   261  						}
   262  						if expStatus == nativeCallStatusCodeReturned {
   263  							require.Equal(t, exp, env.stackTopAsInt64())
   264  						}
   265  					} else if tc.inputType == wazeroir.Float64 && tc.outputType == wazeroir.SignedInt32 {
   266  						if v < math.MinInt32 || v > math.MaxInt32 {
   267  							if tc.nonTrapping {
   268  								if v < 0 {
   269  									v = math.MinInt32
   270  								} else {
   271  									v = math.MaxInt32
   272  								}
   273  							} else {
   274  								expStatus = nativeCallStatusIntegerOverflow
   275  							}
   276  						}
   277  						if expStatus == nativeCallStatusCodeReturned {
   278  							require.Equal(t, int32(math.Trunc(v)), env.stackTopAsInt32())
   279  						}
   280  					} else if tc.inputType == wazeroir.Float64 && tc.outputType == wazeroir.SignedInt64 {
   281  						exp := int64(math.Trunc(v))
   282  						if v < math.MinInt64 || v >= math.MaxInt64 {
   283  							if tc.nonTrapping {
   284  								if v < 0 {
   285  									exp = math.MinInt64
   286  								} else {
   287  									exp = math.MaxInt64
   288  								}
   289  							} else {
   290  								expStatus = nativeCallStatusIntegerOverflow
   291  							}
   292  						}
   293  						if expStatus == nativeCallStatusCodeReturned {
   294  							require.Equal(t, exp, env.stackTopAsInt64())
   295  						}
   296  					} else if tc.inputType == wazeroir.Float32 && tc.outputType == wazeroir.SignedUint32 {
   297  						f32 := float32(v)
   298  						exp := uint32(math.Trunc(float64(f32)))
   299  						if f32 < 0 || f32 >= math.MaxUint32 {
   300  							if tc.nonTrapping {
   301  								if v < 0 {
   302  									exp = 0
   303  								} else {
   304  									exp = math.MaxUint32
   305  								}
   306  							} else {
   307  								expStatus = nativeCallStatusIntegerOverflow
   308  							}
   309  						}
   310  						if expStatus == nativeCallStatusCodeReturned {
   311  							require.Equal(t, exp, env.stackTopAsUint32())
   312  						}
   313  					} else if tc.inputType == wazeroir.Float64 && tc.outputType == wazeroir.SignedUint32 {
   314  						exp := uint32(math.Trunc(v))
   315  						if v < 0 || v > math.MaxUint32 {
   316  							if tc.nonTrapping {
   317  								if v < 0 {
   318  									exp = 0
   319  								} else {
   320  									exp = math.MaxUint32
   321  								}
   322  							} else {
   323  								expStatus = nativeCallStatusIntegerOverflow
   324  							}
   325  						}
   326  						if expStatus == nativeCallStatusCodeReturned {
   327  							require.Equal(t, exp, env.stackTopAsUint32())
   328  						}
   329  					} else if tc.inputType == wazeroir.Float32 && tc.outputType == wazeroir.SignedUint64 {
   330  						f32 := float32(v)
   331  						exp := uint64(math.Trunc(float64(f32)))
   332  						if f32 < 0 || f32 >= math.MaxUint64 {
   333  							if tc.nonTrapping {
   334  								if v < 0 {
   335  									exp = 0
   336  								} else {
   337  									exp = math.MaxUint64
   338  								}
   339  							} else {
   340  								expStatus = nativeCallStatusIntegerOverflow
   341  							}
   342  						}
   343  						if expStatus == nativeCallStatusCodeReturned {
   344  							require.Equal(t, exp, env.stackTopAsUint64())
   345  						}
   346  					} else if tc.inputType == wazeroir.Float64 && tc.outputType == wazeroir.SignedUint64 {
   347  						exp := uint64(math.Trunc(v))
   348  						if v < 0 || v >= math.MaxUint64 {
   349  							if tc.nonTrapping {
   350  								if v < 0 {
   351  									exp = 0
   352  								} else {
   353  									exp = math.MaxUint64
   354  								}
   355  							} else {
   356  								expStatus = nativeCallStatusIntegerOverflow
   357  							}
   358  						}
   359  						if expStatus == nativeCallStatusCodeReturned {
   360  							require.Equal(t, exp, env.stackTopAsUint64())
   361  						}
   362  					}
   363  					require.Equal(t, expStatus, env.compilerStatus())
   364  				})
   365  			}
   366  		})
   367  	}
   368  }
   369  
   370  func TestCompiler_compileFConvertFromI(t *testing.T) {
   371  	tests := []struct {
   372  		inputType  wazeroir.SignedInt
   373  		outputType wazeroir.Float
   374  	}{
   375  		{inputType: wazeroir.SignedInt32, outputType: wazeroir.Float32},
   376  		{inputType: wazeroir.SignedInt32, outputType: wazeroir.Float64},
   377  		{inputType: wazeroir.SignedInt64, outputType: wazeroir.Float32},
   378  		{inputType: wazeroir.SignedInt64, outputType: wazeroir.Float64},
   379  		{inputType: wazeroir.SignedUint32, outputType: wazeroir.Float32},
   380  		{inputType: wazeroir.SignedUint32, outputType: wazeroir.Float64},
   381  		{inputType: wazeroir.SignedUint64, outputType: wazeroir.Float32},
   382  		{inputType: wazeroir.SignedUint64, outputType: wazeroir.Float64},
   383  	}
   384  
   385  	for _, tt := range tests {
   386  		tc := tt
   387  		t.Run(fmt.Sprintf("%s from %s", tc.outputType, tc.inputType), func(t *testing.T) {
   388  			for _, v := range []uint64{
   389  				0, 1, 12345, 1 << 31, 1 << 32, 1 << 54, 1 << 63,
   390  				0xffff_ffff_ffff_ffff, 0xffff_ffff,
   391  				0xffff_ffff_ffff_fffe, 0xffff_fffe,
   392  				math.MaxUint32, math.MaxUint64, math.MaxInt32, math.MaxInt64,
   393  			} {
   394  				t.Run(fmt.Sprintf("%d", v), func(t *testing.T) {
   395  					env := newCompilerEnvironment()
   396  					compiler := env.requireNewCompiler(t, newCompiler, nil)
   397  					err := compiler.compilePreamble()
   398  					require.NoError(t, err)
   399  
   400  					// Setup the conversion target.
   401  					if tc.inputType == wazeroir.SignedInt32 || tc.inputType == wazeroir.SignedUint32 {
   402  						err = compiler.compileConstI32(&wazeroir.OperationConstI32{Value: uint32(v)})
   403  					} else {
   404  						err = compiler.compileConstI64(&wazeroir.OperationConstI64{Value: uint64(v)})
   405  					}
   406  					require.NoError(t, err)
   407  
   408  					err = compiler.compileFConvertFromI(&wazeroir.OperationFConvertFromI{
   409  						InputType: tc.inputType, OutputType: tc.outputType,
   410  					})
   411  					require.NoError(t, err)
   412  
   413  					err = compiler.compileReturnFunction()
   414  					require.NoError(t, err)
   415  
   416  					// Generate and run the code under test.
   417  					code, _, err := compiler.compile()
   418  					require.NoError(t, err)
   419  					env.exec(code)
   420  
   421  					// Check the result.
   422  					require.Equal(t, uint64(1), env.stackPointer())
   423  					actualBits := env.stackTopAsUint64()
   424  					if tc.outputType == wazeroir.Float32 && tc.inputType == wazeroir.SignedInt32 {
   425  						exp := float32(int32(v))
   426  						actual := math.Float32frombits(uint32(actualBits))
   427  						require.Equal(t, exp, actual)
   428  					} else if tc.outputType == wazeroir.Float32 && tc.inputType == wazeroir.SignedInt64 {
   429  						exp := float32(int64(v))
   430  						actual := math.Float32frombits(uint32(actualBits))
   431  						require.Equal(t, exp, actual)
   432  					} else if tc.outputType == wazeroir.Float64 && tc.inputType == wazeroir.SignedInt32 {
   433  						exp := float64(int32(v))
   434  						actual := math.Float64frombits(actualBits)
   435  						require.Equal(t, exp, actual)
   436  					} else if tc.outputType == wazeroir.Float64 && tc.inputType == wazeroir.SignedInt64 {
   437  						exp := float64(int64(v))
   438  						actual := math.Float64frombits(actualBits)
   439  						require.Equal(t, exp, actual)
   440  					} else if tc.outputType == wazeroir.Float32 && tc.inputType == wazeroir.SignedUint32 {
   441  						exp := float32(uint32(v))
   442  						actual := math.Float32frombits(uint32(actualBits))
   443  						require.Equal(t, exp, actual)
   444  					} else if tc.outputType == wazeroir.Float64 && tc.inputType == wazeroir.SignedUint32 {
   445  						exp := float64(uint32(v))
   446  						actual := math.Float64frombits(actualBits)
   447  						require.Equal(t, exp, actual)
   448  					} else if tc.outputType == wazeroir.Float32 && tc.inputType == wazeroir.SignedUint64 {
   449  						exp := float32(v)
   450  						actual := math.Float32frombits(uint32(actualBits))
   451  						require.Equal(t, exp, actual)
   452  					} else if tc.outputType == wazeroir.Float64 && tc.inputType == wazeroir.SignedUint64 {
   453  						exp := float64(v)
   454  						actual := math.Float64frombits(actualBits)
   455  						require.Equal(t, exp, actual)
   456  					}
   457  				})
   458  			}
   459  		})
   460  	}
   461  }
   462  
   463  func TestCompiler_compileF64PromoteFromF32(t *testing.T) {
   464  	for _, v := range []float32{
   465  		0, 100, -100, 1, -1,
   466  		100.01234124, -100.01234124, 200.12315,
   467  		math.MaxFloat32,
   468  		math.SmallestNonzeroFloat32,
   469  		float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.NaN()),
   470  	} {
   471  		t.Run(fmt.Sprintf("%f", v), func(t *testing.T) {
   472  			env := newCompilerEnvironment()
   473  			compiler := env.requireNewCompiler(t, newCompiler, nil)
   474  			err := compiler.compilePreamble()
   475  			require.NoError(t, err)
   476  
   477  			// Setup the promote target.
   478  			err = compiler.compileConstF32(&wazeroir.OperationConstF32{Value: v})
   479  			require.NoError(t, err)
   480  
   481  			err = compiler.compileF64PromoteFromF32()
   482  			require.NoError(t, err)
   483  
   484  			err = compiler.compileReturnFunction()
   485  			require.NoError(t, err)
   486  
   487  			// Generate and run the code under test.
   488  			code, _, err := compiler.compile()
   489  			require.NoError(t, err)
   490  			env.exec(code)
   491  
   492  			// Check the result.
   493  			require.Equal(t, uint64(1), env.stackPointer())
   494  			if math.IsNaN(float64(v)) {
   495  				require.True(t, math.IsNaN(env.stackTopAsFloat64()))
   496  			} else {
   497  				exp := float64(v)
   498  				actual := env.stackTopAsFloat64()
   499  				require.Equal(t, exp, actual)
   500  			}
   501  		})
   502  	}
   503  }
   504  
   505  func TestCompiler_compileF32DemoteFromF64(t *testing.T) {
   506  	for _, v := range []float64{
   507  		0, 100, -100, 1, -1,
   508  		100.01234124, -100.01234124, 200.12315,
   509  		math.MaxFloat32,
   510  		math.SmallestNonzeroFloat32,
   511  		math.MaxFloat64,
   512  		math.SmallestNonzeroFloat64,
   513  		6.8719476736e+10,  /* = 1 << 36 */
   514  		1.37438953472e+11, /* = 1 << 37 */
   515  		math.Inf(1), math.Inf(-1), math.NaN(),
   516  	} {
   517  		t.Run(fmt.Sprintf("%f", v), func(t *testing.T) {
   518  			env := newCompilerEnvironment()
   519  			compiler := env.requireNewCompiler(t, newCompiler, nil)
   520  			err := compiler.compilePreamble()
   521  			require.NoError(t, err)
   522  
   523  			// Setup the demote target.
   524  			err = compiler.compileConstF64(&wazeroir.OperationConstF64{Value: v})
   525  			require.NoError(t, err)
   526  
   527  			err = compiler.compileF32DemoteFromF64()
   528  			require.NoError(t, err)
   529  
   530  			err = compiler.compileReturnFunction()
   531  			require.NoError(t, err)
   532  
   533  			// Generate and run the code under test.
   534  			code, _, err := compiler.compile()
   535  			require.NoError(t, err)
   536  			env.exec(code)
   537  
   538  			// Check the result.
   539  			require.Equal(t, uint64(1), env.stackPointer())
   540  			if math.IsNaN(v) {
   541  				require.True(t, math.IsNaN(float64(env.stackTopAsFloat32())))
   542  			} else {
   543  				exp := float32(v)
   544  				actual := env.stackTopAsFloat32()
   545  				require.Equal(t, exp, actual)
   546  			}
   547  		})
   548  	}
   549  }