1 package compiler
2
3 import (
4 "fmt"
5 "math"
6 "testing"
7
8 "github.com/tetratelabs/wazero/internal/asm"
9 "github.com/tetratelabs/wazero/internal/testing/require"
10 "github.com/tetratelabs/wazero/internal/wasm"
11 "github.com/tetratelabs/wazero/internal/wazeroir"
12 )
13
14 func TestCompiler_releaseRegisterToStack(t *testing.T) {
15 const val = 10000
16 tests := []struct {
17 name string
18 stackPointer uint64
19 isFloat bool
20 }{
21 {name: "int", stackPointer: 10, isFloat: false},
22 {name: "float", stackPointer: 10, isFloat: true},
23 {name: "int-huge-height", stackPointer: math.MaxInt16 + 1, isFloat: false},
24 {name: "float-huge-height", stackPointer: math.MaxInt16 + 1, isFloat: true},
25 }
26
27 for _, tt := range tests {
28 tc := tt
29 t.Run(tc.name, func(t *testing.T) {
30 env := newCompilerEnvironment()
31
32
33 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
34 err := compiler.compilePreamble()
35 require.NoError(t, err)
36
37
38 s := &runtimeValueLocationStack{
39 sp: tc.stackPointer,
40 stack: make([]runtimeValueLocation, tc.stackPointer),
41 unreservedVectorRegisters: unreservedVectorRegisters,
42 unreservedGeneralPurposeRegisters: unreservedGeneralPurposeRegisters,
43 }
44
45 compiler.setRuntimeValueLocationStack(s)
46
47 if tc.isFloat {
48 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(val))))
49 } else {
50 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(val)))
51 }
52 require.NoError(t, err)
53
54 compiler.compileReleaseRegisterToStack(compiler.runtimeValueLocationStack().peek())
55 compiler.compileExitFromNativeCode(nativeCallStatusCodeReturned)
56
57 code := asm.CodeSegment{}
58 defer func() { require.NoError(t, code.Unmap()) }()
59
60
61 _, err = compiler.compile(code.NextCodeSection())
62 require.NoError(t, err)
63
64
65 env.callEngine().builtinFunctionGrowStack(tc.stackPointer)
66 env.exec(code.Bytes())
67
68
69 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
70 require.Equal(t, tc.stackPointer+1, env.ce.stackPointer)
71
72 if tc.isFloat {
73 require.Equal(t, math.Float64frombits(val), env.stackTopAsFloat64())
74 } else {
75 require.Equal(t, uint64(val), env.stackTopAsUint64())
76 }
77 })
78 }
79 }
80
81 func TestCompiler_compileLoadValueOnStackToRegister(t *testing.T) {
82 const val = 123
83 tests := []struct {
84 name string
85 stackPointer uint64
86 isFloat bool
87 }{
88 {name: "int", stackPointer: 10, isFloat: false},
89 {name: "float", stackPointer: 10, isFloat: true},
90 {name: "int-huge-height", stackPointer: math.MaxInt16 + 1, isFloat: false},
91 {name: "float-huge-height", stackPointer: math.MaxInt16 + 1, isFloat: true},
92 }
93
94 for _, tt := range tests {
95 tc := tt
96 t.Run(tc.name, func(t *testing.T) {
97 env := newCompilerEnvironment()
98
99
100 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
101 err := compiler.compilePreamble()
102 require.NoError(t, err)
103
104
105 compiler.runtimeValueLocationStack().sp = tc.stackPointer
106 compiler.runtimeValueLocationStack().stack = make([]runtimeValueLocation, tc.stackPointer)
107
108 require.Zero(t, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
109 loc := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
110 if tc.isFloat {
111 loc.valueType = runtimeValueTypeF64
112 } else {
113 loc.valueType = runtimeValueTypeI64
114 }
115
116 require.True(t, loc.onStack())
117
118
119 err = compiler.compileEnsureOnRegister(loc)
120 require.NoError(t, err)
121 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
122 require.True(t, loc.onRegister())
123
124
125 if tc.isFloat {
126 err = compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(1)))
127 require.NoError(t, err)
128 err = compiler.compileAdd(operationPtr(wazeroir.NewOperationAdd(wazeroir.UnsignedTypeF64)))
129 require.NoError(t, err)
130 } else {
131 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(1)))
132 require.NoError(t, err)
133 err = compiler.compileAdd(operationPtr(wazeroir.NewOperationAdd(wazeroir.UnsignedTypeI64)))
134 require.NoError(t, err)
135 }
136
137
138 compiler.compileReleaseRegisterToStack(loc)
139 require.NoError(t, err)
140 compiler.compileExitFromNativeCode(nativeCallStatusCodeReturned)
141 require.NoError(t, err)
142
143 code := asm.CodeSegment{}
144 defer func() { require.NoError(t, code.Unmap()) }()
145
146
147 _, err = compiler.compile(code.NextCodeSection())
148 require.NoError(t, err)
149
150
151 env.callEngine().builtinFunctionGrowStack(tc.stackPointer)
152 env.stack()[tc.stackPointer] = val
153 env.exec(code.Bytes())
154
155
156 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
157 require.Equal(t, tc.stackPointer+1, env.ce.stackPointer)
158
159 if tc.isFloat {
160 require.Equal(t, math.Float64frombits(val)+1, env.stackTopAsFloat64())
161 } else {
162 require.Equal(t, uint64(val)+1, env.stackTopAsUint64())
163 }
164 })
165 }
166 }
167
168 func TestCompiler_compilePick_v128(t *testing.T) {
169 const pickTargetLo, pickTargetHi uint64 = 12345, 6789
170
171 op := operationPtr(wazeroir.NewOperationPick(2, true))
172 tests := []struct {
173 name string
174 isPickTargetOnRegister bool
175 }{
176 {name: "target on register", isPickTargetOnRegister: false},
177 {name: "target on stack", isPickTargetOnRegister: true},
178 }
179
180 for _, tt := range tests {
181 tc := tt
182 t.Run(tc.name, func(t *testing.T) {
183 env := newCompilerEnvironment()
184 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
185 err := compiler.compilePreamble()
186 require.NoError(t, err)
187
188
189 if tc.isPickTargetOnRegister {
190 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(pickTargetLo, pickTargetHi)))
191 require.NoError(t, err)
192 } else {
193 lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
194 lo.valueType = runtimeValueTypeV128Lo
195 env.stack()[lo.stackPointer] = pickTargetLo
196 hi := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
197 hi.valueType = runtimeValueTypeV128Hi
198 env.stack()[hi.stackPointer] = pickTargetHi
199 }
200
201
202 _ = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
203 requireRuntimeLocationStackPointerEqual(t, uint64(3), compiler)
204
205
206 err = compiler.compilePick(op)
207 require.NoError(t, err)
208 requireRuntimeLocationStackPointerEqual(t, uint64(5), compiler)
209
210 hiLoc := compiler.runtimeValueLocationStack().peek()
211 loLoc := compiler.runtimeValueLocationStack().stack[hiLoc.stackPointer-1]
212 require.True(t, hiLoc.onRegister())
213 require.Equal(t, runtimeValueTypeV128Hi, hiLoc.valueType)
214 require.Equal(t, runtimeValueTypeV128Lo, loLoc.valueType)
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
223 _, err = compiler.compile(code.NextCodeSection())
224 require.NoError(t, err)
225 env.exec(code.Bytes())
226
227
228 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
229 require.Equal(t, uint64(5), env.stackPointer())
230
231
232 lo, hi := env.stackTopAsV128()
233 require.Equal(t, pickTargetLo, lo)
234 require.Equal(t, pickTargetHi, hi)
235 require.Equal(t, pickTargetLo, env.stack()[loLoc.stackPointer])
236 require.Equal(t, pickTargetHi, env.stack()[hiLoc.stackPointer])
237 })
238 }
239 }
240
241 func TestCompiler_compilePick(t *testing.T) {
242 const pickTargetValue uint64 = 12345
243 op := operationPtr(wazeroir.NewOperationPick(1, false))
244 tests := []struct {
245 name string
246 pickTargetSetupFunc func(compiler compilerImpl, ce *callEngine) error
247 isPickTargetFloat, isPickTargetOnRegister bool
248 }{
249 {
250 name: "float on register",
251 pickTargetSetupFunc: func(compiler compilerImpl, _ *callEngine) error {
252 return compiler.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(pickTargetValue))))
253 },
254 isPickTargetFloat: true,
255 isPickTargetOnRegister: true,
256 },
257 {
258 name: "int on register",
259 pickTargetSetupFunc: func(compiler compilerImpl, _ *callEngine) error {
260 return compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(pickTargetValue)))
261 },
262 isPickTargetFloat: false,
263 isPickTargetOnRegister: true,
264 },
265 {
266 name: "float on stack",
267 pickTargetSetupFunc: func(compiler compilerImpl, ce *callEngine) error {
268 pickTargetLocation := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
269 pickTargetLocation.valueType = runtimeValueTypeF64
270 ce.stack[pickTargetLocation.stackPointer] = pickTargetValue
271 return nil
272 },
273 isPickTargetFloat: true,
274 isPickTargetOnRegister: false,
275 },
276 {
277 name: "int on stack",
278 pickTargetSetupFunc: func(compiler compilerImpl, ce *callEngine) error {
279 pickTargetLocation := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
280 pickTargetLocation.valueType = runtimeValueTypeI64
281 ce.stack[pickTargetLocation.stackPointer] = pickTargetValue
282 return nil
283 },
284 isPickTargetFloat: false,
285 isPickTargetOnRegister: false,
286 },
287 }
288
289 for _, tt := range tests {
290 tc := tt
291 t.Run(tc.name, func(t *testing.T) {
292 env := newCompilerEnvironment()
293 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
294 err := compiler.compilePreamble()
295 require.NoError(t, err)
296
297
298 err = tc.pickTargetSetupFunc(compiler, env.callEngine())
299 require.NoError(t, err)
300 pickTargetLocation := compiler.runtimeValueLocationStack().peek()
301
302
303 _ = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
304 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
305
306
307 err = compiler.compilePick(op)
308 require.NoError(t, err)
309 requireRuntimeLocationStackPointerEqual(t, uint64(3), compiler)
310
311 pickedLocation := compiler.runtimeValueLocationStack().peek()
312 require.True(t, pickedLocation.onRegister())
313 require.Equal(t, pickTargetLocation.getRegisterType(), pickedLocation.getRegisterType())
314
315 err = compiler.compileReturnFunction()
316 require.NoError(t, err)
317
318 code := asm.CodeSegment{}
319 defer func() { require.NoError(t, code.Unmap()) }()
320
321
322 _, err = compiler.compile(code.NextCodeSection())
323 require.NoError(t, err)
324 env.exec(code.Bytes())
325
326
327 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
328 require.Equal(t, uint64(3), env.stackPointer())
329
330
331 if tc.isPickTargetFloat {
332 require.Equal(t, math.Float64frombits(pickTargetValue), env.stackTopAsFloat64())
333 require.Equal(t, math.Float64frombits(pickTargetValue), math.Float64frombits(env.stack()[pickTargetLocation.stackPointer]))
334 } else {
335 require.Equal(t, pickTargetValue, env.stackTopAsUint64())
336 require.Equal(t, pickTargetValue, env.stack()[pickTargetLocation.stackPointer])
337 }
338 })
339 }
340 }
341
342 func TestCompiler_compileDrop(t *testing.T) {
343 t.Run("range nop", func(t *testing.T) {
344 env := newCompilerEnvironment()
345 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
346
347 err := compiler.compilePreamble()
348 require.NoError(t, err)
349
350
351 liveNum := 10
352 for i := 0; i < liveNum; i++ {
353 compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
354 }
355 requireRuntimeLocationStackPointerEqual(t, uint64(liveNum), compiler)
356
357 err = compiler.compileDrop(operationPtr(wazeroir.NewOperationDrop(wazeroir.NopInclusiveRange)))
358 require.NoError(t, err)
359
360
361 requireRuntimeLocationStackPointerEqual(t, uint64(liveNum), compiler)
362
363 err = compiler.compileReturnFunction()
364 require.NoError(t, err)
365
366 code := asm.CodeSegment{}
367 defer func() { require.NoError(t, code.Unmap()) }()
368
369 _, err = compiler.compile(code.NextCodeSection())
370 require.NoError(t, err)
371
372 env.exec(code.Bytes())
373 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
374 })
375 t.Run("start top", func(t *testing.T) {
376 r := wazeroir.InclusiveRange{Start: 0, End: 2}
377 dropTargetNum := int(r.End - r.Start + 1)
378 liveNum := 5
379
380 env := newCompilerEnvironment()
381 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
382
383 err := compiler.compilePreamble()
384 require.NoError(t, err)
385
386
387 const expectedTopLiveValue = 100
388 for i := 0; i < liveNum+dropTargetNum; i++ {
389 if i == liveNum-1 {
390 err := compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(expectedTopLiveValue)))
391 require.NoError(t, err)
392 } else {
393 compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
394 }
395 }
396 requireRuntimeLocationStackPointerEqual(t, uint64(liveNum+dropTargetNum), compiler)
397
398 err = compiler.compileDrop(operationPtr(wazeroir.NewOperationDrop(r)))
399 require.NoError(t, err)
400
401
402 requireRuntimeLocationStackPointerEqual(t, uint64(liveNum), compiler)
403
404 top := compiler.runtimeValueLocationStack().peek()
405 require.True(t, top.onRegister())
406
407 err = compiler.compileReturnFunction()
408 require.NoError(t, err)
409
410 code := asm.CodeSegment{}
411 defer func() { require.NoError(t, code.Unmap()) }()
412
413 _, err = compiler.compile(code.NextCodeSection())
414 require.NoError(t, err)
415
416 env.exec(code.Bytes())
417 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
418 require.Equal(t, uint64(5), env.stackPointer())
419 require.Equal(t, uint64(expectedTopLiveValue), env.stackTopAsUint64())
420 })
421
422 t.Run("start from middle", func(t *testing.T) {
423 r := wazeroir.InclusiveRange{Start: 2, End: 3}
424 liveAboveDropStartNum := 3
425 dropTargetNum := int(r.End - r.Start + 1)
426 liveBelowDropEndNum := 5
427 total := liveAboveDropStartNum + dropTargetNum + liveBelowDropEndNum
428 liveTotal := liveAboveDropStartNum + liveBelowDropEndNum
429
430 env := newCompilerEnvironment()
431 ce := env.callEngine()
432 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
433
434 err := compiler.compilePreamble()
435 require.NoError(t, err)
436
437
438 for i := 0; i < callFrameDataSizeInUint64; i++ {
439 compiler.runtimeValueLocationStack().pop()
440 }
441
442
443 for i := 0; i < total-1; i++ {
444 loc := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
445 loc.valueType = runtimeValueTypeI32
446 ce.stack[loc.stackPointer] = uint64(i)
447 }
448
449
450 const expectedTopLiveValue = 100
451 err = compiler.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(expectedTopLiveValue)))
452 require.NoError(t, err)
453
454 require.Equal(t, uint64(total), compiler.runtimeValueLocationStack().sp)
455
456 err = compiler.compileDrop(operationPtr(wazeroir.NewOperationDrop(r)))
457 require.NoError(t, err)
458
459
460 require.Equal(t, uint64(liveTotal), compiler.runtimeValueLocationStack().sp)
461
462 require.True(t, compiler.runtimeValueLocationStack().peek().onRegister())
463
464 err = compiler.compileReturnFunction()
465 require.NoError(t, err)
466
467 code := asm.CodeSegment{}
468 defer func() { require.NoError(t, code.Unmap()) }()
469
470 _, err = compiler.compile(code.NextCodeSection())
471 require.NoError(t, err)
472
473 env.exec(code.Bytes())
474 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
475 require.Equal(t, uint64(liveTotal), env.ce.stackPointer)
476
477 stack := env.stack()[:env.stackPointer()]
478 for i, val := range stack {
479 if i <= liveBelowDropEndNum {
480 require.Equal(t, uint64(i), val)
481 } else if i == liveTotal-1 {
482 require.Equal(t, uint64(expectedTopLiveValue), val)
483 } else {
484 require.Equal(t, uint64(i+dropTargetNum), val)
485 }
486 }
487 })
488 }
489
490 func TestCompiler_compileSelect(t *testing.T) {
491
492
493
494
495
496
497
498
499
500
501
502
503 tests := []struct {
504 x1OnRegister, x2OnRegister bool
505 selectX1 bool
506 condlValueOnStack, condValueOnGPRegister, condValueOnCondRegister bool
507 }{
508
509 {x1OnRegister: true, x2OnRegister: true, selectX1: true, condlValueOnStack: true},
510 {x1OnRegister: true, x2OnRegister: true, selectX1: false, condlValueOnStack: true},
511 {x1OnRegister: true, x2OnRegister: false, selectX1: true, condlValueOnStack: true},
512 {x1OnRegister: true, x2OnRegister: false, selectX1: false, condlValueOnStack: true},
513 {x1OnRegister: false, x2OnRegister: true, selectX1: true, condlValueOnStack: true},
514 {x1OnRegister: false, x2OnRegister: true, selectX1: false, condlValueOnStack: true},
515 {x1OnRegister: false, x2OnRegister: false, selectX1: true, condlValueOnStack: true},
516 {x1OnRegister: false, x2OnRegister: false, selectX1: false, condlValueOnStack: true},
517
518 {x1OnRegister: true, x2OnRegister: true, selectX1: true, condValueOnGPRegister: true},
519 {x1OnRegister: true, x2OnRegister: true, selectX1: false, condValueOnGPRegister: true},
520 {x1OnRegister: true, x2OnRegister: false, selectX1: true, condValueOnGPRegister: true},
521 {x1OnRegister: true, x2OnRegister: false, selectX1: false, condValueOnGPRegister: true},
522 {x1OnRegister: false, x2OnRegister: true, selectX1: true, condValueOnGPRegister: true},
523 {x1OnRegister: false, x2OnRegister: true, selectX1: false, condValueOnGPRegister: true},
524 {x1OnRegister: false, x2OnRegister: false, selectX1: true, condValueOnGPRegister: true},
525 {x1OnRegister: false, x2OnRegister: false, selectX1: false, condValueOnGPRegister: true},
526
527 {x1OnRegister: true, x2OnRegister: true, selectX1: true, condValueOnCondRegister: true},
528 {x1OnRegister: true, x2OnRegister: true, selectX1: false, condValueOnCondRegister: true},
529 {x1OnRegister: true, x2OnRegister: false, selectX1: true, condValueOnCondRegister: true},
530 {x1OnRegister: true, x2OnRegister: false, selectX1: false, condValueOnCondRegister: true},
531 {x1OnRegister: false, x2OnRegister: true, selectX1: true, condValueOnCondRegister: true},
532 {x1OnRegister: false, x2OnRegister: true, selectX1: false, condValueOnCondRegister: true},
533 {x1OnRegister: false, x2OnRegister: false, selectX1: true, condValueOnCondRegister: true},
534 {x1OnRegister: false, x2OnRegister: false, selectX1: false, condValueOnCondRegister: true},
535 }
536
537 for i, tt := range tests {
538 tc := tt
539 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
540 for _, vals := range [][2]uint64{
541 {1, 2},
542 {0, 1},
543 {1, 0},
544 {math.Float64bits(-1), math.Float64bits(-1)},
545 {math.Float64bits(-1), math.Float64bits(1)},
546 {math.Float64bits(1), math.Float64bits(-1)},
547 } {
548 x1Value, x2Value := vals[0], vals[1]
549 t.Run(fmt.Sprintf("x1=0x%x,x2=0x%x", vals[0], vals[1]), func(t *testing.T) {
550 env := newCompilerEnvironment()
551 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
552
553
554
555 compiler.runtimeValueLocationStack().stack = make([]runtimeValueLocation, 100)
556
557 err := compiler.compilePreamble()
558 require.NoError(t, err)
559
560 x1 := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
561 x1.valueType = runtimeValueTypeI64
562 env.stack()[x1.stackPointer] = x1Value
563 if tc.x1OnRegister {
564 err = compiler.compileEnsureOnRegister(x1)
565 require.NoError(t, err)
566 }
567
568 x2 := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
569 x2.valueType = runtimeValueTypeI64
570 env.stack()[x2.stackPointer] = x2Value
571 if tc.x2OnRegister {
572 err = compiler.compileEnsureOnRegister(x2)
573 require.NoError(t, err)
574 }
575
576 var c *runtimeValueLocation
577 if tc.condlValueOnStack {
578 c = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
579 c.valueType = runtimeValueTypeI32
580 if tc.selectX1 {
581 env.stack()[c.stackPointer] = 1
582 } else {
583 env.stack()[c.stackPointer] = 0
584 }
585 } else if tc.condValueOnGPRegister {
586 c = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
587 c.valueType = runtimeValueTypeI32
588 if tc.selectX1 {
589 env.stack()[c.stackPointer] = 1
590 } else {
591 env.stack()[c.stackPointer] = 0
592 }
593 err = compiler.compileEnsureOnRegister(c)
594 require.NoError(t, err)
595 } else if tc.condValueOnCondRegister {
596 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0)))
597 require.NoError(t, err)
598 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0)))
599 require.NoError(t, err)
600 if tc.selectX1 {
601 err = compiler.compileEq(operationPtr(wazeroir.NewOperationEq(wazeroir.UnsignedTypeI32)))
602 } else {
603 err = compiler.compileNe(operationPtr(wazeroir.NewOperationNe(wazeroir.UnsignedTypeI32)))
604 }
605 require.NoError(t, err)
606 }
607
608
609 err = compiler.compileSelect(operationPtr(wazeroir.NewOperationSelect(false)))
610 require.NoError(t, err)
611
612
613 require.Equal(t, x1, compiler.runtimeValueLocationStack().peek())
614
615 err = compiler.compileReturnFunction()
616 require.NoError(t, err)
617
618 code := asm.CodeSegment{}
619 defer func() { require.NoError(t, code.Unmap()) }()
620
621
622 _, err = compiler.compile(code.NextCodeSection())
623 require.NoError(t, err)
624 env.exec(code.Bytes())
625
626
627 require.Equal(t, uint64(1), env.stackPointer())
628 if tc.selectX1 {
629 require.Equal(t, env.stack()[x1.stackPointer], x1Value)
630 } else {
631 require.Equal(t, env.stack()[x1.stackPointer], x2Value)
632 }
633 })
634 }
635 })
636 }
637 }
638
639 func TestCompiler_compileSwap_v128(t *testing.T) {
640 const x1Lo, x1Hi uint64 = 100000, 200000
641 const x2Lo, x2Hi uint64 = 1, 2
642
643 tests := []struct {
644 x1OnRegister, x2OnRegister bool
645 }{
646 {x1OnRegister: true, x2OnRegister: true},
647 {x1OnRegister: true, x2OnRegister: false},
648 {x1OnRegister: false, x2OnRegister: true},
649 {x1OnRegister: false, x2OnRegister: false},
650 }
651
652 for _, tt := range tests {
653 tc := tt
654 t.Run(fmt.Sprintf("x1_register=%v, x2_register=%v", tc.x1OnRegister, tc.x2OnRegister), func(t *testing.T) {
655 env := newCompilerEnvironment()
656 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
657 err := compiler.compilePreamble()
658 require.NoError(t, err)
659
660 if tc.x1OnRegister {
661 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(x1Lo, x1Hi)))
662 require.NoError(t, err)
663 } else {
664 lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
665 lo.valueType = runtimeValueTypeV128Lo
666 env.stack()[lo.stackPointer] = x1Lo
667 hi := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
668 hi.valueType = runtimeValueTypeV128Hi
669 env.stack()[hi.stackPointer] = x1Hi
670 }
671
672 _ = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
673
674 if tc.x2OnRegister {
675 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(x2Lo, x2Hi)))
676 require.NoError(t, err)
677 } else {
678 lo := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
679 lo.valueType = runtimeValueTypeV128Lo
680 env.stack()[lo.stackPointer] = x2Lo
681 hi := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
682 hi.valueType = runtimeValueTypeV128Hi
683 env.stack()[hi.stackPointer] = x2Hi
684 }
685
686
687 err = compiler.compileSet(operationPtr(wazeroir.NewOperationSet(4, true)))
688 require.NoError(t, err)
689
690 require.NoError(t, compiler.compileReturnFunction())
691
692 code := asm.CodeSegment{}
693 defer func() { require.NoError(t, code.Unmap()) }()
694
695
696 _, err = compiler.compile(code.NextCodeSection())
697 require.NoError(t, err)
698
699
700 env.exec(code.Bytes())
701
702 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
703 require.Equal(t, uint64(3), env.stackPointer())
704
705
706 st := env.stack()
707 require.Equal(t, x2Lo, st[callFrameDataSizeInUint64])
708 require.Equal(t, x2Hi, st[callFrameDataSizeInUint64+1])
709 })
710 }
711 }
712
713 func TestCompiler_compileSet(t *testing.T) {
714 var x1Value, x2Value int64 = 100, 200
715 tests := []struct {
716 x1OnConditionalRegister, x1OnRegister, x2OnRegister bool
717 }{
718 {x1OnRegister: true, x2OnRegister: true},
719 {x1OnRegister: true, x2OnRegister: false},
720 {x1OnRegister: false, x2OnRegister: true},
721 {x1OnRegister: false, x2OnRegister: false},
722
723 {x1OnConditionalRegister: true, x2OnRegister: false},
724 {x1OnConditionalRegister: true, x2OnRegister: true},
725 }
726
727 for i, tt := range tests {
728 tc := tt
729 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
730 env := newCompilerEnvironment()
731 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
732 err := compiler.compilePreamble()
733 require.NoError(t, err)
734
735 x2 := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
736 x2.valueType = runtimeValueTypeI32
737 env.stack()[x2.stackPointer] = uint64(x2Value)
738 if tc.x2OnRegister {
739 err = compiler.compileEnsureOnRegister(x2)
740 require.NoError(t, err)
741 }
742
743 _ = compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
744 if tc.x1OnRegister && !tc.x1OnConditionalRegister {
745 x1 := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
746 x1.valueType = runtimeValueTypeI32
747 env.stack()[x1.stackPointer] = uint64(x1Value)
748 err = compiler.compileEnsureOnRegister(x1)
749 require.NoError(t, err)
750 } else if !tc.x1OnConditionalRegister {
751 x1 := compiler.runtimeValueLocationStack().pushRuntimeValueLocationOnStack()
752 x1.valueType = runtimeValueTypeI32
753 env.stack()[x1.stackPointer] = uint64(x1Value)
754 } else {
755 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0)))
756 require.NoError(t, err)
757 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0)))
758 require.NoError(t, err)
759 err = compiler.compileEq(operationPtr(wazeroir.NewOperationEq(wazeroir.UnsignedTypeI32)))
760 require.NoError(t, err)
761 x1Value = 1
762 }
763
764
765 err = compiler.compileSet(operationPtr(wazeroir.NewOperationSet(2, false)))
766 require.NoError(t, err)
767
768 require.NoError(t, compiler.compileReturnFunction())
769
770 code := asm.CodeSegment{}
771 defer func() { require.NoError(t, code.Unmap()) }()
772
773
774 _, err = compiler.compile(code.NextCodeSection())
775 require.NoError(t, err)
776
777
778 env.exec(code.Bytes())
779
780 require.Equal(t, uint64(2), env.stackPointer())
781
782 require.Equal(t, uint64(x1Value), env.stack()[callFrameDataSizeInUint64])
783 })
784 }
785 }
786
View as plain text