github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/compiler/compiler_conversion_test.go (about)

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