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