1 package compiler
2
3 import (
4 "encoding/binary"
5 "math"
6 "testing"
7
8 "github.com/tetratelabs/wazero/internal/asm"
9 "github.com/tetratelabs/wazero/internal/moremath"
10 "github.com/tetratelabs/wazero/internal/testing/require"
11 "github.com/tetratelabs/wazero/internal/wasm"
12 "github.com/tetratelabs/wazero/internal/wazeroir"
13 )
14
15 func TestCompiler_compileV128Add(t *testing.T) {
16 tests := []struct {
17 name string
18 shape wazeroir.Shape
19 x1, x2, exp [16]byte
20 }{
21 {
22 name: "i8x16",
23 shape: wazeroir.ShapeI8x16,
24 x1: [16]byte{0: 1, 2: 10, 10: 10},
25 x2: [16]byte{0: 10, 4: 5, 10: 5},
26 exp: [16]byte{0: 11, 2: 10, 4: 5, 10: 15},
27 },
28 {
29 name: "i16x8",
30 shape: wazeroir.ShapeI16x8,
31 x1: i16x8(1123, 0, 123, 1, 1, 5, 8, 1),
32 x2: i16x8(0, 123, 123, 0, 1, 5, 9, 1),
33 exp: i16x8(1123, 123, 246, 1, 2, 10, 17, 2),
34 },
35 {
36 name: "i32x4",
37 shape: wazeroir.ShapeI32x4,
38 x1: i32x4(i32ToU32(-123), 5, 4, math.MaxUint32),
39 x2: i32x4(i32ToU32(-10), 1, i32ToU32(-104), math.MaxUint32),
40 exp: i32x4(i32ToU32(-133), 6, i32ToU32(-100), math.MaxUint32-1),
41 },
42 {
43 name: "i64x2",
44 shape: wazeroir.ShapeI64x2,
45 x1: i64x2(i64ToU64(math.MinInt64), 12345),
46 x2: i64x2(i64ToU64(-1), i64ToU64(-12345)),
47 exp: i64x2(i64ToU64(math.MinInt64)+i64ToU64(-1), 0),
48 },
49 {
50 name: "f32x4",
51 shape: wazeroir.ShapeF32x4,
52 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))),
53 x2: f32x4(51234.12341, 123, math.MaxFloat32, -123),
54 exp: f32x4(51235.12341, 246, float32(math.Inf(1)), float32(math.Inf(-1))),
55 },
56 {
57 name: "f64x2",
58 shape: wazeroir.ShapeF64x2,
59 x1: f64x2(1.123, math.Inf(1)),
60 x2: f64x2(1.123, math.MinInt64),
61 exp: f64x2(2.246, math.Inf(1)),
62 },
63 }
64
65 for _, tc := range tests {
66 tc := tc
67 t.Run(tc.name, func(t *testing.T) {
68 env := newCompilerEnvironment()
69 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
70 &wazeroir.CompilationResult{HasMemory: true})
71
72 err := compiler.compilePreamble()
73 require.NoError(t, err)
74
75 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
76 require.NoError(t, err)
77
78 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
79 require.NoError(t, err)
80
81 err = compiler.compileV128Add(operationPtr(wazeroir.NewOperationV128Add(tc.shape)))
82 require.NoError(t, err)
83
84 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
85 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
86
87 err = compiler.compileReturnFunction()
88 require.NoError(t, err)
89
90 code := asm.CodeSegment{}
91 defer func() { require.NoError(t, code.Unmap()) }()
92
93
94 _, err = compiler.compile(code.NextCodeSection())
95 require.NoError(t, err)
96 env.exec(code.Bytes())
97
98 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
99
100 lo, hi := env.stackTopAsV128()
101 var actual [16]byte
102 binary.LittleEndian.PutUint64(actual[:8], lo)
103 binary.LittleEndian.PutUint64(actual[8:], hi)
104 require.Equal(t, tc.exp, actual)
105 })
106 }
107 }
108
109 func TestCompiler_compileV128Sub(t *testing.T) {
110 tests := []struct {
111 name string
112 shape wazeroir.Shape
113 x1, x2, exp [16]byte
114 }{
115 {
116 name: "i8x16",
117 shape: wazeroir.ShapeI8x16,
118 x1: [16]byte{0: 1, 2: 10, 10: 10},
119 x2: [16]byte{0: 10, 4: 5, 10: 5},
120 exp: [16]byte{0: i8ToU8(-9), 2: 10, 4: i8ToU8(-5), 10: 5},
121 },
122 {
123 name: "i16x8",
124 shape: wazeroir.ShapeI16x8,
125 x1: i16x8(1123, 0, 123, 1, 1, 5, 8, 1),
126 x2: i16x8(0, 123, 123, 0, 1, 5, 9, 1),
127 exp: i16x8(1123, i16ToU16(-123), 0, 1, 0, 0, i16ToU16(-1), 0),
128 },
129 {
130 name: "i32x4",
131 shape: wazeroir.ShapeI32x4,
132 x1: i32x4(i32ToU32(-123), 5, 4, math.MaxUint32),
133 x2: i32x4(i32ToU32(-10), 1, i32ToU32(-104), math.MaxUint32),
134 exp: i32x4(i32ToU32(-113), 4, 108, 0),
135 },
136 {
137 name: "i64x2",
138 shape: wazeroir.ShapeI64x2,
139 x1: i64x2(i64ToU64(math.MinInt64), 12345),
140 x2: i64x2(i64ToU64(-1), i64ToU64(-12345)),
141 exp: i64x2(i64ToU64(math.MinInt64+1), 12345*2),
142 },
143 {
144 name: "f32x4",
145 shape: wazeroir.ShapeF32x4,
146 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))),
147 x2: f32x4(51234.12341, 123, math.MaxFloat32, -123),
148 exp: f32x4(-51233.12341, 0, float32(math.Inf(1)), float32(math.Inf(-1))),
149 },
150 {
151 name: "f64x2",
152 shape: wazeroir.ShapeF64x2,
153 x1: f64x2(1.123, math.Inf(1)),
154 x2: f64x2(1.123, math.MinInt64),
155 exp: f64x2(0, math.Inf(1)),
156 },
157 }
158
159 for _, tc := range tests {
160 tc := tc
161 t.Run(tc.name, func(t *testing.T) {
162 env := newCompilerEnvironment()
163 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
164 &wazeroir.CompilationResult{HasMemory: true})
165
166 err := compiler.compilePreamble()
167 require.NoError(t, err)
168
169 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
170 require.NoError(t, err)
171
172 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
173 require.NoError(t, err)
174
175 err = compiler.compileV128Sub(operationPtr(wazeroir.NewOperationV128Sub(tc.shape)))
176 require.NoError(t, err)
177
178 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
179 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
180
181 err = compiler.compileReturnFunction()
182 require.NoError(t, err)
183
184 code := asm.CodeSegment{}
185 defer func() { require.NoError(t, code.Unmap()) }()
186
187
188 _, err = compiler.compile(code.NextCodeSection())
189 require.NoError(t, err)
190 env.exec(code.Bytes())
191
192 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
193
194 lo, hi := env.stackTopAsV128()
195 var actual [16]byte
196 binary.LittleEndian.PutUint64(actual[:8], lo)
197 binary.LittleEndian.PutUint64(actual[8:], hi)
198 require.Equal(t, tc.exp, actual)
199 })
200 }
201 }
202
203 func TestCompiler_compileV128Load(t *testing.T) {
204 tests := []struct {
205 name string
206 memSetupFn func(buf []byte)
207 loadType wazeroir.V128LoadType
208 offset uint32
209 exp [16]byte
210 }{
211 {
212 name: "v128 offset=0", loadType: wazeroir.V128LoadType128, offset: 0,
213 memSetupFn: func(buf []byte) {
214 copy(buf, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
215 },
216 exp: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
217 },
218 {
219 name: "v128 offset=2", loadType: wazeroir.V128LoadType128, offset: 2,
220 memSetupFn: func(buf []byte) {
221 copy(buf, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20})
222 },
223 exp: [16]byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
224 },
225 {
226 name: "8x8s offset=0", loadType: wazeroir.V128LoadType8x8s, offset: 0,
227 memSetupFn: func(buf []byte) {
228 copy(buf, []byte{
229 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10,
230 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
231 })
232 },
233 exp: [16]byte{
234 1, 0, 0xff, 0xff, 3, 0, 0xff, 0xff, 5, 0, 0xff, 0xff, 7, 0, 0xff, 0xff,
235 },
236 },
237 {
238 name: "8x8s offset=3", loadType: wazeroir.V128LoadType8x8s, offset: 3,
239 memSetupFn: func(buf []byte) {
240 copy(buf, []byte{
241 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10,
242 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
243 })
244 },
245 exp: [16]byte{
246 0xff, 0xff, 5, 0, 0xff, 0xff, 7, 0, 0xff, 0xff, 9, 0, 10, 0, 11, 0,
247 },
248 },
249 {
250 name: "8x8u offset=0", loadType: wazeroir.V128LoadType8x8u, offset: 0,
251 memSetupFn: func(buf []byte) {
252 copy(buf, []byte{
253 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10,
254 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
255 })
256 },
257 exp: [16]byte{
258 1, 0, 0xff, 0, 3, 0, 0xff, 0, 5, 0, 0xff, 0, 7, 0, 0xff, 0,
259 },
260 },
261 {
262 name: "8x8i offset=3", loadType: wazeroir.V128LoadType8x8u, offset: 3,
263 memSetupFn: func(buf []byte) {
264 copy(buf, []byte{
265 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10,
266 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
267 })
268 },
269 exp: [16]byte{
270 0xff, 0, 5, 0, 0xff, 0, 7, 0, 0xff, 0, 9, 0, 10, 0, 11, 0,
271 },
272 },
273 {
274 name: "16x4s offset=0", loadType: wazeroir.V128LoadType16x4s, offset: 0,
275 memSetupFn: func(buf []byte) {
276 copy(buf, []byte{
277 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10,
278 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
279 })
280 },
281 exp: [16]byte{
282 1, 0xff, 0xff, 0xff,
283 3, 0xff, 0xff, 0xff,
284 5, 0xff, 0xff, 0xff,
285 7, 0xff, 0xff, 0xff,
286 },
287 },
288 {
289 name: "16x4s offset=3", loadType: wazeroir.V128LoadType16x4s, offset: 3,
290 memSetupFn: func(buf []byte) {
291 copy(buf, []byte{
292 1, 0xff, 3, 0xff, 5, 6, 0xff, 0xff, 9, 10,
293 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
294 })
295 },
296 exp: [16]byte{
297 0xff, 5, 0, 0,
298 6, 0xff, 0xff, 0xff,
299 0xff, 9, 0, 0,
300 10, 11, 0, 0,
301 },
302 },
303 {
304 name: "16x4u offset=0", loadType: wazeroir.V128LoadType16x4u, offset: 0,
305 memSetupFn: func(buf []byte) {
306 copy(buf, []byte{
307 1, 0xff, 3, 0xff, 5, 0xff, 7, 0xff, 9, 10,
308 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
309 })
310 },
311 exp: [16]byte{
312 1, 0xff, 0, 0,
313 3, 0xff, 0, 0,
314 5, 0xff, 0, 0,
315 7, 0xff, 0, 0,
316 },
317 },
318 {
319 name: "16x4u offset=3", loadType: wazeroir.V128LoadType16x4u, offset: 3,
320 memSetupFn: func(buf []byte) {
321 copy(buf, []byte{
322 1, 0xff, 3, 0xff, 5, 6, 0xff, 0xff, 9, 10,
323 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
324 })
325 },
326 exp: [16]byte{
327 0xff, 5, 0, 0,
328 6, 0xff, 0, 0,
329 0xff, 9, 0, 0,
330 10, 11, 0, 0,
331 },
332 },
333 {
334 name: "32x2s offset=0", loadType: wazeroir.V128LoadType32x2s, offset: 0,
335 memSetupFn: func(buf []byte) {
336 copy(buf, []byte{
337 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 10,
338 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
339 })
340 },
341 exp: [16]byte{
342 1, 0xff, 3, 0xff, 0xff, 0xff, 0xff, 0xff,
343 5, 6, 7, 0xff, 0xff, 0xff, 0xff, 0xff,
344 },
345 },
346 {
347 name: "32x2s offset=2", loadType: wazeroir.V128LoadType32x2s, offset: 2,
348 memSetupFn: func(buf []byte) {
349 copy(buf, []byte{
350 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
351 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
352 })
353 },
354 exp: [16]byte{
355 3, 0xff, 5, 6, 0, 0, 0, 0,
356 7, 0xff, 9, 0xff, 0xff, 0xff, 0xff, 0xff,
357 },
358 },
359 {
360 name: "32x2u offset=0", loadType: wazeroir.V128LoadType32x2u, offset: 0,
361 memSetupFn: func(buf []byte) {
362 copy(buf, []byte{
363 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 10,
364 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
365 })
366 },
367 exp: [16]byte{
368 1, 0xff, 3, 0xff, 0, 0, 0, 0,
369 5, 6, 7, 0xff, 0, 0, 0, 0,
370 },
371 },
372 {
373 name: "32x2u offset=2", loadType: wazeroir.V128LoadType32x2u, offset: 2,
374 memSetupFn: func(buf []byte) {
375 copy(buf, []byte{
376 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
377 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
378 })
379 },
380 exp: [16]byte{
381 3, 0xff, 5, 6, 0, 0, 0, 0,
382 7, 0xff, 9, 0xff, 0, 0, 0, 0,
383 },
384 },
385 {
386 name: "32zero offset=0", loadType: wazeroir.V128LoadType32zero, offset: 0,
387 memSetupFn: func(buf []byte) {
388 copy(buf, []byte{
389 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
390 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
391 })
392 },
393 exp: [16]byte{
394 1, 0xff, 3, 0xff, 0, 0, 0, 0,
395 0, 0, 0, 0, 0, 0, 0, 0,
396 },
397 },
398 {
399 name: "32zero offset=3", loadType: wazeroir.V128LoadType32zero, offset: 3,
400 memSetupFn: func(buf []byte) {
401 copy(buf, []byte{
402 1, 0xff, 3, 0xff, 5, 6, 0xff, 8, 9, 0xff,
403 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
404 })
405 },
406 exp: [16]byte{
407 0xff, 5, 6, 0xff, 0, 0, 0, 0,
408 0, 0, 0, 0, 0, 0, 0, 0,
409 },
410 },
411 {
412 name: "32zero on ceil", loadType: wazeroir.V128LoadType32zero,
413 offset: wasm.MemoryPageSize - 4,
414 memSetupFn: func(buf []byte) {
415 copy(buf[wasm.MemoryPageSize-8:], []byte{
416 1, 0xff, 3, 0xff,
417 5, 6, 0xff, 8,
418 })
419 },
420 exp: [16]byte{5, 6, 0xff, 8},
421 },
422 {
423 name: "64zero offset=0", loadType: wazeroir.V128LoadType64zero, offset: 0,
424 memSetupFn: func(buf []byte) {
425 copy(buf, []byte{
426 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
427 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
428 })
429 },
430 exp: [16]byte{
431 1, 0xff, 3, 0xff, 5, 6, 7, 0xff,
432 0, 0, 0, 0, 0, 0, 0, 0,
433 },
434 },
435 {
436 name: "64zero offset=2", loadType: wazeroir.V128LoadType64zero, offset: 2,
437 memSetupFn: func(buf []byte) {
438 copy(buf, []byte{
439 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
440 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
441 })
442 },
443 exp: [16]byte{
444 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
445 0, 0, 0, 0, 0, 0, 0, 0,
446 },
447 },
448 {
449 name: "64zero on ceil", loadType: wazeroir.V128LoadType64zero,
450 offset: wasm.MemoryPageSize - 8,
451 memSetupFn: func(buf []byte) {
452 copy(buf[wasm.MemoryPageSize-16:], []byte{
453 1, 0xff, 3, 0xff, 5, 6, 7, 0xff,
454 9, 0xff, 11, 12, 13, 14, 15,
455 })
456 },
457 exp: [16]byte{9, 0xff, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0},
458 },
459 {
460 name: "8splat offset=0", loadType: wazeroir.V128LoadType8Splat, offset: 0,
461 memSetupFn: func(buf []byte) {
462 copy(buf, []byte{
463 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
464 })
465 },
466 exp: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
467 },
468 {
469 name: "8splat offset=1", loadType: wazeroir.V128LoadType8Splat, offset: 1,
470 memSetupFn: func(buf []byte) {
471 copy(buf, []byte{
472 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
473 })
474 },
475 exp: [16]byte{
476 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
477 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
478 },
479 },
480 {
481 name: "16splat offset=0", loadType: wazeroir.V128LoadType16Splat, offset: 0,
482 memSetupFn: func(buf []byte) {
483 copy(buf, []byte{
484 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
485 })
486 },
487 exp: [16]byte{1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff},
488 },
489 {
490 name: "16splat offset=5", loadType: wazeroir.V128LoadType16Splat, offset: 5,
491 memSetupFn: func(buf []byte) {
492 copy(buf, []byte{
493 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
494 })
495 },
496 exp: [16]byte{6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7},
497 },
498 {
499 name: "32splat offset=0", loadType: wazeroir.V128LoadType32Splat, offset: 0,
500 memSetupFn: func(buf []byte) {
501 copy(buf, []byte{
502 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
503 })
504 },
505 exp: [16]byte{1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff, 1, 0xff, 3, 0xff},
506 },
507 {
508 name: "32splat offset=1", loadType: wazeroir.V128LoadType32Splat, offset: 1,
509 memSetupFn: func(buf []byte) {
510 copy(buf, []byte{
511 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
512 })
513 },
514 exp: [16]byte{0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5, 0xff, 3, 0xff, 5},
515 },
516 {
517 name: "64splat offset=0", loadType: wazeroir.V128LoadType64Splat, offset: 0,
518 memSetupFn: func(buf []byte) {
519 copy(buf, []byte{
520 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
521 })
522 },
523 exp: [16]byte{1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 1, 0xff, 3, 0xff, 5, 6, 7, 0xff},
524 },
525 {
526 name: "64splat offset=1", loadType: wazeroir.V128LoadType64Splat, offset: 1,
527 memSetupFn: func(buf []byte) {
528 copy(buf, []byte{
529 1, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff,
530 })
531 },
532 exp: [16]byte{0xff, 3, 0xff, 5, 6, 7, 0xff, 9, 0xff, 3, 0xff, 5, 6, 7, 0xff, 9},
533 },
534 }
535
536 for _, tc := range tests {
537 tc := tc
538 t.Run(tc.name, func(t *testing.T) {
539 env := newCompilerEnvironment()
540 tc.memSetupFn(env.memory())
541
542 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
543 &wazeroir.CompilationResult{HasMemory: true})
544
545 err := compiler.compilePreamble()
546 require.NoError(t, err)
547
548 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(tc.offset)))
549 require.NoError(t, err)
550
551 err = compiler.compileV128Load(operationPtr(wazeroir.NewOperationV128Load(tc.loadType, wazeroir.MemoryArg{})))
552 require.NoError(t, err)
553
554 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
555 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
556 loadedLocation := compiler.runtimeValueLocationStack().peek()
557 require.True(t, loadedLocation.onRegister())
558
559 err = compiler.compileReturnFunction()
560 require.NoError(t, err)
561
562 code := asm.CodeSegment{}
563 defer func() { require.NoError(t, code.Unmap()) }()
564
565
566 _, err = compiler.compile(code.NextCodeSection())
567 require.NoError(t, err)
568 env.exec(code.Bytes())
569
570 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
571
572 require.Equal(t, uint64(2), env.stackPointer())
573 lo, hi := env.stackTopAsV128()
574
575 var actual [16]byte
576 binary.LittleEndian.PutUint64(actual[:8], lo)
577 binary.LittleEndian.PutUint64(actual[8:], hi)
578 require.Equal(t, tc.exp, actual)
579 })
580 }
581 }
582
583 func TestCompiler_compileV128LoadLane(t *testing.T) {
584 originalVecLo, originalVecHi := uint64(0), uint64(0)
585 tests := []struct {
586 name string
587 memSetupFn func(buf []byte)
588 laneIndex, laneSize byte
589 offset uint32
590 exp [16]byte
591 }{
592 {
593 name: "8_lane offset=0 laneIndex=0",
594 memSetupFn: func(buf []byte) {
595 copy(buf, []byte{
596 1, 0xff,
597 })
598 },
599 laneSize: 8,
600 laneIndex: 0,
601 offset: 0,
602 exp: [16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
603 },
604 {
605 name: "8_lane offset=1 laneIndex=0",
606 memSetupFn: func(buf []byte) {
607 copy(buf, []byte{
608 1, 0xff,
609 })
610 },
611 laneSize: 8,
612 laneIndex: 0,
613 offset: 1,
614 exp: [16]byte{0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
615 },
616 {
617 name: "8_lane offset=1 laneIndex=5",
618 memSetupFn: func(buf []byte) {
619 copy(buf, []byte{
620 1, 0xff,
621 })
622 },
623 laneSize: 8,
624 laneIndex: 5,
625 offset: 1,
626 exp: [16]byte{0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
627 },
628 {
629 name: "16_lane offset=0 laneIndex=0",
630 memSetupFn: func(buf []byte) {
631 copy(buf, []byte{
632 1, 0xff, 1, 0xa,
633 })
634 },
635 laneSize: 16,
636 laneIndex: 0,
637 offset: 0,
638 exp: [16]byte{1, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
639 },
640 {
641 name: "16_lane offset=1 laneIndex=0",
642 memSetupFn: func(buf []byte) {
643 copy(buf, []byte{
644 1, 0xff, 1, 0xa,
645 })
646 },
647 laneSize: 16,
648 laneIndex: 0,
649 offset: 1,
650 exp: [16]byte{0xff, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
651 },
652 {
653 name: "16_lane offset=1 laneIndex=5",
654 memSetupFn: func(buf []byte) {
655 copy(buf, []byte{
656 1, 0xff, 1, 0xa,
657 })
658 },
659 laneSize: 16,
660 laneIndex: 5,
661 offset: 1,
662 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 1, 0, 0, 0, 0},
663 },
664 {
665 name: "32_lane offset=0 laneIndex=0",
666 memSetupFn: func(buf []byte) {
667 copy(buf, []byte{
668 1, 0xff, 1, 0xa, 0x9, 0x8,
669 })
670 },
671 laneSize: 32,
672 laneIndex: 0,
673 offset: 0,
674 exp: [16]byte{1, 0xff, 1, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
675 },
676 {
677 name: "32_lane offset=1 laneIndex=0",
678 memSetupFn: func(buf []byte) {
679 copy(buf, []byte{
680 1, 0xff, 1, 0xa, 0x9, 0x8,
681 })
682 },
683 laneSize: 32,
684 laneIndex: 0,
685 offset: 1,
686 exp: [16]byte{0xff, 1, 0xa, 0x9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
687 },
688 {
689 name: "32_lane offset=1 laneIndex=3",
690 memSetupFn: func(buf []byte) {
691 copy(buf, []byte{
692 1, 0xff, 1, 0xa, 0x9, 0x8,
693 })
694 },
695 laneSize: 32,
696 laneIndex: 3,
697 offset: 1,
698 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 1, 0xa, 0x9},
699 },
700
701 {
702 name: "64_lane offset=0 laneIndex=0",
703 memSetupFn: func(buf []byte) {
704 copy(buf, []byte{
705 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4,
706 })
707 },
708 laneSize: 64,
709 laneIndex: 0,
710 offset: 0,
711 exp: [16]byte{1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0, 0, 0, 0, 0, 0, 0, 0},
712 },
713 {
714 name: "64_lane offset=1 laneIndex=0",
715 memSetupFn: func(buf []byte) {
716 copy(buf, []byte{
717 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4,
718 })
719 },
720 laneSize: 64,
721 laneIndex: 0,
722 offset: 1,
723 exp: [16]byte{0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0, 0, 0, 0, 0, 0, 0, 0},
724 },
725 {
726 name: "64_lane offset=3 laneIndex=1",
727 memSetupFn: func(buf []byte) {
728 copy(buf, []byte{
729 1, 0xff, 1, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 0xa,
730 })
731 },
732 laneSize: 64,
733 laneIndex: 1,
734 offset: 3,
735 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xa, 0x9, 0x8, 0x1, 0x2, 0x3, 0x4, 0xa},
736 },
737 }
738
739 for _, tc := range tests {
740 tc := tc
741 t.Run(tc.name, func(t *testing.T) {
742 env := newCompilerEnvironment()
743 tc.memSetupFn(env.memory())
744
745 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
746 &wazeroir.CompilationResult{HasMemory: true})
747
748 err := compiler.compilePreamble()
749 require.NoError(t, err)
750
751 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(tc.offset)))
752 require.NoError(t, err)
753
754 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(originalVecLo, originalVecHi)))
755 require.NoError(t, err)
756
757 err = compiler.compileV128LoadLane(
758 operationPtr(wazeroir.NewOperationV128LoadLane(tc.laneIndex, tc.laneSize, wazeroir.MemoryArg{})))
759 require.NoError(t, err)
760
761 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
762 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
763 loadedLocation := compiler.runtimeValueLocationStack().peek()
764 require.True(t, loadedLocation.onRegister())
765
766 err = compiler.compileReturnFunction()
767 require.NoError(t, err)
768
769 code := asm.CodeSegment{}
770 defer func() { require.NoError(t, code.Unmap()) }()
771
772
773 _, err = compiler.compile(code.NextCodeSection())
774 require.NoError(t, err)
775 env.exec(code.Bytes())
776
777 require.Equal(t, uint64(2), env.stackPointer())
778 lo, hi := env.stackTopAsV128()
779
780 var actual [16]byte
781 binary.LittleEndian.PutUint64(actual[:8], lo)
782 binary.LittleEndian.PutUint64(actual[8:], hi)
783 require.Equal(t, tc.exp, actual)
784 })
785 }
786 }
787
788 func TestCompiler_compileV128Store(t *testing.T) {
789 tests := []struct {
790 name string
791 offset uint32
792 }{
793 {name: "offset=1", offset: 1},
794 {name: "offset=5", offset: 5},
795 {name: "offset=10", offset: 10},
796 }
797
798 for _, tc := range tests {
799 tc := tc
800 t.Run(tc.name, func(t *testing.T) {
801 env := newCompilerEnvironment()
802
803 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
804 &wazeroir.CompilationResult{HasMemory: true})
805
806 err := compiler.compilePreamble()
807 require.NoError(t, err)
808
809 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(tc.offset)))
810 require.NoError(t, err)
811
812 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(^uint64(0), ^uint64(0))))
813 require.NoError(t, err)
814
815 err = compiler.compileV128Store(operationPtr(wazeroir.NewOperationV128Store(wazeroir.MemoryArg{})))
816 require.NoError(t, err)
817
818 requireRuntimeLocationStackPointerEqual(t, uint64(0), compiler)
819 require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
820
821 err = compiler.compileReturnFunction()
822 require.NoError(t, err)
823
824 code := asm.CodeSegment{}
825 defer func() { require.NoError(t, code.Unmap()) }()
826
827
828 _, err = compiler.compile(code.NextCodeSection())
829 require.NoError(t, err)
830 env.exec(code.Bytes())
831
832 require.Equal(t, uint64(0), env.stackPointer())
833
834 mem := env.memory()
835 require.Equal(t, []byte{
836 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
837 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
838 },
839 mem[tc.offset:tc.offset+16])
840 })
841 }
842 }
843
844 func TestCompiler_compileV128StoreLane(t *testing.T) {
845 vecBytes := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
846 tests := []struct {
847 name string
848 laneIndex, laneSize byte
849 offset uint32
850 exp [16]byte
851 }{
852 {
853 name: "8_lane offset=0 laneIndex=0",
854 laneSize: 8,
855 laneIndex: 0,
856 offset: 0,
857 exp: [16]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
858 },
859 {
860 name: "8_lane offset=1 laneIndex=0",
861 laneSize: 8,
862 laneIndex: 0,
863 offset: 1,
864 exp: [16]byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
865 },
866 {
867 name: "8_lane offset=3 laneIndex=5",
868 laneSize: 8,
869 laneIndex: 5,
870 offset: 3,
871 exp: [16]byte{0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
872 },
873 {
874 name: "16_lane offset=0 laneIndex=0",
875 laneSize: 16,
876 laneIndex: 0,
877 offset: 0,
878 exp: [16]byte{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
879 },
880 {
881 name: "16_lane offset=1 laneIndex=0",
882 laneSize: 16,
883 laneIndex: 0,
884 offset: 1,
885 exp: [16]byte{0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
886 },
887 {
888 name: "16_lane offset=5 laneIndex=7",
889 laneSize: 16,
890 laneIndex: 7,
891 offset: 5,
892 exp: [16]byte{0, 0, 0, 0, 0, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0},
893 },
894
895 {
896 name: "32_lane offset=0 laneIndex=0",
897 laneSize: 32,
898 laneIndex: 0,
899 offset: 0,
900 exp: [16]byte{1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
901 },
902 {
903 name: "32_lane offset=1 laneIndex=0",
904 laneSize: 32,
905 laneIndex: 0,
906 offset: 1,
907 exp: [16]byte{0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
908 },
909 {
910 name: "32_lane offset=5 laneIndex=3",
911 laneSize: 32,
912 laneIndex: 3,
913 offset: 5,
914 exp: [16]byte{0, 0, 0, 0, 0, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0},
915 },
916
917 {
918 name: "64_lane offset=0 laneIndex=0",
919 laneSize: 64,
920 laneIndex: 0,
921 offset: 0,
922 exp: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0},
923 },
924 {
925 name: "64_lane offset=1 laneIndex=0",
926 laneSize: 64,
927 laneIndex: 0,
928 offset: 1,
929 exp: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0},
930 },
931 {
932 name: "64_lane offset=5 laneIndex=3",
933 laneSize: 64,
934 laneIndex: 1,
935 offset: 6,
936 exp: [16]byte{0, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, 0, 0},
937 },
938 }
939
940 for _, tc := range tests {
941 tc := tc
942 t.Run(tc.name, func(t *testing.T) {
943 env := newCompilerEnvironment()
944
945 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
946 &wazeroir.CompilationResult{HasMemory: true})
947
948 err := compiler.compilePreamble()
949 require.NoError(t, err)
950
951 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(tc.offset)))
952 require.NoError(t, err)
953
954 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(vecBytes[:8]), binary.LittleEndian.Uint64(vecBytes[8:]))))
955 require.NoError(t, err)
956
957 err = compiler.compileV128StoreLane(operationPtr(wazeroir.NewOperationV128StoreLane(tc.laneIndex, tc.laneSize, wazeroir.MemoryArg{})))
958 require.NoError(t, err)
959
960 requireRuntimeLocationStackPointerEqual(t, uint64(0), compiler)
961 require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
962
963 err = compiler.compileReturnFunction()
964 require.NoError(t, err)
965
966 code := asm.CodeSegment{}
967 defer func() { require.NoError(t, code.Unmap()) }()
968
969
970 _, err = compiler.compile(code.NextCodeSection())
971 require.NoError(t, err)
972 env.exec(code.Bytes())
973
974 require.Equal(t, tc.exp[:], env.memory()[:16])
975 })
976 }
977 }
978
979 func TestCompiler_compileV128ExtractLane(t *testing.T) {
980 tests := []struct {
981 name string
982 vecBytes [16]byte
983 shape wazeroir.Shape
984 signed bool
985 laneIndex byte
986 exp uint64
987 }{
988 {
989 name: "i8x16 unsigned index=0",
990 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
991 shape: wazeroir.ShapeI8x16,
992 signed: false,
993 laneIndex: 0,
994 exp: uint64(byte(1)),
995 },
996 {
997 name: "i8x16 unsigned index=15",
998 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xff},
999 shape: wazeroir.ShapeI8x16,
1000 signed: false,
1001 laneIndex: 15,
1002 exp: uint64(byte(0xff)),
1003 },
1004 {
1005 name: "i8x16 signed index=0",
1006 vecBytes: [16]byte{0xf1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
1007 shape: wazeroir.ShapeI8x16,
1008 signed: true,
1009 laneIndex: 0,
1010 exp: uint64(0xff_ff_ff_f1),
1011 },
1012 {
1013 name: "i8x16 signed index=1",
1014 vecBytes: [16]byte{0xf0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
1015 shape: wazeroir.ShapeI8x16,
1016 signed: true,
1017 laneIndex: 1,
1018 exp: uint64(2),
1019 },
1020 {
1021 name: "i16x8 unsigned index=0",
1022 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
1023 shape: wazeroir.ShapeI16x8,
1024 signed: false,
1025 laneIndex: 0,
1026 exp: uint64(uint16(0x2<<8 | 0x1)),
1027 },
1028 {
1029 name: "i16x8 unsigned index=7",
1030 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xff},
1031 shape: wazeroir.ShapeI16x8,
1032 signed: false,
1033 laneIndex: 7,
1034 exp: uint64(uint16(0xff<<8 | 15)),
1035 },
1036 {
1037 name: "i16x8 signed index=0",
1038 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
1039 shape: wazeroir.ShapeI16x8,
1040 signed: true,
1041 laneIndex: 0,
1042 exp: uint64(uint16(0x2<<8 | 0x1)),
1043 },
1044 {
1045 name: "i16x8 signed index=7",
1046 vecBytes: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0xf1},
1047 shape: wazeroir.ShapeI16x8,
1048 signed: true,
1049 laneIndex: 7,
1050 exp: uint64(uint32(0xffff<<16) | uint32(uint16(0xf1<<8|15))),
1051 },
1052 {
1053 name: "i32x4 index=0",
1054 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1055 shape: wazeroir.ShapeI32x4,
1056 laneIndex: 0,
1057 exp: uint64(uint32(0x04_03_02_01)),
1058 },
1059 {
1060 name: "i32x4 index=3",
1061 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1062 shape: wazeroir.ShapeI32x4,
1063 laneIndex: 3,
1064 exp: uint64(uint32(0x16_15_14_13)),
1065 },
1066 {
1067 name: "i64x4 index=0",
1068 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1069 shape: wazeroir.ShapeI64x2,
1070 laneIndex: 0,
1071 exp: uint64(0x08_07_06_05_04_03_02_01),
1072 },
1073 {
1074 name: "i64x4 index=1",
1075 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1076 shape: wazeroir.ShapeI64x2,
1077 laneIndex: 1,
1078 exp: uint64(0x16_15_14_13_12_11_10_09),
1079 },
1080 {
1081 name: "f32x4 index=0",
1082 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1083 shape: wazeroir.ShapeF32x4,
1084 laneIndex: 0,
1085 exp: uint64(uint32(0x04_03_02_01)),
1086 },
1087 {
1088 name: "f32x4 index=3",
1089 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1090 shape: wazeroir.ShapeF32x4,
1091 laneIndex: 3,
1092 exp: uint64(uint32(0x16_15_14_13)),
1093 },
1094 {
1095 name: "f64x4 index=0",
1096 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1097 shape: wazeroir.ShapeF64x2,
1098 laneIndex: 0,
1099 exp: uint64(0x08_07_06_05_04_03_02_01),
1100 },
1101 {
1102 name: "f64x4 index=1",
1103 vecBytes: [16]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16},
1104 shape: wazeroir.ShapeF64x2,
1105 laneIndex: 1,
1106 exp: uint64(0x16_15_14_13_12_11_10_09),
1107 },
1108 }
1109
1110 for _, tc := range tests {
1111 tc := tc
1112 t.Run(tc.name, func(t *testing.T) {
1113 env := newCompilerEnvironment()
1114
1115 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1116 &wazeroir.CompilationResult{HasMemory: true})
1117
1118 err := compiler.compilePreamble()
1119 require.NoError(t, err)
1120
1121 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.vecBytes[:8]), binary.LittleEndian.Uint64(tc.vecBytes[8:]))))
1122 require.NoError(t, err)
1123
1124 err = compiler.compileV128ExtractLane(operationPtr(wazeroir.NewOperationV128ExtractLane(tc.laneIndex, tc.signed, tc.shape)))
1125 require.NoError(t, err)
1126
1127 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler)
1128 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
1129
1130 vt := compiler.runtimeValueLocationStack().peek().valueType
1131 switch tc.shape {
1132 case wazeroir.ShapeI8x16, wazeroir.ShapeI16x8, wazeroir.ShapeI32x4:
1133 require.Equal(t, runtimeValueTypeI32, vt)
1134 case wazeroir.ShapeI64x2:
1135 require.Equal(t, runtimeValueTypeI64, vt)
1136 case wazeroir.ShapeF32x4:
1137 require.Equal(t, runtimeValueTypeF32, vt)
1138 case wazeroir.ShapeF64x2:
1139 require.Equal(t, runtimeValueTypeF64, vt)
1140 }
1141
1142 err = compiler.compileReturnFunction()
1143 require.NoError(t, err)
1144
1145 code := asm.CodeSegment{}
1146 defer func() { require.NoError(t, code.Unmap()) }()
1147
1148
1149 _, err = compiler.compile(code.NextCodeSection())
1150 require.NoError(t, err)
1151 env.exec(code.Bytes())
1152
1153 switch tc.shape {
1154 case wazeroir.ShapeI8x16, wazeroir.ShapeI16x8, wazeroir.ShapeI32x4, wazeroir.ShapeF32x4:
1155 require.Equal(t, uint32(tc.exp), env.stackTopAsUint32())
1156 case wazeroir.ShapeI64x2, wazeroir.ShapeF64x2:
1157 require.Equal(t, tc.exp, env.stackTopAsUint64())
1158 }
1159 })
1160 }
1161 }
1162
1163 func TestCompiler_compileV128ReplaceLane(t *testing.T) {
1164 tests := []struct {
1165 name string
1166 originValueSetupFn func(*testing.T, compilerImpl)
1167 shape wazeroir.Shape
1168 laneIndex byte
1169 exp [16]byte
1170 lo, hi uint64
1171 }{
1172 {
1173 name: "i8x16 index=0",
1174 shape: wazeroir.ShapeI8x16,
1175 laneIndex: 5,
1176 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1177 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xff)))
1178 require.NoError(t, err)
1179 },
1180 exp: [16]byte{5: 0xff},
1181 },
1182 {
1183 name: "i8x16 index=3",
1184 shape: wazeroir.ShapeI8x16,
1185 laneIndex: 5,
1186 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1187 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xff << 8)))
1188 require.NoError(t, err)
1189 },
1190 exp: [16]byte{},
1191 },
1192 {
1193 name: "i8x16 index=5",
1194 shape: wazeroir.ShapeI8x16,
1195 laneIndex: 5,
1196 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1197 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xff)))
1198 require.NoError(t, err)
1199 },
1200 exp: [16]byte{5: 0xff},
1201 },
1202 {
1203 name: "i16x8 index=0",
1204 shape: wazeroir.ShapeI16x8,
1205 laneIndex: 0,
1206 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1207 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xee_ff)))
1208 require.NoError(t, err)
1209 },
1210 exp: [16]byte{0: 0xff, 1: 0xee},
1211 },
1212 {
1213 name: "i16x8 index=3",
1214 shape: wazeroir.ShapeI16x8,
1215 laneIndex: 3,
1216 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1217 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xaa_00)))
1218 require.NoError(t, err)
1219 },
1220 exp: [16]byte{7: 0xaa},
1221 },
1222 {
1223 name: "i16x8 index=7",
1224 shape: wazeroir.ShapeI16x8,
1225 laneIndex: 3,
1226 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1227 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xaa_bb << 16)))
1228 require.NoError(t, err)
1229 },
1230 exp: [16]byte{},
1231 },
1232 {
1233 name: "i32x4 index=0",
1234 shape: wazeroir.ShapeI32x4,
1235 laneIndex: 0,
1236 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1237 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xaa_bb_cc_dd)))
1238 require.NoError(t, err)
1239 },
1240 exp: [16]byte{0: 0xdd, 1: 0xcc, 2: 0xbb, 3: 0xaa},
1241 },
1242 {
1243 name: "i32x4 index=3",
1244 shape: wazeroir.ShapeI32x4,
1245 laneIndex: 3,
1246 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1247 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xaa_bb_cc_dd)))
1248 require.NoError(t, err)
1249 },
1250 exp: [16]byte{12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa},
1251 },
1252 {
1253 name: "i64x2 index=0",
1254 shape: wazeroir.ShapeI64x2,
1255 laneIndex: 0,
1256 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1257 err := c.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(0xaa_bb_cc_dd_01_02_03_04)))
1258 require.NoError(t, err)
1259 },
1260 exp: [16]byte{0: 0x04, 1: 0x03, 2: 0x02, 3: 0x01, 4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa},
1261 },
1262 {
1263 name: "i64x2 index=1",
1264 shape: wazeroir.ShapeI64x2,
1265 laneIndex: 1,
1266 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1267 err := c.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(0xaa_bb_cc_dd_01_02_03_04)))
1268 require.NoError(t, err)
1269 },
1270 exp: [16]byte{8: 0x04, 9: 0x03, 10: 0x02, 11: 0x01, 12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa},
1271 },
1272 {
1273 name: "f32x4 index=0",
1274 shape: wazeroir.ShapeF32x4,
1275 laneIndex: 0,
1276 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1277 err := c.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(0xaa_bb_cc_dd))))
1278 require.NoError(t, err)
1279 },
1280 exp: [16]byte{0: 0xdd, 1: 0xcc, 2: 0xbb, 3: 0xaa},
1281 },
1282 {
1283 name: "f32x4 index=1",
1284 shape: wazeroir.ShapeF32x4,
1285 laneIndex: 1,
1286 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1287 err := c.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(0xaa_bb_cc_dd))))
1288 require.NoError(t, err)
1289 },
1290 exp: [16]byte{4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa},
1291 },
1292 {
1293 name: "f32x4 index=2",
1294 shape: wazeroir.ShapeF32x4,
1295 laneIndex: 2,
1296 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1297 err := c.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(0xaa_bb_cc_dd))))
1298 require.NoError(t, err)
1299 },
1300 exp: [16]byte{8: 0xdd, 9: 0xcc, 10: 0xbb, 11: 0xaa},
1301 },
1302 {
1303 name: "f32x4 index=3",
1304 shape: wazeroir.ShapeF32x4,
1305 laneIndex: 3,
1306 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1307 err := c.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(0xaa_bb_cc_dd))))
1308 require.NoError(t, err)
1309 },
1310 exp: [16]byte{12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa},
1311 },
1312 {
1313 name: "f64x2 index=0",
1314 shape: wazeroir.ShapeF64x2,
1315 laneIndex: 0,
1316 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1317 err := c.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(0xaa_bb_cc_dd_01_02_03_04))))
1318 require.NoError(t, err)
1319 },
1320 exp: [16]byte{0: 0x04, 1: 0x03, 2: 0x02, 3: 0x01, 4: 0xdd, 5: 0xcc, 6: 0xbb, 7: 0xaa},
1321 },
1322 {
1323 name: "f64x2 index=1",
1324 shape: wazeroir.ShapeF64x2,
1325 laneIndex: 1,
1326 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1327 err := c.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(0xaa_bb_cc_dd_01_02_03_04))))
1328 require.NoError(t, err)
1329 },
1330 exp: [16]byte{8: 0x04, 9: 0x03, 10: 0x02, 11: 0x01, 12: 0xdd, 13: 0xcc, 14: 0xbb, 15: 0xaa},
1331 },
1332 {
1333 name: "f64x2 index=0 / lo,hi = 1.0",
1334 shape: wazeroir.ShapeF64x2,
1335 laneIndex: 0,
1336 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1337 err := c.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(0.0))))
1338 require.NoError(t, err)
1339 },
1340 lo: math.Float64bits(1.0),
1341 hi: math.Float64bits(1.0),
1342 exp: [16]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f},
1343 },
1344 {
1345 name: "f64x2 index=1 / lo,hi = 1.0",
1346 shape: wazeroir.ShapeF64x2,
1347 laneIndex: 1,
1348 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1349 err := c.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(0.0))))
1350 require.NoError(t, err)
1351 },
1352 lo: math.Float64bits(1.0),
1353 hi: math.Float64bits(1.0),
1354 exp: [16]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
1355 },
1356 }
1357
1358 for _, tc := range tests {
1359 tc := tc
1360 t.Run(tc.name, func(t *testing.T) {
1361 env := newCompilerEnvironment()
1362 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1363 &wazeroir.CompilationResult{HasMemory: true})
1364
1365 err := compiler.compilePreamble()
1366 require.NoError(t, err)
1367
1368 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(tc.lo, tc.hi)))
1369 require.NoError(t, err)
1370
1371 tc.originValueSetupFn(t, compiler)
1372
1373 err = compiler.compileV128ReplaceLane(operationPtr(wazeroir.NewOperationV128ReplaceLane(tc.laneIndex, tc.shape)))
1374 require.NoError(t, err)
1375
1376 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
1377 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
1378
1379 err = compiler.compileReturnFunction()
1380 require.NoError(t, err)
1381
1382 code := asm.CodeSegment{}
1383 defer func() { require.NoError(t, code.Unmap()) }()
1384
1385
1386 _, err = compiler.compile(code.NextCodeSection())
1387 require.NoError(t, err)
1388 env.exec(code.Bytes())
1389
1390 lo, hi := env.stackTopAsV128()
1391 var actual [16]byte
1392 binary.LittleEndian.PutUint64(actual[:8], lo)
1393 binary.LittleEndian.PutUint64(actual[8:], hi)
1394 require.Equal(t, tc.exp, actual)
1395 })
1396 }
1397 }
1398
1399 func TestCompiler_compileV128Splat(t *testing.T) {
1400 tests := []struct {
1401 name string
1402 originValueSetupFn func(*testing.T, compilerImpl)
1403 shape wazeroir.Shape
1404 exp [16]byte
1405 }{
1406 {
1407 name: "i8x16",
1408 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1409 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0x1)))
1410 require.NoError(t, err)
1411 },
1412 shape: wazeroir.ShapeI8x16,
1413 exp: [16]byte{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1},
1414 },
1415 {
1416 name: "i16x8",
1417 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1418 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xff_11)))
1419 require.NoError(t, err)
1420 },
1421 shape: wazeroir.ShapeI16x8,
1422 exp: [16]byte{0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff, 0x11, 0xff},
1423 },
1424 {
1425 name: "i32x4",
1426 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1427 err := c.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(0xff_11_ee_22)))
1428 require.NoError(t, err)
1429 },
1430 shape: wazeroir.ShapeI32x4,
1431 exp: [16]byte{0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff},
1432 },
1433 {
1434 name: "i64x2",
1435 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1436 err := c.compileConstI64(operationPtr(wazeroir.NewOperationConstI64(0xff_00_ee_00_11_00_22_00)))
1437 require.NoError(t, err)
1438 },
1439 shape: wazeroir.ShapeI64x2,
1440 exp: [16]byte{0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff, 0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff},
1441 },
1442 {
1443 name: "f32x4",
1444 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1445 err := c.compileConstF32(operationPtr(wazeroir.NewOperationConstF32(math.Float32frombits(0xff_11_ee_22))))
1446 require.NoError(t, err)
1447 },
1448 shape: wazeroir.ShapeF32x4,
1449 exp: [16]byte{0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff, 0x22, 0xee, 0x11, 0xff},
1450 },
1451 {
1452 name: "f64x2",
1453 originValueSetupFn: func(t *testing.T, c compilerImpl) {
1454 err := c.compileConstF64(operationPtr(wazeroir.NewOperationConstF64(math.Float64frombits(0xff_00_ee_00_11_00_22_00))))
1455 require.NoError(t, err)
1456 },
1457 shape: wazeroir.ShapeF64x2,
1458 exp: [16]byte{0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff, 0x00, 0x22, 0x00, 0x11, 0x00, 0xee, 0x00, 0xff},
1459 },
1460 }
1461
1462 for _, tc := range tests {
1463 tc := tc
1464 t.Run(tc.name, func(t *testing.T) {
1465 env := newCompilerEnvironment()
1466 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1467 &wazeroir.CompilationResult{HasMemory: true})
1468
1469 err := compiler.compilePreamble()
1470 require.NoError(t, err)
1471
1472 tc.originValueSetupFn(t, compiler)
1473
1474 err = compiler.compileV128Splat(operationPtr(wazeroir.NewOperationV128Splat(tc.shape)))
1475 require.NoError(t, err)
1476
1477 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
1478 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
1479
1480 err = compiler.compileReturnFunction()
1481 require.NoError(t, err)
1482
1483 code := asm.CodeSegment{}
1484 defer func() { require.NoError(t, code.Unmap()) }()
1485
1486
1487 _, err = compiler.compile(code.NextCodeSection())
1488 require.NoError(t, err)
1489 env.exec(code.Bytes())
1490
1491 lo, hi := env.stackTopAsV128()
1492 var actual [16]byte
1493 binary.LittleEndian.PutUint64(actual[:8], lo)
1494 binary.LittleEndian.PutUint64(actual[8:], hi)
1495 require.Equal(t, tc.exp, actual)
1496 })
1497 }
1498 }
1499
1500 func TestCompiler_compileV128AnyTrue(t *testing.T) {
1501 tests := []struct {
1502 name string
1503 lo, hi uint64
1504 exp uint32
1505 }{
1506 {name: "lo == 0 && hi == 0", lo: 0, hi: 0, exp: 0},
1507 {name: "lo != 0", lo: 1, exp: 1},
1508 {name: "hi != 0", hi: 1, exp: 1},
1509 {name: "lo != 0 && hi != 0", lo: 1, hi: 1, exp: 1},
1510 }
1511
1512 for _, tc := range tests {
1513 tc := tc
1514 t.Run(tc.name, func(t *testing.T) {
1515 env := newCompilerEnvironment()
1516 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1517 &wazeroir.CompilationResult{HasMemory: true})
1518
1519 err := compiler.compilePreamble()
1520 require.NoError(t, err)
1521
1522 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(tc.lo, tc.hi)))
1523 require.NoError(t, err)
1524
1525 err = compiler.compileV128AnyTrue(operationPtr(wazeroir.NewOperationV128AnyTrue()))
1526 require.NoError(t, err)
1527
1528 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler)
1529
1530 err = compiler.compileReturnFunction()
1531 require.NoError(t, err)
1532
1533 code := asm.CodeSegment{}
1534 defer func() { require.NoError(t, code.Unmap()) }()
1535
1536
1537 _, err = compiler.compile(code.NextCodeSection())
1538 require.NoError(t, err)
1539 env.exec(code.Bytes())
1540
1541 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
1542 require.Equal(t, uint64(1), env.stackPointer())
1543 require.Equal(t, tc.exp, env.stackTopAsUint32())
1544 })
1545 }
1546 }
1547
1548 func TestCompiler_compileV128AllTrue(t *testing.T) {
1549 tests := []struct {
1550 name string
1551 shape wazeroir.Shape
1552 lo, hi uint64
1553 exp uint32
1554 }{
1555 {
1556 name: "i8x16 - true",
1557 shape: wazeroir.ShapeI8x16,
1558 lo: 0xffff_ffff_ffff_ffff,
1559 hi: 0x0101_0101_0101_0101,
1560 exp: 1,
1561 },
1562 {
1563 name: "i8x16 - false on lo",
1564 shape: wazeroir.ShapeI8x16,
1565 lo: 0xffff_ffff_ffff_ffff,
1566 hi: 0x1111_1111_0011_1111,
1567 exp: 0,
1568 },
1569 {
1570 name: "i8x16 - false on hi",
1571 shape: wazeroir.ShapeI8x16,
1572 lo: 0xffff_00ff_ffff_ffff,
1573 hi: 0x1111_1111_1111_1111,
1574 exp: 0,
1575 },
1576 {
1577 name: "i16x8 - true",
1578 shape: wazeroir.ShapeI16x8,
1579 lo: 0x1000_0100_0010_0001,
1580 hi: 0x0101_0101_0101_0101,
1581 exp: 1,
1582 },
1583 {
1584 name: "i16x8 - false on hi",
1585 shape: wazeroir.ShapeI16x8,
1586 lo: 0x1000_0100_0010_0001,
1587 hi: 0x1111_1111_0000_1111,
1588 exp: 0,
1589 },
1590 {
1591 name: "i16x8 - false on lo",
1592 shape: wazeroir.ShapeI16x8,
1593 lo: 0xffff_0000_ffff_ffff,
1594 hi: 0x1111_1111_1111_1111,
1595 exp: 0,
1596 },
1597 {
1598 name: "i32x4 - true",
1599 shape: wazeroir.ShapeI32x4,
1600 lo: 0x1000_0000_0010_0000,
1601 hi: 0x0000_0001_0000_1000,
1602 exp: 1,
1603 },
1604 {
1605 name: "i32x4 - true",
1606 shape: wazeroir.ShapeI32x4,
1607 lo: 0x0000_1111_1111_0000,
1608 hi: 0x0000_0001_1000_0000,
1609 exp: 1,
1610 },
1611 {
1612 name: "i32x4 - false on lo",
1613 shape: wazeroir.ShapeI32x4,
1614 lo: 0x1111_1111_0000_0000,
1615 hi: 0x1111_1111_1111_1111,
1616 exp: 0,
1617 },
1618 {
1619 name: "i32x4 - false on lo",
1620 shape: wazeroir.ShapeI32x4,
1621 lo: 0x0000_0000_1111_1111,
1622 hi: 0x1111_1111_1111_1111,
1623 exp: 0,
1624 },
1625 {
1626 name: "i32x4 - false on hi",
1627 shape: wazeroir.ShapeI32x4,
1628 lo: 0x1111_1111_1111_1111,
1629 hi: 0x1111_1111_0000_0000,
1630 exp: 0,
1631 },
1632 {
1633 name: "i32x4 - false on hi",
1634 shape: wazeroir.ShapeI32x4,
1635 lo: 0x1111_1111_1111_1111,
1636 hi: 0x0000_0000_1111_1111,
1637 exp: 0,
1638 },
1639
1640 {
1641 name: "i64x2 - true",
1642 shape: wazeroir.ShapeI64x2,
1643 lo: 0x1000_0000_0000_0000,
1644 hi: 0x0000_0001_0000_0000,
1645 exp: 1,
1646 },
1647 {
1648 name: "i64x2 - true",
1649 shape: wazeroir.ShapeI64x2,
1650 lo: 0x0000_0000_0010_0000,
1651 hi: 0x0000_0000_0000_0100,
1652 exp: 1,
1653 },
1654 {
1655 name: "i64x2 - true",
1656 shape: wazeroir.ShapeI64x2,
1657 lo: 0x0000_0000_0000_1000,
1658 hi: 0x1000_0000_0000_0000,
1659 exp: 1,
1660 },
1661 {
1662 name: "i64x2 - false on lo",
1663 shape: wazeroir.ShapeI64x2,
1664 lo: 0,
1665 hi: 0x1111_1111_1111_1111,
1666 exp: 0,
1667 },
1668 {
1669 name: "i64x2 - false on hi",
1670 shape: wazeroir.ShapeI64x2,
1671 lo: 0x1111_1111_1111_1111,
1672 hi: 0,
1673 exp: 0,
1674 },
1675 }
1676
1677 for _, tc := range tests {
1678 tc := tc
1679 t.Run(tc.name, func(t *testing.T) {
1680 env := newCompilerEnvironment()
1681 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1682 &wazeroir.CompilationResult{HasMemory: true})
1683
1684 err := compiler.compilePreamble()
1685 require.NoError(t, err)
1686
1687 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(tc.lo, tc.hi)))
1688 require.NoError(t, err)
1689
1690 err = compiler.compileV128AllTrue(operationPtr(wazeroir.NewOperationV128AllTrue(tc.shape)))
1691 require.NoError(t, err)
1692
1693 require.Equal(t, 0, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
1694
1695 err = compiler.compileReturnFunction()
1696 require.NoError(t, err)
1697
1698 code := asm.CodeSegment{}
1699 defer func() { require.NoError(t, code.Unmap()) }()
1700
1701
1702 _, err = compiler.compile(code.NextCodeSection())
1703 require.NoError(t, err)
1704 env.exec(code.Bytes())
1705
1706 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
1707 require.Equal(t, uint64(1), env.stackPointer())
1708 require.Equal(t, tc.exp, env.stackTopAsUint32())
1709 })
1710 }
1711 }
1712
1713 func i8ToU8(v int8) byte {
1714 return byte(v)
1715 }
1716
1717 func i16ToU16(v int16) uint16 {
1718 return uint16(v)
1719 }
1720
1721 func i32ToU32(v int32) uint32 {
1722 return uint32(v)
1723 }
1724
1725 func i64ToU64(v int64) uint64 {
1726 return uint64(v)
1727 }
1728
1729 func TestCompiler_compileV128Swizzle(t *testing.T) {
1730 tests := []struct {
1731 name string
1732 indexVec, baseVec [16]byte
1733 expVec [16]byte
1734 }{
1735 {
1736 name: "1",
1737 baseVec: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
1738 indexVec: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
1739 expVec: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
1740 },
1741 {
1742 name: "2",
1743 baseVec: [16]byte{
1744 i8ToU8(-16), i8ToU8(-15), i8ToU8(-14), i8ToU8(-13), i8ToU8(-12),
1745 i8ToU8(-11), i8ToU8(-10), i8ToU8(-9), i8ToU8(-8), i8ToU8(-7), i8ToU8(-6), i8ToU8(-5),
1746 i8ToU8(-4), i8ToU8(-3), i8ToU8(-2), i8ToU8(-1),
1747 },
1748 indexVec: [16]byte{
1749 i8ToU8(-8), i8ToU8(-7), i8ToU8(-6), i8ToU8(-5), i8ToU8(-4),
1750 i8ToU8(-3), i8ToU8(-2), i8ToU8(-1), 16, 17, 18, 19, 20, 21, 22, 23,
1751 },
1752 expVec: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
1753 },
1754 {
1755 name: "3",
1756 baseVec: [16]byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115},
1757 indexVec: [16]byte{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
1758 expVec: [16]byte{115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100},
1759 },
1760 {
1761 name: "4",
1762 baseVec: [16]byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115},
1763 indexVec: [16]byte{
1764 9, 16, 10, 17, 11, 18, 12, 19, 13, 20, 14, 21, 15, 22, 16, 23,
1765 },
1766 expVec: [16]byte{109, 0, 110, 0, 111, 0, 112, 0, 113, 0, 114, 0, 115, 0, 0, 0},
1767 },
1768 {
1769 name: "5",
1770 baseVec: [16]byte{0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73},
1771 indexVec: [16]byte{9, 16, 10, 17, 11, 18, 12, 19, 13, 20, 14, 21, 15, 22, 16, 23},
1772 expVec: [16]byte{0x6d, 0, 0x6e, 0, 0x6f, 0, 0x70, 0, 0x71, 0, 0x72, 0, 0x73, 0, 0, 0},
1773 },
1774 }
1775
1776 for _, tc := range tests {
1777 tc := tc
1778 t.Run(tc.name, func(t *testing.T) {
1779 env := newCompilerEnvironment()
1780 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1781 &wazeroir.CompilationResult{HasMemory: true})
1782
1783 err := compiler.compilePreamble()
1784 require.NoError(t, err)
1785
1786 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.baseVec[:8]), binary.LittleEndian.Uint64(tc.baseVec[8:]))))
1787 require.NoError(t, err)
1788
1789 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.indexVec[:8]), binary.LittleEndian.Uint64(tc.indexVec[8:]))))
1790 require.NoError(t, err)
1791
1792 err = compiler.compileV128Swizzle(operationPtr(wazeroir.NewOperationV128Swizzle()))
1793 require.NoError(t, err)
1794
1795 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
1796 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
1797
1798 err = compiler.compileReturnFunction()
1799 require.NoError(t, err)
1800
1801 code := asm.CodeSegment{}
1802 defer func() { require.NoError(t, code.Unmap()) }()
1803
1804
1805 _, err = compiler.compile(code.NextCodeSection())
1806 require.NoError(t, err)
1807 env.exec(code.Bytes())
1808
1809 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
1810
1811 lo, hi := env.stackTopAsV128()
1812 var actual [16]byte
1813 binary.LittleEndian.PutUint64(actual[:8], lo)
1814 binary.LittleEndian.PutUint64(actual[8:], hi)
1815 require.Equal(t, tc.expVec, actual)
1816 })
1817 }
1818 }
1819
1820 func TestCompiler_compileV128Shuffle(t *testing.T) {
1821 tests := []struct {
1822 name string
1823 lanes []uint64
1824 w, v, exp [16]byte
1825 }{
1826 {
1827 name: "v only",
1828 lanes: []uint64{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0},
1829 v: [16]byte{0: 0xa, 1: 0xb, 10: 0xc},
1830 w: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1831 exp: [16]byte{
1832 0xb, 0xb, 0xb, 0xb,
1833 0xa, 0xa, 0xa, 0xa,
1834 0xc, 0xc, 0xc, 0xc,
1835 0xa, 0xa, 0xa, 0xa,
1836 },
1837 },
1838 {
1839 name: "w only",
1840 lanes: []uint64{17, 17, 17, 17, 16, 16, 16, 16, 26, 26, 26, 26, 16, 16, 16, 16},
1841 v: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
1842 w: [16]byte{0: 0xa, 1: 0xb, 10: 0xc},
1843 exp: [16]byte{
1844 0xb, 0xb, 0xb, 0xb,
1845 0xa, 0xa, 0xa, 0xa,
1846 0xc, 0xc, 0xc, 0xc,
1847 0xa, 0xa, 0xa, 0xa,
1848 },
1849 },
1850 {
1851 name: "mix",
1852 lanes: []uint64{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31},
1853 v: [16]byte{
1854 0x1, 0xff, 0x2, 0xff, 0x3, 0xff, 0x4, 0xff,
1855 0x5, 0xff, 0x6, 0xff, 0x7, 0xff, 0x8, 0xff,
1856 },
1857 w: [16]byte{
1858 0xff, 0x11, 0xff, 0x12, 0xff, 0x13, 0xff, 0x14,
1859 0xff, 0x15, 0xff, 0x16, 0xff, 0x17, 0xff, 0x18,
1860 },
1861 exp: [16]byte{
1862 0x1, 0x11, 0x2, 0x12, 0x3, 0x13, 0x4, 0x14,
1863 0x5, 0x15, 0x6, 0x16, 0x7, 0x17, 0x8, 0x18,
1864 },
1865 },
1866 }
1867
1868 for _, tc := range tests {
1869 tc := tc
1870 t.Run(tc.name, func(t *testing.T) {
1871 env := newCompilerEnvironment()
1872 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
1873 &wazeroir.CompilationResult{HasMemory: true})
1874
1875 err := compiler.compilePreamble()
1876 require.NoError(t, err)
1877
1878 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
1879 require.NoError(t, err)
1880
1881 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.w[:8]), binary.LittleEndian.Uint64(tc.w[8:]))))
1882 require.NoError(t, err)
1883
1884 err = compiler.compileV128Shuffle(operationPtr(wazeroir.NewOperationV128Shuffle(tc.lanes)))
1885 require.NoError(t, err)
1886
1887 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
1888 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
1889
1890 err = compiler.compileReturnFunction()
1891 require.NoError(t, err)
1892
1893 code := asm.CodeSegment{}
1894 defer func() { require.NoError(t, code.Unmap()) }()
1895
1896
1897 _, err = compiler.compile(code.NextCodeSection())
1898 require.NoError(t, err)
1899 env.exec(code.Bytes())
1900
1901 lo, hi := env.stackTopAsV128()
1902 var actual [16]byte
1903 binary.LittleEndian.PutUint64(actual[:8], lo)
1904 binary.LittleEndian.PutUint64(actual[8:], hi)
1905 require.Equal(t, tc.exp, actual)
1906 })
1907 }
1908 }
1909
1910 func TestCompiler_compileV128Bitmask(t *testing.T) {
1911 u16x8 := func(u1, u2, u3, u4, u5, u6, u7, u8 uint16) (ret [16]byte) {
1912 binary.LittleEndian.PutUint16(ret[0:], u1)
1913 binary.LittleEndian.PutUint16(ret[2:], u2)
1914 binary.LittleEndian.PutUint16(ret[4:], u3)
1915 binary.LittleEndian.PutUint16(ret[6:], u4)
1916 binary.LittleEndian.PutUint16(ret[8:], u5)
1917 binary.LittleEndian.PutUint16(ret[10:], u6)
1918 binary.LittleEndian.PutUint16(ret[12:], u7)
1919 binary.LittleEndian.PutUint16(ret[14:], u8)
1920 return
1921 }
1922 u32x4 := func(u1, u2, u3, u4 uint32) (ret [16]byte) {
1923 binary.LittleEndian.PutUint32(ret[0:], u1)
1924 binary.LittleEndian.PutUint32(ret[4:], u2)
1925 binary.LittleEndian.PutUint32(ret[8:], u3)
1926 binary.LittleEndian.PutUint32(ret[12:], u4)
1927 return
1928 }
1929 u64x2 := func(u1, u2 uint64) (ret [16]byte) {
1930 binary.LittleEndian.PutUint64(ret[0:], u1)
1931 binary.LittleEndian.PutUint64(ret[8:], u2)
1932 return
1933 }
1934
1935 tests := []struct {
1936 name string
1937 shape wazeroir.Shape
1938 v [16]byte
1939 exp uint32
1940 }{
1941 {
1942 name: wasm.OpcodeVecI8x16BitMaskName,
1943 v: [16]byte{
1944 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1,
1945 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1,
1946 },
1947 shape: wazeroir.ShapeI8x16,
1948 exp: 0b0101_0101_0101_0101,
1949 },
1950 {
1951 name: wasm.OpcodeVecI8x16BitMaskName,
1952 v: [16]byte{
1953 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1,
1954 0, 0, 0, 0, 0, 0, 0, 0,
1955 },
1956 shape: wazeroir.ShapeI8x16,
1957 exp: 0b0000_0000_0101_0101,
1958 },
1959 {
1960 name: wasm.OpcodeVecI8x16BitMaskName,
1961 v: [16]byte{
1962 0, 0, 0, 0, 0, 0, 0, 0,
1963 i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1, i8ToU8(-1), 1,
1964 },
1965 shape: wazeroir.ShapeI8x16,
1966 exp: 0b0101_0101_0000_0000,
1967 },
1968 {
1969 name: wasm.OpcodeVecI16x8BitMaskName,
1970 v: u16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
1971 shape: wazeroir.ShapeI16x8,
1972 exp: 0b1111_1111,
1973 },
1974 {
1975 name: wasm.OpcodeVecI16x8BitMaskName,
1976 v: u16x8(0, 0xffff, 0, 0xffff, 0, 0xffff, 0, 0xffff),
1977 shape: wazeroir.ShapeI16x8,
1978 exp: 0b1010_1010,
1979 },
1980 {
1981 name: wasm.OpcodeVecI32x4BitMaskName,
1982 v: u32x4(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff),
1983 shape: wazeroir.ShapeI32x4,
1984 exp: 0b1111,
1985 },
1986 {
1987 name: wasm.OpcodeVecI32x4BitMaskName,
1988 v: u32x4(0, 0xffffffff, 0xffffffff, 0),
1989 shape: wazeroir.ShapeI32x4,
1990 exp: 0b0110,
1991 },
1992 {
1993 name: wasm.OpcodeVecI64x2BitMaskName,
1994 v: u64x2(0, 0xffffffffffffffff),
1995 shape: wazeroir.ShapeI64x2,
1996 exp: 0b10,
1997 },
1998 {
1999 name: wasm.OpcodeVecI64x2BitMaskName,
2000 v: u64x2(0xffffffffffffffff, 0xffffffffffffffff),
2001 shape: wazeroir.ShapeI64x2,
2002 exp: 0b11,
2003 },
2004 }
2005
2006 for _, tc := range tests {
2007 tc := tc
2008 t.Run(tc.name, func(t *testing.T) {
2009 env := newCompilerEnvironment()
2010 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
2011 &wazeroir.CompilationResult{HasMemory: true})
2012
2013 err := compiler.compilePreamble()
2014 require.NoError(t, err)
2015
2016 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
2017 require.NoError(t, err)
2018
2019 err = compiler.compileV128BitMask(operationPtr(wazeroir.NewOperationV128BitMask(tc.shape)))
2020 require.NoError(t, err)
2021
2022 requireRuntimeLocationStackPointerEqual(t, uint64(1), compiler)
2023 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
2024
2025 err = compiler.compileReturnFunction()
2026 require.NoError(t, err)
2027
2028 code := asm.CodeSegment{}
2029 defer func() { require.NoError(t, code.Unmap()) }()
2030
2031
2032 _, err = compiler.compile(code.NextCodeSection())
2033 require.NoError(t, err)
2034 env.exec(code.Bytes())
2035
2036 actual := env.stackTopAsUint32()
2037 require.Equal(t, tc.exp, actual)
2038 })
2039 }
2040 }
2041
2042 func TestCompiler_compileV128_Not(t *testing.T) {
2043 env := newCompilerEnvironment()
2044 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
2045 &wazeroir.CompilationResult{HasMemory: true})
2046
2047 err := compiler.compilePreamble()
2048 require.NoError(t, err)
2049
2050 var originalLo, originalHi uint64 = 0xffff_0000_ffff_0000, 0x0000_ffff_0000_ffff
2051
2052 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(originalLo, originalHi)))
2053 require.NoError(t, err)
2054
2055 err = compiler.compileV128Not(operationPtr(wazeroir.NewOperationV128Not()))
2056 require.NoError(t, err)
2057
2058 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
2059 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
2060
2061 err = compiler.compileReturnFunction()
2062 require.NoError(t, err)
2063
2064 code := asm.CodeSegment{}
2065 defer func() { require.NoError(t, code.Unmap()) }()
2066
2067
2068 _, err = compiler.compile(code.NextCodeSection())
2069 require.NoError(t, err)
2070 env.exec(code.Bytes())
2071
2072 lo, hi := env.stackTopAsV128()
2073 require.Equal(t, ^originalLo, lo)
2074 require.Equal(t, ^originalHi, hi)
2075 }
2076
2077 func TestCompiler_compileV128_And_Or_Xor_AndNot(t *testing.T) {
2078 tests := []struct {
2079 name string
2080 op wazeroir.OperationKind
2081 x1, x2, exp [16]byte
2082 }{
2083 {
2084 name: "AND",
2085 op: wazeroir.OperationKindV128And,
2086 x1: [16]byte{
2087 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2088 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2089 },
2090 x2: [16]byte{},
2091 exp: [16]byte{},
2092 },
2093 {
2094 name: "AND",
2095 op: wazeroir.OperationKindV128And,
2096 x2: [16]byte{
2097 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2098 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2099 },
2100 x1: [16]byte{},
2101 exp: [16]byte{},
2102 },
2103 {
2104 name: "AND",
2105 op: wazeroir.OperationKindV128And,
2106 x2: [16]byte{
2107 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2108 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2109 },
2110 x1: [16]byte{0: 0x1, 5: 0x1, 15: 0x1},
2111 exp: [16]byte{0: 0x1, 5: 0x1, 15: 0x1},
2112 },
2113 {
2114 name: "OR",
2115 op: wazeroir.OperationKindV128Or,
2116 x1: [16]byte{
2117 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2118 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2119 },
2120 x2: [16]byte{},
2121 exp: [16]byte{
2122 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2123 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2124 },
2125 },
2126 {
2127 name: "OR",
2128 op: wazeroir.OperationKindV128Or,
2129 x2: [16]byte{
2130 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2131 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2132 },
2133 x1: [16]byte{},
2134 exp: [16]byte{
2135 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2136 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2137 },
2138 },
2139 {
2140 name: "OR",
2141 op: wazeroir.OperationKindV128Or,
2142 x2: [16]byte{},
2143 x1: [16]byte{0: 0x1, 5: 0x1, 15: 0x1},
2144 exp: [16]byte{0: 0x1, 5: 0x1, 15: 0x1},
2145 },
2146 {
2147 name: "OR",
2148 op: wazeroir.OperationKindV128Or,
2149 x2: [16]byte{8: 0x1, 10: 0x1},
2150 x1: [16]byte{0: 0x1},
2151 exp: [16]byte{0: 0x1, 8: 0x1, 10: 0x1},
2152 },
2153 {
2154 name: "XOR",
2155 op: wazeroir.OperationKindV128Xor,
2156 x1: [16]byte{
2157 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2158 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2159 },
2160 x2: [16]byte{},
2161 exp: [16]byte{
2162 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2163 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2164 },
2165 },
2166 {
2167 name: "XOR",
2168 op: wazeroir.OperationKindV128Xor,
2169 x2: [16]byte{
2170 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2171 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2172 },
2173 x1: [16]byte{},
2174 exp: [16]byte{
2175 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2176 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2177 },
2178 },
2179 {
2180 name: "XOR",
2181 op: wazeroir.OperationKindV128Xor,
2182 x2: [16]byte{
2183 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2184 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2185 },
2186 x1: [16]byte{
2187 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2188 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2189 },
2190 exp: [16]byte{},
2191 },
2192 {
2193 name: "XOR",
2194 op: wazeroir.OperationKindV128Xor,
2195 x2: [16]byte{
2196 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2197 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2198 },
2199 x1: [16]byte{0: 0x1, 15: 0x2},
2200 exp: [16]byte{
2201 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2202 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd,
2203 },
2204 },
2205
2206 {
2207 name: "AndNot",
2208 op: wazeroir.OperationKindV128AndNot,
2209 x2: [16]byte{},
2210 x1: [16]byte{
2211 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2212 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2213 },
2214 exp: [16]byte{
2215 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2216 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2217 },
2218 },
2219 {
2220 name: "AndNot",
2221 op: wazeroir.OperationKindV128AndNot,
2222 x2: [16]byte{
2223 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2224 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2225 },
2226 x1: [16]byte{},
2227 exp: [16]byte{},
2228 },
2229 {
2230 name: "AndNot",
2231 op: wazeroir.OperationKindV128AndNot,
2232 x2: [16]byte{
2233 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2234 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2235 },
2236 x1: [16]byte{
2237 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2238 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2239 },
2240 exp: [16]byte{},
2241 },
2242 {
2243 name: "AndNot",
2244 op: wazeroir.OperationKindV128AndNot,
2245 x2: [16]byte{
2246 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2247 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd,
2248 },
2249 x1: [16]byte{0: 0x1, 15: 0x2},
2250 exp: [16]byte{0: 0x1, 15: 0x2},
2251 },
2252 }
2253
2254 for _, tc := range tests {
2255 tc := tc
2256 t.Run(tc.name, func(t *testing.T) {
2257 env := newCompilerEnvironment()
2258 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
2259 &wazeroir.CompilationResult{HasMemory: true})
2260
2261 err := compiler.compilePreamble()
2262 require.NoError(t, err)
2263
2264 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
2265 require.NoError(t, err)
2266
2267 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
2268 require.NoError(t, err)
2269
2270 switch tc.op {
2271 case wazeroir.OperationKindV128And:
2272 err = compiler.compileV128And(operationPtr(wazeroir.NewOperationV128And()))
2273 case wazeroir.OperationKindV128Or:
2274 err = compiler.compileV128Or(operationPtr(wazeroir.NewOperationV128Or()))
2275 case wazeroir.OperationKindV128Xor:
2276 err = compiler.compileV128Xor(operationPtr(wazeroir.NewOperationV128Xor()))
2277 case wazeroir.OperationKindV128AndNot:
2278 err = compiler.compileV128AndNot(operationPtr(wazeroir.NewOperationV128AndNot()))
2279 }
2280 require.NoError(t, err)
2281
2282 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
2283 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
2284
2285 err = compiler.compileReturnFunction()
2286 require.NoError(t, err)
2287
2288 code := asm.CodeSegment{}
2289 defer func() { require.NoError(t, code.Unmap()) }()
2290
2291
2292 _, err = compiler.compile(code.NextCodeSection())
2293 require.NoError(t, err)
2294 env.exec(code.Bytes())
2295
2296 lo, hi := env.stackTopAsV128()
2297 var actual [16]byte
2298 binary.LittleEndian.PutUint64(actual[:8], lo)
2299 binary.LittleEndian.PutUint64(actual[8:], hi)
2300 require.Equal(t, tc.exp, actual)
2301 })
2302 }
2303 }
2304
2305 func TestCompiler_compileV128Bitselect(t *testing.T) {
2306 tests := []struct {
2307 name string
2308 selector, x1, x2, exp [16]byte
2309 }{
2310 {
2311 name: "all x1",
2312 selector: [16]byte{
2313 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2314 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2315 },
2316 x1: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
2317 x2: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
2318 exp: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
2319 },
2320 {
2321 name: "all x2",
2322 selector: [16]byte{},
2323 x1: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
2324 x2: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
2325 exp: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
2326 },
2327 {
2328 name: "mix",
2329 selector: [16]byte{
2330 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000, 0b1111_0000,
2331 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111,
2332 },
2333 x1: [16]byte{
2334 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010,
2335 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010,
2336 },
2337 x2: [16]byte{
2338 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101,
2339 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101,
2340 },
2341 exp: [16]byte{
2342 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101, 0b1010_0101,
2343 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b0101_0101, 0b1010_1010, 0b1010_1010, 0b1010_1010, 0b1010_1010,
2344 },
2345 },
2346 }
2347
2348 for _, tc := range tests {
2349 tc := tc
2350 t.Run(tc.name, func(t *testing.T) {
2351 env := newCompilerEnvironment()
2352 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
2353 &wazeroir.CompilationResult{HasMemory: true})
2354
2355 err := compiler.compilePreamble()
2356 require.NoError(t, err)
2357
2358 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
2359 require.NoError(t, err)
2360
2361 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
2362 require.NoError(t, err)
2363
2364 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.selector[:8]), binary.LittleEndian.Uint64(tc.selector[8:]))))
2365 require.NoError(t, err)
2366
2367 err = compiler.compileV128Bitselect(operationPtr(wazeroir.NewOperationV128Bitselect()))
2368 require.NoError(t, err)
2369
2370 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
2371 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
2372
2373 err = compiler.compileReturnFunction()
2374 require.NoError(t, err)
2375
2376 code := asm.CodeSegment{}
2377 defer func() { require.NoError(t, code.Unmap()) }()
2378
2379
2380 _, err = compiler.compile(code.NextCodeSection())
2381 require.NoError(t, err)
2382 env.exec(code.Bytes())
2383
2384 lo, hi := env.stackTopAsV128()
2385 var actual [16]byte
2386 binary.LittleEndian.PutUint64(actual[:8], lo)
2387 binary.LittleEndian.PutUint64(actual[8:], hi)
2388 require.Equal(t, tc.exp, actual)
2389 })
2390 }
2391 }
2392
2393 func TestCompiler_compileV128Shl(t *testing.T) {
2394 tests := []struct {
2395 name string
2396 shape wazeroir.Shape
2397 s uint32
2398 x, exp [16]byte
2399 }{
2400 {
2401 name: "i8x16/shift=0",
2402 shape: wazeroir.ShapeI8x16,
2403 x: [16]byte{
2404 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2405 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2406 },
2407 exp: [16]byte{
2408 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2409 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2410 },
2411 s: 0,
2412 },
2413 {
2414 name: "i8x16/shift=1",
2415 shape: wazeroir.ShapeI8x16,
2416 x: [16]byte{
2417 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff,
2418 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff,
2419 },
2420 exp: [16]byte{
2421 2, 0xfe, 2, 0xfe, 2, 0xfe, 2, 0xfe,
2422 2, 0xfe, 2, 0xfe, 2, 0xfe, 2, 0xfe,
2423 },
2424 s: 1,
2425 },
2426 {
2427 name: "i8x16/shift=2",
2428 shape: wazeroir.ShapeI8x16,
2429 x: [16]byte{
2430 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2431 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2432 },
2433 exp: [16]byte{
2434 4, 0, 4, 0, 4, 0, 4, 0,
2435 4, 0, 4, 0, 4, 0, 4, 0,
2436 },
2437 s: 2,
2438 },
2439 {
2440 name: "i8x16/shift=3",
2441 shape: wazeroir.ShapeI8x16,
2442 x: [16]byte{
2443 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff,
2444 1, 0xff, 1, 0xff, 1, 0xff, 1, 0xff,
2445 },
2446 exp: [16]byte{
2447 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111,
2448 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111, 8, 0xff & ^0b111,
2449 },
2450 s: 3,
2451 },
2452 {
2453 name: "i8x16/shift=4",
2454 shape: wazeroir.ShapeI8x16,
2455 x: [16]byte{
2456 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1,
2457 0xff, 1, 0xff, 1, 0xff, 1, 0xff, 1,
2458 },
2459 exp: [16]byte{
2460 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16,
2461 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16, 0xff & ^0b1111, 16,
2462 },
2463 s: 4,
2464 },
2465 {
2466 name: "i8x16/shift=5",
2467 shape: wazeroir.ShapeI8x16,
2468 x: [16]byte{
2469 0xff, 0xff, 0xff, 0xff, 1, 1, 1, 1,
2470 0xff, 0xff, 0xff, 0xff, 1, 1, 1, 1,
2471 },
2472 exp: [16]byte{
2473 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 32, 32, 32, 32,
2474 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 0xff & ^0b11111, 32, 32, 32, 32,
2475 },
2476 s: 5,
2477 },
2478 {
2479 name: "i8x16/shift=6",
2480 shape: wazeroir.ShapeI8x16,
2481 x: [16]byte{
2482 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81,
2483 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81,
2484 },
2485 exp: [16]byte{
2486 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6,
2487 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6, 0xc0, 1 << 6,
2488 },
2489 s: 6,
2490 },
2491 {
2492 name: "i8x16/shift=7",
2493 shape: wazeroir.ShapeI8x16,
2494 x: [16]byte{
2495 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2496 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2497 },
2498 exp: [16]byte{
2499 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
2500 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
2501 },
2502 s: 7,
2503 },
2504 {
2505 name: "i16x8/shift=0",
2506 shape: wazeroir.ShapeI16x8,
2507 x: [16]byte{
2508 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2509 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2510 },
2511 exp: [16]byte{
2512 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2513 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2514 },
2515 s: 0,
2516 },
2517 {
2518 name: "i16x8/shift=1",
2519 shape: wazeroir.ShapeI16x8,
2520 x: [16]byte{
2521 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2522 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2523 },
2524 exp: [16]byte{
2525 2, 0, 2, 0, 2, 0, 2, 0,
2526 2, 0, 2, 0, 2, 0, 2, 0,
2527 },
2528 s: 1,
2529 },
2530 {
2531 name: "i16x8/shift=7",
2532 shape: wazeroir.ShapeI16x8,
2533 x: [16]byte{
2534 1, 1, 1, 1, 0x80, 0x80, 0x80, 0x80,
2535 0, 0x80, 0, 0x80, 0b11, 0b11, 0b11, 0b11,
2536 },
2537 exp: [16]byte{
2538 0, 1, 0, 1, 0, 0x80, 0, 0x80,
2539 0, 0, 0, 0, 0, 0b11, 0, 0b11,
2540 },
2541 s: 8,
2542 },
2543 {
2544 name: "i16x8/shift=15",
2545 shape: wazeroir.ShapeI16x8,
2546 x: [16]byte{
2547 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2548 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2549 },
2550 exp: [16]byte{
2551 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80,
2552 0, 0x80, 0, 0x80, 0, 0x80, 0, 0x80,
2553 },
2554 s: 15,
2555 },
2556 {
2557 name: "i32x4/shift=0",
2558 shape: wazeroir.ShapeI32x4,
2559 x: [16]byte{
2560 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2561 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2562 },
2563 exp: [16]byte{
2564 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2565 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2566 },
2567 s: 0,
2568 },
2569 {
2570 name: "i32x4/shift=1",
2571 shape: wazeroir.ShapeI32x4,
2572 x: [16]byte{
2573 1, 0x80, 0, 0x80, 1, 0x80, 0, 0x80,
2574 1, 0x80, 0, 0x80, 1, 0x80, 0, 0x80,
2575 },
2576 exp: [16]byte{
2577 2, 0, 1, 0, 2, 0, 1, 0,
2578 2, 0, 1, 0, 2, 0, 1, 0,
2579 },
2580 s: 1,
2581 },
2582 {
2583 name: "i32x4/shift=31",
2584 shape: wazeroir.ShapeI32x4,
2585 x: [16]byte{
2586 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2587 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2588 },
2589 exp: [16]byte{
2590 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2591 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2592 },
2593 s: 31,
2594 },
2595 {
2596 name: "i64x2/shift=0",
2597 shape: wazeroir.ShapeI64x2,
2598 x: [16]byte{
2599 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2600 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2601 },
2602 exp: [16]byte{
2603 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2604 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2605 },
2606 s: 0,
2607 },
2608 {
2609 name: "i64x2/shift=5",
2610 shape: wazeroir.ShapeI64x2,
2611 x: [16]byte{
2612 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2613 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2614 },
2615 exp: [16]byte{
2616 1 << 5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0,
2617 1 << 5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0, 1<<4 | 1<<5, 0,
2618 },
2619 s: 5,
2620 },
2621 {
2622 name: "i64x2/shift=63",
2623 shape: wazeroir.ShapeI64x2,
2624 x: [16]byte{
2625 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2626 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2627 },
2628 exp: [16]byte{
2629 0, 0, 0, 0, 0, 0, 0, 0x80,
2630 0, 0, 0, 0, 0, 0, 0, 0x80,
2631 },
2632 s: 63,
2633 },
2634 }
2635
2636 for _, tc := range tests {
2637 tc := tc
2638 t.Run(tc.name, func(t *testing.T) {
2639 env := newCompilerEnvironment()
2640 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
2641 &wazeroir.CompilationResult{HasMemory: true})
2642
2643 err := compiler.compilePreamble()
2644 require.NoError(t, err)
2645
2646 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x[:8]), binary.LittleEndian.Uint64(tc.x[8:]))))
2647 require.NoError(t, err)
2648
2649 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(tc.s)))
2650 require.NoError(t, err)
2651
2652 err = compiler.compileV128Shl(operationPtr(wazeroir.NewOperationV128Shl(tc.shape)))
2653 require.NoError(t, err)
2654
2655 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
2656 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
2657
2658 err = compiler.compileReturnFunction()
2659 require.NoError(t, err)
2660
2661 code := asm.CodeSegment{}
2662 defer func() { require.NoError(t, code.Unmap()) }()
2663
2664
2665 _, err = compiler.compile(code.NextCodeSection())
2666 require.NoError(t, err)
2667 env.exec(code.Bytes())
2668
2669 lo, hi := env.stackTopAsV128()
2670 var actual [16]byte
2671 binary.LittleEndian.PutUint64(actual[:8], lo)
2672 binary.LittleEndian.PutUint64(actual[8:], hi)
2673 require.Equal(t, tc.exp, actual)
2674 })
2675 }
2676 }
2677
2678 func TestCompiler_compileV128Shr(t *testing.T) {
2679 tests := []struct {
2680 name string
2681 signed bool
2682 shape wazeroir.Shape
2683 s uint32
2684 x, exp [16]byte
2685 }{
2686 {
2687 name: "i8x16/shift=0/signed=false",
2688 shape: wazeroir.ShapeI8x16,
2689 x: [16]byte{
2690 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2691 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2692 },
2693 exp: [16]byte{
2694 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2695 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2696 },
2697 s: 0,
2698 signed: false,
2699 },
2700 {
2701 name: "i8x16/shift=7/signed=false",
2702 shape: wazeroir.ShapeI8x16,
2703 x: [16]byte{
2704 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
2705 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
2706 },
2707 exp: [16]byte{
2708 1, 1, 1, 1, 1, 1, 1, 1,
2709 1, 1, 1, 1, 1, 1, 1, 1,
2710 },
2711 s: 7,
2712 signed: false,
2713 },
2714 {
2715 name: "i8x16/shift=0/signed=false",
2716 shape: wazeroir.ShapeI8x16,
2717 x: [16]byte{
2718 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2719 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2720 },
2721 exp: [16]byte{
2722 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2723 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2724 },
2725 s: 0,
2726 signed: true,
2727 },
2728 {
2729 name: "i8x16/shift=7/signed=false",
2730 shape: wazeroir.ShapeI8x16,
2731 x: [16]byte{
2732 1, 0x80, 0x7e, 0x80, 1, 0x80, 0x7e, 0x80,
2733 1, 0x80, 0x7e, 0x80, 1, 0x80, 0x7e, 0x80,
2734 },
2735 exp: [16]byte{
2736 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff,
2737 0, 0xff, 0, 0xff, 0, 0xff, 0, 0xff,
2738 },
2739 s: 7,
2740 signed: true,
2741 },
2742 {
2743 name: "i16x8/shift=0/signed=false",
2744 shape: wazeroir.ShapeI16x8,
2745 x: [16]byte{
2746 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2747 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2748 },
2749 exp: [16]byte{
2750 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2751 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2752 },
2753 s: 0,
2754 signed: false,
2755 },
2756 {
2757 name: "i16x8/shift=8/signed=false",
2758 shape: wazeroir.ShapeI16x8,
2759 x: [16]byte{
2760 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80,
2761 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80,
2762 },
2763 exp: [16]byte{
2764 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0,
2765 0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0,
2766 },
2767 s: 8,
2768 signed: false,
2769 },
2770 {
2771 name: "i16x8/shift=0/signed=true",
2772 shape: wazeroir.ShapeI16x8,
2773 x: [16]byte{
2774 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2775 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2776 },
2777 exp: [16]byte{
2778 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2779 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2780 },
2781 s: 0,
2782 signed: true,
2783 },
2784 {
2785 name: "i16x8/shift=8/signed=true",
2786 shape: wazeroir.ShapeI16x8,
2787 x: [16]byte{
2788 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80,
2789 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80,
2790 },
2791 exp: [16]byte{
2792 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff,
2793 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff,
2794 },
2795 s: 8,
2796 signed: true,
2797 },
2798 {
2799 name: "i32x4/shift=0/signed=false",
2800 shape: wazeroir.ShapeI32x4,
2801 x: [16]byte{
2802 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2803 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2804 },
2805 exp: [16]byte{
2806 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2807 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2808 },
2809 s: 0,
2810 signed: false,
2811 },
2812 {
2813 name: "i32x4/shift=16/signed=false",
2814 shape: wazeroir.ShapeI32x4,
2815 x: [16]byte{
2816 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2817 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2818 },
2819 exp: [16]byte{
2820 0, 0x80, 0, 0, 0, 0x80, 0, 0,
2821 0, 0x80, 0, 0, 0, 0x80, 0, 0,
2822 },
2823 s: 16,
2824 signed: false,
2825 },
2826 {
2827 name: "i32x4/shift=0/signed=true",
2828 shape: wazeroir.ShapeI32x4,
2829 x: [16]byte{
2830 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2831 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2832 },
2833 exp: [16]byte{
2834 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2835 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2836 },
2837 s: 0,
2838 signed: true,
2839 },
2840 {
2841 name: "i32x4/shift=16/signed=true",
2842 shape: wazeroir.ShapeI32x4,
2843 x: [16]byte{
2844 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2845 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2846 },
2847 exp: [16]byte{
2848 0, 0x80, 0xff, 0xff, 0, 0x80, 0xff, 0xff,
2849 0, 0x80, 0xff, 0xff, 0, 0x80, 0xff, 0xff,
2850 },
2851 s: 16,
2852 signed: true,
2853 },
2854 {
2855 name: "i64x2/shift=0/signed=false",
2856 shape: wazeroir.ShapeI32x4,
2857 x: [16]byte{
2858 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2859 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2860 },
2861 exp: [16]byte{
2862 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2863 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2864 },
2865 s: 0,
2866 signed: false,
2867 },
2868 {
2869 name: "i64x2/shift=16/signed=false",
2870 shape: wazeroir.ShapeI64x2,
2871 x: [16]byte{
2872 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2873 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2874 },
2875 exp: [16]byte{
2876 0, 0x80, 0, 0, 0, 0x80, 0, 0,
2877 0, 0x80, 0, 0, 0, 0x80, 0, 0,
2878 },
2879 s: 16,
2880 signed: false,
2881 },
2882 {
2883 name: "i64x2/shift=0/signed=true",
2884 shape: wazeroir.ShapeI64x2,
2885 x: [16]byte{
2886 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2887 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2888 },
2889 exp: [16]byte{
2890 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2891 1, 0x80, 1, 0x80, 1, 0x80, 1, 0x80,
2892 },
2893 s: 0,
2894 signed: true,
2895 },
2896 {
2897 name: "i64x2/shift=16/signed=true",
2898 shape: wazeroir.ShapeI64x2,
2899 x: [16]byte{
2900 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2901 0, 0, 0, 0x80, 0, 0, 0, 0x80,
2902 },
2903 exp: [16]byte{
2904 0, 0x80, 0, 0, 0, 0x80, 0xff, 0xff,
2905 0, 0x80, 0, 0, 0, 0x80, 0xff, 0xff,
2906 },
2907 s: 16,
2908 signed: true,
2909 },
2910 }
2911
2912 for _, tc := range tests {
2913 tc := tc
2914 t.Run(tc.name, func(t *testing.T) {
2915 env := newCompilerEnvironment()
2916 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
2917 &wazeroir.CompilationResult{HasMemory: true})
2918
2919 err := compiler.compilePreamble()
2920 require.NoError(t, err)
2921
2922 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x[:8]), binary.LittleEndian.Uint64(tc.x[8:]))))
2923 require.NoError(t, err)
2924
2925 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(tc.s)))
2926 require.NoError(t, err)
2927
2928 err = compiler.compileV128Shr(operationPtr(wazeroir.NewOperationV128Shr(tc.shape, tc.signed)))
2929 require.NoError(t, err)
2930
2931 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
2932 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
2933
2934 err = compiler.compileReturnFunction()
2935 require.NoError(t, err)
2936
2937 code := asm.CodeSegment{}
2938 defer func() { require.NoError(t, code.Unmap()) }()
2939
2940
2941 _, err = compiler.compile(code.NextCodeSection())
2942 require.NoError(t, err)
2943 env.exec(code.Bytes())
2944
2945 lo, hi := env.stackTopAsV128()
2946 var actual [16]byte
2947 binary.LittleEndian.PutUint64(actual[:8], lo)
2948 binary.LittleEndian.PutUint64(actual[8:], hi)
2949 require.Equal(t, tc.exp, actual)
2950 })
2951 }
2952 }
2953
2954 func i16x8(i1, i2, i3, i4, i5, i6, i7, i8 uint16) (ret [16]byte) {
2955 binary.LittleEndian.PutUint16(ret[0:], i1)
2956 binary.LittleEndian.PutUint16(ret[2:], i2)
2957 binary.LittleEndian.PutUint16(ret[4:], i3)
2958 binary.LittleEndian.PutUint16(ret[6:], i4)
2959 binary.LittleEndian.PutUint16(ret[8:], i5)
2960 binary.LittleEndian.PutUint16(ret[10:], i6)
2961 binary.LittleEndian.PutUint16(ret[12:], i7)
2962 binary.LittleEndian.PutUint16(ret[14:], i8)
2963 return
2964 }
2965
2966 func i32x4(i1, i2, i3, i4 uint32) (ret [16]byte) {
2967 binary.LittleEndian.PutUint32(ret[0:], i1)
2968 binary.LittleEndian.PutUint32(ret[4:], i2)
2969 binary.LittleEndian.PutUint32(ret[8:], i3)
2970 binary.LittleEndian.PutUint32(ret[12:], i4)
2971 return
2972 }
2973
2974 func f32x4(f1, f2, f3, f4 float32) (ret [16]byte) {
2975 binary.LittleEndian.PutUint32(ret[0:], math.Float32bits(f1))
2976 binary.LittleEndian.PutUint32(ret[4:], math.Float32bits(f2))
2977 binary.LittleEndian.PutUint32(ret[8:], math.Float32bits(f3))
2978 binary.LittleEndian.PutUint32(ret[12:], math.Float32bits(f4))
2979 return
2980 }
2981
2982 func i64x2(i1, i2 uint64) (ret [16]byte) {
2983 binary.LittleEndian.PutUint64(ret[0:], i1)
2984 binary.LittleEndian.PutUint64(ret[8:], i2)
2985 return
2986 }
2987
2988 func f64x2(f1, f2 float64) (ret [16]byte) {
2989 binary.LittleEndian.PutUint64(ret[0:], math.Float64bits(f1))
2990 binary.LittleEndian.PutUint64(ret[8:], math.Float64bits(f2))
2991 return
2992 }
2993
2994 func TestCompiler_compileV128Cmp(t *testing.T) {
2995 tests := []struct {
2996 name string
2997 cmpType wazeroir.V128CmpType
2998 x1, x2, exp [16]byte
2999 }{
3000 {
3001 name: "f32x4 eq",
3002 cmpType: wazeroir.V128CmpTypeF32x4Eq,
3003 x1: f32x4(1.0, -123.123, 0, 3214231),
3004 x2: f32x4(0, 0, 0, 0),
3005 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0},
3006 },
3007 {
3008 name: "f32x4 ne",
3009 cmpType: wazeroir.V128CmpTypeF32x4Ne,
3010 x1: f32x4(1.0, -123.123, 123, 3214231),
3011 x2: f32x4(2.0, 213123123.1231, 123, 0),
3012 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff},
3013 },
3014 {
3015 name: "f32x4 lt",
3016 cmpType: wazeroir.V128CmpTypeF32x4Lt,
3017 x1: f32x4(2.0, -123.123, 1234, 3214231),
3018 x2: f32x4(2.0, 213123123.1231, 123, 0),
3019 exp: [16]byte{0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
3020 },
3021 {
3022 name: "f32x4 le",
3023 cmpType: wazeroir.V128CmpTypeF32x4Le,
3024 x1: f32x4(2.0, -123.123, 1234, 3214231),
3025 x2: f32x4(2.0, 213123123.1231, 123, 0),
3026 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
3027 },
3028 {
3029 name: "f32x4 gt",
3030 cmpType: wazeroir.V128CmpTypeF32x4Gt,
3031 x1: f32x4(2.0, -123.123, 1234, 3214231),
3032 x2: f32x4(2.0, 213123123.1231, 123, 0),
3033 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
3034 },
3035 {
3036 name: "f32x4 ge",
3037 cmpType: wazeroir.V128CmpTypeF32x4Ge,
3038 x1: f32x4(2.0, -123.123, 1234, 3214231),
3039 x2: f32x4(2.0, 213123123.1231, 123, 0),
3040 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
3041 },
3042 {
3043 name: "f64x2 eq",
3044 cmpType: wazeroir.V128CmpTypeF64x2Eq,
3045 x1: f64x2(1.0, -123.12412),
3046 x2: f64x2(1.0, 123.123124),
3047 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
3048 },
3049 {
3050 name: "f64x2 ne",
3051 cmpType: wazeroir.V128CmpTypeF64x2Ne,
3052 x1: f64x2(1.0, -123.12412),
3053 x2: f64x2(1.0, 123.123124),
3054 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
3055 },
3056 {
3057 name: "f64x2 lt",
3058 cmpType: wazeroir.V128CmpTypeF64x2Lt,
3059 x1: f64x2(-123, math.Inf(-1)),
3060 x2: f64x2(-123, -1234515),
3061 exp: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
3062 },
3063 {
3064 name: "f64x2 le",
3065 cmpType: wazeroir.V128CmpTypeF64x2Le,
3066 x1: f64x2(-123, 123),
3067 x2: f64x2(-123, math.MaxFloat64),
3068 exp: [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
3069 },
3070 {
3071 name: "f64x2 gt",
3072 cmpType: wazeroir.V128CmpTypeF64x2Gt,
3073 x1: f64x2(math.MaxFloat64, -123.0),
3074 x2: f64x2(123, -123.0),
3075 exp: [16]byte{
3076 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3077 0, 0, 0, 0, 0, 0, 0, 0,
3078 },
3079 },
3080 {
3081 name: "f64x2 ge",
3082 cmpType: wazeroir.V128CmpTypeF64x2Ge,
3083 x1: f64x2(math.MaxFloat64, -123.0),
3084 x2: f64x2(123, -123.0),
3085 exp: [16]byte{
3086 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3087 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
3088 },
3089 },
3090 {
3091 name: "i8x16 eq",
3092 cmpType: wazeroir.V128CmpTypeI8x16Eq,
3093 x1: [16]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1},
3094 x2: [16]byte{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0},
3095 exp: [16]byte{0, 0xff, 0, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xff, 0},
3096 },
3097 {
3098 name: "i8x16 ne",
3099 cmpType: wazeroir.V128CmpTypeI8x16Ne,
3100 x1: [16]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1},
3101 x2: [16]byte{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0},
3102 exp: [16]byte{0xff, 0, 0xff, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0xff, 0, 0, 0xff},
3103 },
3104 {
3105 name: "i8x16 lt_s",
3106 cmpType: wazeroir.V128CmpTypeI8x16LtS,
3107 x1: [16]byte{0: i8ToU8(-1), 15: 0},
3108 x2: [16]byte{0: 0x7f, 15: i8ToU8(-1)},
3109 exp: [16]byte{0: 0xff},
3110 },
3111 {
3112 name: "i8x16 lt_u",
3113 cmpType: wazeroir.V128CmpTypeI8x16LtU,
3114 x1: [16]byte{0: 0xff, 15: 0},
3115 x2: [16]byte{0: 0x7f, 15: 0xff},
3116 exp: [16]byte{15: 0xff},
3117 },
3118 {
3119 name: "i8x16 gt_s",
3120 cmpType: wazeroir.V128CmpTypeI8x16GtS,
3121 x1: [16]byte{0: i8ToU8(-1), 15: 0},
3122 x2: [16]byte{0: 0x7f, 15: i8ToU8(-1)},
3123 exp: [16]byte{15: 0xff},
3124 },
3125 {
3126 name: "i8x16 gt_u",
3127 cmpType: wazeroir.V128CmpTypeI8x16GtU,
3128 x1: [16]byte{0: 0xff, 15: 0},
3129 x2: [16]byte{0: 0x7f, 15: 0xff},
3130 exp: [16]byte{0: 0xff},
3131 },
3132 {
3133 name: "i8x16 le_s",
3134 cmpType: wazeroir.V128CmpTypeI8x16LeS,
3135 x1: [16]byte{i8ToU8(-1), 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, i8ToU8(-1)},
3136 x2: [16]byte{0: 0x7f, 15: i8ToU8(-1)},
3137 exp: [16]byte{0: 0xff, 15: 0xff},
3138 },
3139 {
3140 name: "i8x16 le_u",
3141 cmpType: wazeroir.V128CmpTypeI8x16LeU,
3142 x1: [16]byte{0x80, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff},
3143 x2: [16]byte{0: 0x7f, 5: 0x1, 15: 0xff},
3144 exp: [16]byte{5: 0xff, 15: 0xff},
3145 },
3146 {
3147 name: "i8x16 ge_s",
3148 cmpType: wazeroir.V128CmpTypeI8x16GeS,
3149 x1: [16]byte{0: 0x7f, 15: i8ToU8(-1)},
3150 x2: [16]byte{i8ToU8(-1), 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, i8ToU8(-1)},
3151 exp: [16]byte{0: 0xff, 15: 0xff},
3152 },
3153 {
3154 name: "i8x16 ge_u",
3155 cmpType: wazeroir.V128CmpTypeI8x16GeU,
3156 x1: [16]byte{0: 0x7f, 3: 0xe, 15: 0xff},
3157 x2: [16]byte{0xff, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xff},
3158 exp: [16]byte{3: 0xff, 15: 0xff},
3159 },
3160 {
3161 name: "i16x8 eq",
3162 cmpType: wazeroir.V128CmpTypeI16x8Eq,
3163 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1),
3164 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0),
3165 exp: i16x8(0xffff, 0, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0),
3166 },
3167 {
3168 name: "i8x16 ne",
3169 cmpType: wazeroir.V128CmpTypeI16x8Ne,
3170 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1),
3171 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0),
3172 exp: i16x8(0, 0xffff, 0, 0, 0xffff, 0, 0, 0xffff),
3173 },
3174 {
3175 name: "i8x16 lt_s",
3176 cmpType: wazeroir.V128CmpTypeI16x8LtS,
3177 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
3178 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0),
3179 exp: i16x8(0xffff, 0, 0, 0, 0xffff, 0, 0, 0),
3180 },
3181 {
3182 name: "i8x16 lt_u",
3183 cmpType: wazeroir.V128CmpTypeI16x8LtU,
3184 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
3185 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0),
3186 exp: i16x8(0, 0, 0, 0, 0xffff, 0, 0, 0),
3187 },
3188 {
3189 name: "i8x16 gt_s",
3190 cmpType: wazeroir.V128CmpTypeI16x8GtS,
3191 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1),
3192 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
3193 exp: i16x8(0xffff, 0xffff, 0, 0, 0, 0, 0, 0xffff),
3194 },
3195 {
3196 name: "i8x16 gt_u",
3197 cmpType: wazeroir.V128CmpTypeI16x8GtU,
3198 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1),
3199 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
3200 exp: i16x8(0, 0xffff, 0, 0, 0, 0, 0, 0xffff),
3201 },
3202 {
3203 name: "i8x16 le_s",
3204 cmpType: wazeroir.V128CmpTypeI16x8LeS,
3205 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
3206 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0),
3207 exp: i16x8(0xffff, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0),
3208 },
3209 {
3210 name: "i8x16 le_u",
3211 cmpType: wazeroir.V128CmpTypeI16x8LeU,
3212 x1: i16x8(0xffff, 1, 0, 1, 0, 1, 0, 1),
3213 x2: i16x8(0, 0, 0, 1, 1, 1, 0, 0),
3214 exp: i16x8(0, 0, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0),
3215 },
3216 {
3217 name: "i8x16 ge_s",
3218 cmpType: wazeroir.V128CmpTypeI16x8GeS,
3219 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1),
3220 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
3221 exp: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0xffff),
3222 },
3223 {
3224 name: "i8x16 ge_u",
3225 cmpType: wazeroir.V128CmpTypeI16x8GeU,
3226 x1: i16x8(0, 1, 0, 1, 0, 1, 0, 1),
3227 x2: i16x8(0xffff, 0, 0, 1, 1, 1, 0, 0),
3228 exp: i16x8(0, 0xffff, 0xffff, 0xffff, 0, 0xffff, 0xffff, 0xffff),
3229 },
3230 {
3231 name: "i32x4 eq",
3232 cmpType: wazeroir.V128CmpTypeI32x4Eq,
3233 x1: i32x4(0, 1, 1, 0),
3234 x2: i32x4(0, 1, 0, 1),
3235 exp: i32x4(0xffffffff, 0xffffffff, 0, 0),
3236 },
3237 {
3238 name: "i32x4 ne",
3239 cmpType: wazeroir.V128CmpTypeI32x4Ne,
3240 x1: i32x4(0, 1, 1, 0),
3241 x2: i32x4(0, 1, 0, 1),
3242 exp: i32x4(0, 0, 0xffffffff, 0xffffffff),
3243 },
3244 {
3245 name: "i32x4 lt_s",
3246 cmpType: wazeroir.V128CmpTypeI32x4LtS,
3247 x1: i32x4(0xffffffff, 1, 1, 0),
3248 x2: i32x4(0, 1, 0, 1),
3249 exp: i32x4(0xffffffff, 0, 0, 0xffffffff),
3250 },
3251 {
3252 name: "i32x4 lt_u",
3253 cmpType: wazeroir.V128CmpTypeI32x4LtU,
3254 x1: i32x4(0xffffffff, 1, 1, 0),
3255 x2: i32x4(0, 1, 0, 1),
3256 exp: i32x4(0, 0, 0, 0xffffffff),
3257 },
3258 {
3259 name: "i32x4 gt_s",
3260 cmpType: wazeroir.V128CmpTypeI32x4GtS,
3261 x1: i32x4(0, 1, 1, 1),
3262 x2: i32x4(0xffffffff, 1, 0, 0),
3263 exp: i32x4(0xffffffff, 0, 0xffffffff, 0xffffffff),
3264 },
3265 {
3266 name: "i32x4 gt_u",
3267 cmpType: wazeroir.V128CmpTypeI32x4GtU,
3268 x1: i32x4(0, 1, 1, 1),
3269 x2: i32x4(0xffffffff, 1, 0, 0),
3270 exp: i32x4(0, 0, 0xffffffff, 0xffffffff),
3271 },
3272 {
3273 name: "i32x4 le_s",
3274 cmpType: wazeroir.V128CmpTypeI32x4LeS,
3275 x1: i32x4(0xffffffff, 1, 1, 0),
3276 x2: i32x4(0, 1, 0, 1),
3277 exp: i32x4(0xffffffff, 0xffffffff, 0, 0xffffffff),
3278 },
3279 {
3280 name: "i32x4 le_u",
3281 cmpType: wazeroir.V128CmpTypeI32x4LeU,
3282 x1: i32x4(0xffffffff, 1, 1, 0),
3283 x2: i32x4(0, 1, 0, 1),
3284 exp: i32x4(0, 0xffffffff, 0, 0xffffffff),
3285 },
3286 {
3287 name: "i32x4 ge_s",
3288 cmpType: wazeroir.V128CmpTypeI32x4GeS,
3289 x1: i32x4(0, 1, 1, 0),
3290 x2: i32x4(0xffffffff, 1, 0, 1),
3291 exp: i32x4(0xffffffff, 0xffffffff, 0xffffffff, 0),
3292 },
3293 {
3294 name: "i32x4 ge_u",
3295 cmpType: wazeroir.V128CmpTypeI32x4GeU,
3296 x1: i32x4(0, 1, 1, 0),
3297 x2: i32x4(0xffffffff, 1, 0, 1),
3298 exp: i32x4(0, 0xffffffff, 0xffffffff, 0),
3299 },
3300 {
3301 name: "i64x2 eq",
3302 cmpType: wazeroir.V128CmpTypeI64x2Eq,
3303 x1: i64x2(1, 0),
3304 x2: i64x2(0, 0),
3305 exp: i64x2(0, 0xffffffffffffffff),
3306 },
3307 {
3308 name: "i64x2 ne",
3309 cmpType: wazeroir.V128CmpTypeI64x2Ne,
3310 x1: i64x2(1, 0),
3311 x2: i64x2(0, 0),
3312 exp: i64x2(0xffffffffffffffff, 0),
3313 },
3314 {
3315 name: "i64x2 lt_s",
3316 cmpType: wazeroir.V128CmpTypeI64x2LtS,
3317 x1: i64x2(0xffffffffffffffff, 0),
3318 x2: i64x2(0, 0),
3319 exp: i64x2(0xffffffffffffffff, 0),
3320 },
3321 {
3322 name: "i64x2 gt_s",
3323 cmpType: wazeroir.V128CmpTypeI64x2GtS,
3324 x1: i64x2(123, 0),
3325 x2: i64x2(123, 0xffffffffffffffff),
3326 exp: i64x2(0, 0xffffffffffffffff),
3327 },
3328 {
3329 name: "i64x2 le_s",
3330 cmpType: wazeroir.V128CmpTypeI64x2LeS,
3331 x1: i64x2(123, 0xffffffffffffffff),
3332 x2: i64x2(123, 0),
3333 exp: i64x2(0xffffffffffffffff, 0xffffffffffffffff),
3334 },
3335 {
3336 name: "i64x2 ge_s",
3337 cmpType: wazeroir.V128CmpTypeI64x2GeS,
3338 x1: i64x2(123, 0),
3339 x2: i64x2(123, 0xffffffffffffffff),
3340 exp: i64x2(0xffffffffffffffff, 0xffffffffffffffff),
3341 },
3342 }
3343
3344 for _, tc := range tests {
3345 tc := tc
3346 t.Run(tc.name, func(t *testing.T) {
3347 env := newCompilerEnvironment()
3348 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3349 &wazeroir.CompilationResult{HasMemory: true})
3350
3351 err := compiler.compilePreamble()
3352 require.NoError(t, err)
3353
3354 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
3355 require.NoError(t, err)
3356
3357 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
3358 require.NoError(t, err)
3359
3360 err = compiler.compileV128Cmp(operationPtr(wazeroir.NewOperationV128Cmp(tc.cmpType)))
3361 require.NoError(t, err)
3362
3363 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3364 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3365
3366 err = compiler.compileReturnFunction()
3367 require.NoError(t, err)
3368
3369 code := asm.CodeSegment{}
3370 defer func() { require.NoError(t, code.Unmap()) }()
3371
3372
3373 _, err = compiler.compile(code.NextCodeSection())
3374 require.NoError(t, err)
3375 env.exec(code.Bytes())
3376
3377 lo, hi := env.stackTopAsV128()
3378 var actual [16]byte
3379 binary.LittleEndian.PutUint64(actual[:8], lo)
3380 binary.LittleEndian.PutUint64(actual[8:], hi)
3381 require.Equal(t, tc.exp, actual)
3382 })
3383 }
3384 }
3385
3386 func TestCompiler_compileV128AvgrU(t *testing.T) {
3387 tests := []struct {
3388 name string
3389 shape wazeroir.Shape
3390 x1, x2, exp [16]byte
3391 }{
3392 {
3393 name: "i8x16",
3394 shape: wazeroir.ShapeI8x16,
3395 x1: [16]byte{0: 1, 2: 10, 10: 10, 15: math.MaxUint8},
3396 x2: [16]byte{0: 10, 4: 5, 10: 5, 15: 10},
3397 exp: [16]byte{
3398 0: byte((uint16(1) + uint16(10) + 1) / 2),
3399 2: byte((uint16(10) + 1) / 2),
3400 4: byte((uint16(5) + 1) / 2),
3401 10: byte((uint16(10) + uint16(5) + 1) / 2),
3402 15: byte((uint16(math.MaxUint8) + uint16(10) + 1) / 2),
3403 },
3404 },
3405 {
3406 name: "i16x8",
3407 shape: wazeroir.ShapeI16x8,
3408 x1: i16x8(1, 0, 100, 0, 0, math.MaxUint16, 0, 0),
3409 x2: i16x8(10, 0, math.MaxUint16, 0, 0, 1, 0, 0),
3410 exp: i16x8(
3411 uint16((uint32(1)+uint32(10)+1)/2),
3412 0,
3413 uint16((uint32(100)+uint32(math.MaxUint16)+1)/2),
3414 0,
3415 0,
3416 uint16((uint32(1)+uint32(math.MaxUint16)+1)/2),
3417 0, 0,
3418 ),
3419 },
3420 }
3421
3422 for _, tc := range tests {
3423 tc := tc
3424 t.Run(tc.name, func(t *testing.T) {
3425 env := newCompilerEnvironment()
3426 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3427 &wazeroir.CompilationResult{HasMemory: true})
3428
3429 err := compiler.compilePreamble()
3430 require.NoError(t, err)
3431
3432 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
3433 require.NoError(t, err)
3434
3435 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
3436 require.NoError(t, err)
3437
3438 err = compiler.compileV128AvgrU(operationPtr(wazeroir.NewOperationV128AvgrU(tc.shape)))
3439 require.NoError(t, err)
3440
3441 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3442 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3443
3444 err = compiler.compileReturnFunction()
3445 require.NoError(t, err)
3446
3447 code := asm.CodeSegment{}
3448 defer func() { require.NoError(t, code.Unmap()) }()
3449
3450
3451 _, err = compiler.compile(code.NextCodeSection())
3452 require.NoError(t, err)
3453 env.exec(code.Bytes())
3454
3455 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
3456
3457 lo, hi := env.stackTopAsV128()
3458 var actual [16]byte
3459 binary.LittleEndian.PutUint64(actual[:8], lo)
3460 binary.LittleEndian.PutUint64(actual[8:], hi)
3461 require.Equal(t, tc.exp, actual)
3462 })
3463 }
3464 }
3465
3466 func TestCompiler_compileV128Sqrt(t *testing.T) {
3467 tests := []struct {
3468 name string
3469 shape wazeroir.Shape
3470 v, exp [16]byte
3471 }{
3472 {
3473 name: "f32x4",
3474 shape: wazeroir.ShapeF32x4,
3475 v: f32x4(1.23, -123.1231, math.MaxFloat32, float32(math.Inf(1))),
3476 exp: f32x4(
3477 float32(math.Sqrt(float64(float32(1.23)))),
3478 float32(math.Sqrt(float64(float32(-123.1231)))),
3479 float32(math.Sqrt(float64(float32(math.MaxFloat32)))),
3480 float32(math.Sqrt(float64(float32(math.Inf(1))))),
3481 ),
3482 },
3483 {
3484 name: "f64x2",
3485 shape: wazeroir.ShapeF64x2,
3486 v: f64x2(1.2314, math.MaxFloat64),
3487 exp: f64x2(math.Sqrt(1.2314), math.Sqrt(math.MaxFloat64)),
3488 },
3489 }
3490
3491 for _, tc := range tests {
3492 tc := tc
3493 t.Run(tc.name, func(t *testing.T) {
3494 env := newCompilerEnvironment()
3495 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3496 &wazeroir.CompilationResult{HasMemory: true})
3497
3498 err := compiler.compilePreamble()
3499 require.NoError(t, err)
3500
3501 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
3502 require.NoError(t, err)
3503
3504 err = compiler.compileV128Sqrt(operationPtr(wazeroir.NewOperationV128Sqrt(tc.shape)))
3505 require.NoError(t, err)
3506
3507 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3508 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3509
3510 err = compiler.compileReturnFunction()
3511 require.NoError(t, err)
3512
3513 code := asm.CodeSegment{}
3514 defer func() { require.NoError(t, code.Unmap()) }()
3515
3516
3517 _, err = compiler.compile(code.NextCodeSection())
3518 require.NoError(t, err)
3519 env.exec(code.Bytes())
3520
3521 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
3522
3523 lo, hi := env.stackTopAsV128()
3524 var actual [16]byte
3525 binary.LittleEndian.PutUint64(actual[:8], lo)
3526 binary.LittleEndian.PutUint64(actual[8:], hi)
3527 require.Equal(t, tc.exp, actual)
3528 })
3529 }
3530 }
3531
3532 func TestCompiler_compileV128Mul(t *testing.T) {
3533 tests := []struct {
3534 name string
3535 shape wazeroir.Shape
3536 x1, x2, exp [16]byte
3537 }{
3538 {
3539 name: "i16x8",
3540 shape: wazeroir.ShapeI16x8,
3541 x1: i16x8(1123, 0, 123, 1, 1, 5, 8, 1),
3542 x2: i16x8(0, 123, 123, 0, 1, 5, 9, 1),
3543 exp: i16x8(0, 0, 123*123, 0, 1, 25, 8*9, 1),
3544 },
3545 {
3546 name: "i32x4",
3547 shape: wazeroir.ShapeI32x4,
3548 x1: i32x4(i32ToU32(-123), 5, 4, math.MaxUint32),
3549 x2: i32x4(i32ToU32(-10), 1, i32ToU32(-104), 0),
3550 exp: i32x4(1230, 5, i32ToU32(-416), 0),
3551 },
3552 {
3553 name: "i64x2",
3554 shape: wazeroir.ShapeI64x2,
3555 x1: i64x2(1, 12345),
3556 x2: i64x2(100, i64ToU64(-10)),
3557 exp: i64x2(100, i64ToU64(-123450)),
3558 },
3559 {
3560 name: "f32x4",
3561 shape: wazeroir.ShapeF32x4,
3562 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))),
3563 x2: f32x4(51234.12341, 123, math.MaxFloat32, -123),
3564 exp: f32x4(51234.12341, 123*123, float32(math.Inf(1)), float32(math.Inf(1))),
3565 },
3566 {
3567 name: "f64x2",
3568 shape: wazeroir.ShapeF64x2,
3569 x1: f64x2(1.123, math.Inf(1)),
3570 x2: f64x2(1.123, math.MinInt64),
3571 exp: f64x2(1.123*1.123, math.Inf(-1)),
3572 },
3573 }
3574
3575 for _, tc := range tests {
3576 tc := tc
3577 t.Run(tc.name, func(t *testing.T) {
3578 env := newCompilerEnvironment()
3579 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3580 &wazeroir.CompilationResult{HasMemory: true})
3581
3582 err := compiler.compilePreamble()
3583 require.NoError(t, err)
3584
3585 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
3586 require.NoError(t, err)
3587
3588 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
3589 require.NoError(t, err)
3590
3591 err = compiler.compileV128Mul(operationPtr(wazeroir.NewOperationV128Mul(tc.shape)))
3592 require.NoError(t, err)
3593
3594 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3595 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3596
3597 err = compiler.compileReturnFunction()
3598 require.NoError(t, err)
3599
3600 code := asm.CodeSegment{}
3601 defer func() { require.NoError(t, code.Unmap()) }()
3602
3603
3604 _, err = compiler.compile(code.NextCodeSection())
3605 require.NoError(t, err)
3606 env.exec(code.Bytes())
3607
3608 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
3609
3610 lo, hi := env.stackTopAsV128()
3611 var actual [16]byte
3612 binary.LittleEndian.PutUint64(actual[:8], lo)
3613 binary.LittleEndian.PutUint64(actual[8:], hi)
3614 require.Equal(t, tc.exp, actual)
3615 })
3616 }
3617 }
3618
3619 func TestCompiler_compileV128Neg(t *testing.T) {
3620 tests := []struct {
3621 name string
3622 shape wazeroir.Shape
3623 v, exp [16]byte
3624 }{
3625 {
3626 name: "i8x16",
3627 shape: wazeroir.ShapeI8x16,
3628 v: [16]byte{1: 123, 5: i8ToU8(-1), 15: i8ToU8(-125)},
3629 exp: [16]byte{1: i8ToU8(-123), 5: 1, 15: 125},
3630 },
3631 {
3632 name: "i16x8",
3633 shape: wazeroir.ShapeI16x8,
3634 v: i16x8(0, 0, i16ToU16(-123), 0, 1, 25, 8, i16ToU16(-1)),
3635 exp: i16x8(0, 0, 123, 0, i16ToU16(-1), i16ToU16(-25), i16ToU16(-8), 1),
3636 },
3637 {
3638 name: "i32x4",
3639 shape: wazeroir.ShapeI32x4,
3640 v: i32x4(1230, 5, i32ToU32(-416), 0),
3641 exp: i32x4(i32ToU32(-1230), i32ToU32(-5), 416, 0),
3642 },
3643 {
3644 name: "i64x2",
3645 shape: wazeroir.ShapeI64x2,
3646 v: i64x2(100, i64ToU64(-123450)),
3647 exp: i64x2(i64ToU64(-100), 123450),
3648 },
3649 {
3650 name: "f32x4",
3651 shape: wazeroir.ShapeF32x4,
3652 v: f32x4(51234.12341, -123, float32(math.Inf(1)), 0.1),
3653 exp: f32x4(-51234.12341, 123, float32(math.Inf(-1)), -0.1),
3654 },
3655 {
3656 name: "f32x4",
3657 shape: wazeroir.ShapeF32x4,
3658 v: f32x4(51234.12341, 0, float32(math.Inf(1)), 0.1),
3659 exp: f32x4(-51234.12341, float32(math.Copysign(0, -1)), float32(math.Inf(-1)), -0.1),
3660 },
3661 {
3662 name: "f64x2",
3663 shape: wazeroir.ShapeF64x2,
3664 v: f64x2(1.123, math.Inf(-1)),
3665 exp: f64x2(-1.123, math.Inf(1)),
3666 },
3667 {
3668 name: "f64x2",
3669 shape: wazeroir.ShapeF64x2,
3670 v: f64x2(0, math.Inf(-1)),
3671 exp: f64x2(math.Copysign(0, -1), math.Inf(1)),
3672 },
3673 }
3674
3675 for _, tc := range tests {
3676 tc := tc
3677 t.Run(tc.name, func(t *testing.T) {
3678 env := newCompilerEnvironment()
3679 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3680 &wazeroir.CompilationResult{HasMemory: true})
3681
3682 err := compiler.compilePreamble()
3683 require.NoError(t, err)
3684
3685 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
3686 require.NoError(t, err)
3687
3688 err = compiler.compileV128Neg(operationPtr(wazeroir.NewOperationV128Neg(tc.shape)))
3689 require.NoError(t, err)
3690
3691 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3692 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3693
3694 err = compiler.compileReturnFunction()
3695 require.NoError(t, err)
3696
3697 code := asm.CodeSegment{}
3698 defer func() { require.NoError(t, code.Unmap()) }()
3699
3700
3701 _, err = compiler.compile(code.NextCodeSection())
3702 require.NoError(t, err)
3703 env.exec(code.Bytes())
3704
3705 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
3706
3707 lo, hi := env.stackTopAsV128()
3708 var actual [16]byte
3709 binary.LittleEndian.PutUint64(actual[:8], lo)
3710 binary.LittleEndian.PutUint64(actual[8:], hi)
3711 require.Equal(t, tc.exp, actual)
3712 })
3713 }
3714 }
3715
3716 func TestCompiler_compileV128Abs(t *testing.T) {
3717 tests := []struct {
3718 name string
3719 shape wazeroir.Shape
3720 v, exp [16]byte
3721 }{
3722 {
3723 name: "i8x16",
3724 shape: wazeroir.ShapeI8x16,
3725 v: [16]byte{1: 123, 5: i8ToU8(-1), 15: i8ToU8(-125)},
3726 exp: [16]byte{1: 123, 5: 1, 15: 125},
3727 },
3728 {
3729 name: "i16x8",
3730 shape: wazeroir.ShapeI16x8,
3731 v: i16x8(0, 0, i16ToU16(-123), 0, 1, 25, 8, i16ToU16(-1)),
3732 exp: i16x8(0, 0, 123, 0, 1, 25, 8, 1),
3733 },
3734 {
3735 name: "i32x4",
3736 shape: wazeroir.ShapeI32x4,
3737 v: i32x4(i32ToU32(-1230), 5, i32ToU32(-416), 0),
3738 exp: i32x4(1230, 5, 416, 0),
3739 },
3740 {
3741 name: "i64x2",
3742 shape: wazeroir.ShapeI64x2,
3743 v: i64x2(i64ToU64(-100), i64ToU64(-123450)),
3744 exp: i64x2(100, 123450),
3745 },
3746 {
3747 name: "f32x4",
3748 shape: wazeroir.ShapeF32x4,
3749 v: f32x4(51234.12341, -123, float32(math.Inf(1)), 0.1),
3750 exp: f32x4(51234.12341, 123, float32(math.Inf(1)), 0.1),
3751 },
3752 {
3753 name: "f32x4",
3754 shape: wazeroir.ShapeF32x4,
3755 v: f32x4(51234.12341, 0, float32(math.Inf(1)), -0.1),
3756 exp: f32x4(51234.12341, 0, float32(math.Inf(1)), 0.1),
3757 },
3758 {
3759 name: "f64x2",
3760 shape: wazeroir.ShapeF64x2,
3761 v: f64x2(-1.123, math.Inf(-1)),
3762 exp: f64x2(1.123, math.Inf(1)),
3763 },
3764 {
3765 name: "f64x2",
3766 shape: wazeroir.ShapeF64x2,
3767 v: f64x2(0, math.Inf(-1)),
3768 exp: f64x2(0, math.Inf(1)),
3769 },
3770 }
3771
3772 for _, tc := range tests {
3773 tc := tc
3774 t.Run(tc.name, func(t *testing.T) {
3775 env := newCompilerEnvironment()
3776 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3777 &wazeroir.CompilationResult{HasMemory: true})
3778
3779 err := compiler.compilePreamble()
3780 require.NoError(t, err)
3781
3782 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
3783 require.NoError(t, err)
3784
3785 err = compiler.compileV128Abs(operationPtr(wazeroir.NewOperationV128Abs(tc.shape)))
3786 require.NoError(t, err)
3787
3788 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3789 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3790
3791 err = compiler.compileReturnFunction()
3792 require.NoError(t, err)
3793
3794 code := asm.CodeSegment{}
3795 defer func() { require.NoError(t, code.Unmap()) }()
3796
3797
3798 _, err = compiler.compile(code.NextCodeSection())
3799 require.NoError(t, err)
3800 env.exec(code.Bytes())
3801
3802 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
3803
3804 lo, hi := env.stackTopAsV128()
3805 var actual [16]byte
3806 binary.LittleEndian.PutUint64(actual[:8], lo)
3807 binary.LittleEndian.PutUint64(actual[8:], hi)
3808 require.Equal(t, tc.exp, actual)
3809 })
3810 }
3811 }
3812
3813 func TestCompiler_compileV128Div(t *testing.T) {
3814 tests := []struct {
3815 name string
3816 shape wazeroir.Shape
3817 x1, x2, exp [16]byte
3818 }{
3819 {
3820 name: "f32x4",
3821 shape: wazeroir.ShapeF32x4,
3822 x1: f32x4(1.0, 123, float32(math.Inf(1)), float32(math.Inf(-1))),
3823 x2: f32x4(123.12, 123, math.MaxFloat32, -123),
3824 exp: f32x4(float32(1.0)/float32(123.12), 1, float32(math.Inf(1)), float32(math.Inf(1))),
3825 },
3826 {
3827 name: "f64x2",
3828 shape: wazeroir.ShapeF64x2,
3829 x1: f64x2(1.123, math.Inf(1)),
3830 x2: f64x2(1.123, math.MinInt64),
3831 exp: f64x2(1.0, math.Inf(-1)),
3832 },
3833 {
3834 name: "f64x2",
3835 shape: wazeroir.ShapeF64x2,
3836 x1: f64x2(0, math.Inf(1)),
3837 x2: f64x2(1.123, math.MaxInt64),
3838 exp: f64x2(0, math.Inf(1)),
3839 },
3840 }
3841
3842 for _, tc := range tests {
3843 tc := tc
3844 t.Run(tc.name, func(t *testing.T) {
3845 env := newCompilerEnvironment()
3846 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
3847 &wazeroir.CompilationResult{HasMemory: true})
3848
3849 err := compiler.compilePreamble()
3850 require.NoError(t, err)
3851
3852 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
3853 require.NoError(t, err)
3854
3855 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
3856 require.NoError(t, err)
3857
3858 err = compiler.compileV128Div(operationPtr(wazeroir.NewOperationV128Div(tc.shape)))
3859 require.NoError(t, err)
3860
3861 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
3862 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
3863
3864 err = compiler.compileReturnFunction()
3865 require.NoError(t, err)
3866
3867 code := asm.CodeSegment{}
3868 defer func() { require.NoError(t, code.Unmap()) }()
3869
3870
3871 _, err = compiler.compile(code.NextCodeSection())
3872 require.NoError(t, err)
3873 env.exec(code.Bytes())
3874
3875 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
3876
3877 lo, hi := env.stackTopAsV128()
3878 var actual [16]byte
3879 binary.LittleEndian.PutUint64(actual[:8], lo)
3880 binary.LittleEndian.PutUint64(actual[8:], hi)
3881 require.Equal(t, tc.exp, actual)
3882 })
3883 }
3884 }
3885
3886 func TestCompiler_compileV128Min(t *testing.T) {
3887 tests := []struct {
3888 name string
3889 shape wazeroir.Shape
3890 signed bool
3891 x1, x2, exp [16]byte
3892 }{
3893 {
3894 name: "i8x16s",
3895 shape: wazeroir.ShapeI8x16,
3896 signed: true,
3897 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2},
3898 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)},
3899 exp: [16]byte{0: 1, 5: i8ToU8(-1), 15: i8ToU8(-1)},
3900 },
3901 {
3902 name: "i8x16u",
3903 shape: wazeroir.ShapeI8x16,
3904 signed: false,
3905 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2},
3906 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)},
3907 exp: [16]byte{0: 1, 5: 0, 15: 2},
3908 },
3909 {
3910 name: "i16x8s",
3911 shape: wazeroir.ShapeI16x8,
3912 signed: true,
3913 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1),
3914 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1),
3915 exp: i16x8(0, 0, i16ToU16(-123), 1, 1, 4, i16ToU16(-123), 1),
3916 },
3917 {
3918 name: "i16x8u",
3919 shape: wazeroir.ShapeI16x8,
3920 signed: false,
3921 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1),
3922 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1),
3923 exp: i16x8(0, 0, 123, 1, 1, 4, 5, 1),
3924 },
3925 {
3926 name: "i32x4s",
3927 shape: wazeroir.ShapeI32x4,
3928 signed: true,
3929 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)),
3930 x2: i32x4(123, 5, 1, 0),
3931 exp: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)),
3932 },
3933 {
3934 name: "i32x4u",
3935 shape: wazeroir.ShapeI32x4,
3936 signed: false,
3937 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)),
3938 x2: i32x4(123, 5, 1, 0),
3939 exp: i32x4(123, 0, 1, 0),
3940 },
3941 {
3942 name: "f32x4",
3943 shape: wazeroir.ShapeF32x4,
3944
3945 x1: f32x4(0, math.Float32frombits(0b1<<31), 0, 0),
3946 x2: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0),
3947 exp: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0),
3948 },
3949 {
3950 name: "f32x4",
3951 shape: wazeroir.ShapeF32x4,
3952
3953 x1: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0),
3954 x2: f32x4(0, math.Float32frombits(0b1<<31), 0, 0),
3955 exp: f32x4(math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), math.Float32frombits(0b1<<31), 0),
3956 },
3957 {
3958 name: "f32x4",
3959 shape: wazeroir.ShapeF32x4,
3960 x1: f32x4(float32(math.NaN()), -123.12, 2.3, float32(math.Inf(1))),
3961 x2: f32x4(5.5, 123.12, 5.0, float32(math.Inf(-1))),
3962 exp: f32x4(float32(math.NaN()), -123.12, 2.3, float32(math.Inf(-1))),
3963 },
3964 {
3965 name: "f32x4",
3966 shape: wazeroir.ShapeF32x4,
3967 x1: f32x4(5.5, 123.12, -5.0, float32(math.Inf(-1))),
3968 x2: f32x4(-123.12, float32(math.NaN()), 2.3, float32(math.Inf(-1))),
3969 exp: f32x4(-123.12, float32(math.NaN()), -5.0, float32(math.Inf(-1))),
3970 },
3971 {
3972 name: "f32x4",
3973 shape: wazeroir.ShapeF32x4,
3974 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
3975 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
3976 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
3977 },
3978 {
3979 name: "f32x4",
3980 shape: wazeroir.ShapeF32x4,
3981 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
3982 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
3983 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
3984 },
3985 {
3986 name: "f64x2",
3987 shape: wazeroir.ShapeF64x2,
3988
3989 x1: f64x2(math.Copysign(0, -1), math.Copysign(0, 1)),
3990 x2: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)),
3991 exp: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)),
3992 },
3993 {
3994 name: "f64x2",
3995 shape: wazeroir.ShapeF64x2,
3996
3997 x1: f64x2(math.Copysign(0, -1), 0),
3998 x2: f64x2(0, math.Copysign(0, -1)),
3999 exp: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)),
4000 },
4001 {
4002 name: "f64x2",
4003 shape: wazeroir.ShapeF64x2,
4004 x1: f64x2(math.MinInt64, 0),
4005 x2: f64x2(math.MaxInt64, -12.3),
4006 exp: f64x2(math.MinInt64, -12.3),
4007 },
4008 {
4009 name: "f64x2",
4010 shape: wazeroir.ShapeF64x2,
4011 x1: f64x2(math.MaxInt64, -12.3),
4012 x2: f64x2(math.MinInt64, 0),
4013 exp: f64x2(math.MinInt64, -12.3),
4014 },
4015 {
4016 name: "f64x2",
4017 shape: wazeroir.ShapeF64x2,
4018 x1: f64x2(math.NaN(), math.NaN()),
4019 x2: f64x2(math.Inf(1), math.Inf(-1)),
4020 exp: f64x2(math.NaN(), math.NaN()),
4021 },
4022 {
4023 name: "f64x2",
4024 shape: wazeroir.ShapeF64x2,
4025 x1: f64x2(math.Inf(1), math.Inf(-1)),
4026 x2: f64x2(math.NaN(), math.NaN()),
4027 exp: f64x2(math.NaN(), math.NaN()),
4028 },
4029 }
4030
4031 for _, tc := range tests {
4032 tc := tc
4033 t.Run(tc.name, func(t *testing.T) {
4034 env := newCompilerEnvironment()
4035 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
4036 &wazeroir.CompilationResult{HasMemory: true})
4037
4038 err := compiler.compilePreamble()
4039 require.NoError(t, err)
4040
4041 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
4042 require.NoError(t, err)
4043
4044 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
4045 require.NoError(t, err)
4046
4047 err = compiler.compileV128Min(operationPtr(wazeroir.NewOperationV128Min(tc.shape, tc.signed)))
4048 require.NoError(t, err)
4049
4050 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
4051 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
4052
4053 err = compiler.compileReturnFunction()
4054 require.NoError(t, err)
4055
4056 code := asm.CodeSegment{}
4057 defer func() { require.NoError(t, code.Unmap()) }()
4058
4059
4060 _, err = compiler.compile(code.NextCodeSection())
4061 require.NoError(t, err)
4062 env.exec(code.Bytes())
4063
4064 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
4065
4066 lo, hi := env.stackTopAsV128()
4067 switch tc.shape {
4068 case wazeroir.ShapeF64x2:
4069 for _, vs := range [][2]float64{
4070 {math.Float64frombits(lo), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8]))},
4071 {math.Float64frombits(hi), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:]))},
4072 } {
4073 actual, exp := vs[0], vs[1]
4074 if math.IsNaN(exp) {
4075 require.True(t, math.IsNaN(actual))
4076 } else {
4077 require.Equal(t, math.Float64bits(exp), math.Float64bits(actual))
4078 }
4079 }
4080 case wazeroir.ShapeF32x4:
4081 for _, vs := range [][2]float32{
4082 {math.Float32frombits(uint32(lo)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4]))},
4083 {math.Float32frombits(uint32(lo >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8]))},
4084 {math.Float32frombits(uint32(hi)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12]))},
4085 {math.Float32frombits(uint32(hi >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:]))},
4086 } {
4087 actual, exp := vs[0], vs[1]
4088 if math.IsNaN(float64(exp)) {
4089 require.True(t, math.IsNaN(float64(actual)))
4090 } else {
4091 require.Equal(t, math.Float32bits(exp), math.Float32bits(actual))
4092 }
4093 }
4094 default:
4095 var actual [16]byte
4096 binary.LittleEndian.PutUint64(actual[:8], lo)
4097 binary.LittleEndian.PutUint64(actual[8:], hi)
4098 require.Equal(t, tc.exp, actual)
4099 }
4100 })
4101 }
4102 }
4103
4104 func TestCompiler_compileV128Max(t *testing.T) {
4105 tests := []struct {
4106 name string
4107 shape wazeroir.Shape
4108 signed bool
4109 x1, x2, exp [16]byte
4110 }{
4111 {
4112 name: "i8x16s",
4113 shape: wazeroir.ShapeI8x16,
4114 signed: true,
4115 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2},
4116 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)},
4117 exp: [16]byte{0: 123, 5: 0, 15: 2},
4118 },
4119 {
4120 name: "i8x16u",
4121 shape: wazeroir.ShapeI8x16,
4122 signed: false,
4123 x1: [16]byte{0: 123, 5: i8ToU8(-1), 15: 2},
4124 x2: [16]byte{0: 1, 5: 0, 15: i8ToU8(-1)},
4125 exp: [16]byte{0: 123, 5: i8ToU8(-1), 15: i8ToU8(-1)},
4126 },
4127 {
4128 name: "i16x8s",
4129 shape: wazeroir.ShapeI16x8,
4130 signed: true,
4131 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1),
4132 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1),
4133 exp: i16x8(1123, 123, 123, 3, 1, 6, 5, 1),
4134 },
4135 {
4136 name: "i16x8u",
4137 shape: wazeroir.ShapeI16x8,
4138 signed: false,
4139 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), 1),
4140 x2: i16x8(0, 123, i16ToU16(-123), 3, 1, 4, 5, 1),
4141 exp: i16x8(1123, 123, i16ToU16(-123), 3, 1, 6, i16ToU16(-123), 1),
4142 },
4143 {
4144 name: "i32x4s",
4145 shape: wazeroir.ShapeI32x4,
4146 signed: true,
4147 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)),
4148 x2: i32x4(123, 5, 1, 0),
4149 exp: i32x4(123, 5, 1, 0),
4150 },
4151 {
4152 name: "i32x4u",
4153 shape: wazeroir.ShapeI32x4,
4154 signed: false,
4155 x1: i32x4(i32ToU32(-123), 0, 1, i32ToU32(math.MinInt32)),
4156 x2: i32x4(123, 5, 1, 0),
4157 exp: i32x4(i32ToU32(-123), 5, 1, i32ToU32(math.MinInt32)),
4158 },
4159 {
4160 name: "f32x4",
4161 shape: wazeroir.ShapeF32x4,
4162 x1: f32x4(float32(math.NaN()), -123.12, 2.3, float32(math.Inf(1))),
4163 x2: f32x4(5.5, 123.12, 5.0, float32(math.Inf(-1))),
4164 exp: f32x4(float32(math.NaN()), 123.12, 5.0, float32(math.Inf(1))),
4165 },
4166 {
4167 name: "f32x4",
4168 shape: wazeroir.ShapeF32x4,
4169 x1: f32x4(5.5, 123.12, -5.0, float32(math.Inf(-1))),
4170 x2: f32x4(-123.12, float32(math.NaN()), 2.3, float32(math.Inf(-1))),
4171 exp: f32x4(5.5, float32(math.NaN()), 2.3, float32(math.Inf(-1))),
4172 },
4173 {
4174 name: "f32x4",
4175 shape: wazeroir.ShapeF32x4,
4176 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4177 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4178 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4179 },
4180 {
4181 name: "f32x4",
4182 shape: wazeroir.ShapeF32x4,
4183 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4184 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4185 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4186 },
4187 {
4188 name: "f32x4",
4189 shape: wazeroir.ShapeF32x4,
4190
4191 x1: f32x4(0, 0, math.Float32frombits(1<<31), 0),
4192 x2: f32x4(math.Float32frombits(1<<31), math.Float32frombits(1<<31), math.Float32frombits(1<<31), math.Float32frombits(1<<31)),
4193 exp: f32x4(0, 0, math.Float32frombits(1<<31), 0),
4194 },
4195 {
4196 name: "f64x2",
4197 shape: wazeroir.ShapeF64x2,
4198 x1: f64x2(math.MinInt64, 0),
4199 x2: f64x2(math.MaxInt64, -12.3),
4200 exp: f64x2(math.MaxInt64, 0),
4201 },
4202 {
4203 name: "f64x2",
4204 shape: wazeroir.ShapeF64x2,
4205 x1: f64x2(math.MaxInt64, -12.3),
4206 x2: f64x2(math.MinInt64, 0),
4207 exp: f64x2(math.MaxInt64, 0),
4208 },
4209 {
4210 name: "f64x2",
4211 shape: wazeroir.ShapeF64x2,
4212 x1: f64x2(math.NaN(), -12.3),
4213 x2: f64x2(math.MinInt64, math.NaN()),
4214 exp: f64x2(math.NaN(), math.NaN()),
4215 },
4216 {
4217 name: "f64x2",
4218 shape: wazeroir.ShapeF64x2,
4219 x1: f64x2(math.MinInt64, math.NaN()),
4220 x2: f64x2(math.NaN(), -12.3),
4221 exp: f64x2(math.NaN(), math.NaN()),
4222 },
4223 {
4224 name: "f64x2",
4225 shape: wazeroir.ShapeF64x2,
4226 x1: f64x2(math.NaN(), math.NaN()),
4227 x2: f64x2(math.Inf(1), math.Inf(-1)),
4228 exp: f64x2(math.NaN(), math.NaN()),
4229 },
4230 {
4231 name: "f64x2",
4232 shape: wazeroir.ShapeF64x2,
4233 x1: f64x2(math.Inf(1), math.Inf(-1)),
4234 x2: f64x2(math.NaN(), math.NaN()),
4235 exp: f64x2(math.NaN(), math.NaN()),
4236 },
4237 {
4238 name: "f64x2",
4239 shape: wazeroir.ShapeF64x2,
4240
4241 x1: f64x2(0, 0),
4242 x2: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)),
4243 exp: f64x2(0, 0),
4244 },
4245 {
4246 name: "f64x2",
4247 shape: wazeroir.ShapeF64x2,
4248
4249 x1: f64x2(math.Copysign(0, -1), math.Copysign(0, 1)),
4250 x2: f64x2(math.Copysign(0, -1), math.Copysign(0, -1)),
4251 exp: f64x2(math.Copysign(0, -1), 0),
4252 },
4253 }
4254
4255 for _, tc := range tests {
4256 tc := tc
4257 t.Run(tc.name, func(t *testing.T) {
4258 env := newCompilerEnvironment()
4259 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
4260 &wazeroir.CompilationResult{HasMemory: true})
4261
4262 err := compiler.compilePreamble()
4263 require.NoError(t, err)
4264
4265 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
4266 require.NoError(t, err)
4267
4268 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
4269 require.NoError(t, err)
4270
4271 err = compiler.compileV128Max(operationPtr(wazeroir.NewOperationV128Max(tc.shape, tc.signed)))
4272 require.NoError(t, err)
4273
4274 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
4275 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
4276
4277 err = compiler.compileReturnFunction()
4278 require.NoError(t, err)
4279
4280 code := asm.CodeSegment{}
4281 defer func() { require.NoError(t, code.Unmap()) }()
4282
4283
4284 _, err = compiler.compile(code.NextCodeSection())
4285 require.NoError(t, err)
4286 env.exec(code.Bytes())
4287
4288 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
4289
4290 lo, hi := env.stackTopAsV128()
4291 switch tc.shape {
4292 case wazeroir.ShapeF64x2:
4293 for _, vs := range [][2]float64{
4294 {math.Float64frombits(lo), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8]))},
4295 {math.Float64frombits(hi), math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:]))},
4296 } {
4297 actual, exp := vs[0], vs[1]
4298 if math.IsNaN(exp) {
4299 require.True(t, math.IsNaN(actual))
4300 } else {
4301 require.Equal(t, math.Float64bits(exp), math.Float64bits(actual))
4302 }
4303 }
4304 case wazeroir.ShapeF32x4:
4305 for _, vs := range [][2]float32{
4306 {math.Float32frombits(uint32(lo)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4]))},
4307 {math.Float32frombits(uint32(lo >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8]))},
4308 {math.Float32frombits(uint32(hi)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12]))},
4309 {math.Float32frombits(uint32(hi >> 32)), math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:]))},
4310 } {
4311 actual, exp := vs[0], vs[1]
4312 if math.IsNaN(float64(exp)) {
4313 require.True(t, math.IsNaN(float64(actual)))
4314 } else {
4315 require.Equal(t, math.Float32bits(exp), math.Float32bits(actual))
4316 }
4317 }
4318 default:
4319 var actual [16]byte
4320 binary.LittleEndian.PutUint64(actual[:8], lo)
4321 binary.LittleEndian.PutUint64(actual[8:], hi)
4322 require.Equal(t, tc.exp, actual)
4323 }
4324 })
4325 }
4326 }
4327
4328 func TestCompiler_compileV128AddSat(t *testing.T) {
4329 tests := []struct {
4330 name string
4331 shape wazeroir.Shape
4332 signed bool
4333 x1, x2, exp [16]byte
4334 }{
4335 {
4336 name: "i8x16s",
4337 shape: wazeroir.ShapeI8x16,
4338 signed: true,
4339 x1: [16]byte{
4340 0: i8ToU8(math.MaxInt8),
4341 5: i8ToU8(-1),
4342 15: i8ToU8(math.MinInt8),
4343 },
4344 x2: [16]byte{
4345 0: 1,
4346 5: 0,
4347 15: i8ToU8(-1),
4348 },
4349 exp: [16]byte{
4350 0: i8ToU8(math.MaxInt8),
4351 5: i8ToU8(-1),
4352 15: i8ToU8(math.MinInt8),
4353 },
4354 },
4355 {
4356 name: "i8x16u",
4357 shape: wazeroir.ShapeI8x16,
4358 signed: false,
4359 x1: [16]byte{
4360 0: i8ToU8(math.MaxInt8),
4361 5: 0,
4362 15: math.MaxUint8,
4363 },
4364 x2: [16]byte{
4365 0: 1,
4366 5: i8ToU8(-1),
4367 15: 1,
4368 },
4369 exp: [16]byte{
4370 0: i8ToU8(math.MaxInt8) + 1,
4371 5: i8ToU8(-1),
4372 15: math.MaxUint8,
4373 },
4374 },
4375 {
4376 name: "i16x8s",
4377 shape: wazeroir.ShapeI16x8,
4378 signed: true,
4379 x1: i16x8(i16ToU16(math.MinInt16), 0, 123, 1, 1, 6, i16ToU16(-123), i16ToU16(math.MaxInt16)),
4380 x2: i16x8(i16ToU16(-1), 123, i16ToU16(-123), 3, 1, 4, 5, 1),
4381 exp: i16x8(i16ToU16(math.MinInt16), 123, 0, 4, 2, 10, i16ToU16(-118), i16ToU16(math.MaxInt16)),
4382 },
4383 {
4384 name: "i16x8u",
4385 shape: wazeroir.ShapeI16x8,
4386 signed: false,
4387 x1: i16x8(1123, 0, 123, 1, 1, 6, i16ToU16(-123), math.MaxUint16),
4388 x2: i16x8(0, 123, math.MaxUint16, 3, 1, 4, 0, 1),
4389 exp: i16x8(1123, 123, math.MaxUint16, 4, 2, 10, i16ToU16(-123), math.MaxUint16),
4390 },
4391 }
4392
4393 for _, tc := range tests {
4394 tc := tc
4395 t.Run(tc.name, func(t *testing.T) {
4396 env := newCompilerEnvironment()
4397 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
4398 &wazeroir.CompilationResult{HasMemory: true})
4399
4400 err := compiler.compilePreamble()
4401 require.NoError(t, err)
4402
4403 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
4404 require.NoError(t, err)
4405
4406 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
4407 require.NoError(t, err)
4408
4409 err = compiler.compileV128AddSat(operationPtr(wazeroir.NewOperationV128AddSat(tc.shape, tc.signed)))
4410 require.NoError(t, err)
4411
4412 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
4413 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
4414
4415 err = compiler.compileReturnFunction()
4416 require.NoError(t, err)
4417
4418 code := asm.CodeSegment{}
4419 defer func() { require.NoError(t, code.Unmap()) }()
4420
4421
4422 _, err = compiler.compile(code.NextCodeSection())
4423 require.NoError(t, err)
4424 env.exec(code.Bytes())
4425
4426 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
4427
4428 lo, hi := env.stackTopAsV128()
4429 var actual [16]byte
4430 binary.LittleEndian.PutUint64(actual[:8], lo)
4431 binary.LittleEndian.PutUint64(actual[8:], hi)
4432 require.Equal(t, tc.exp, actual)
4433 })
4434 }
4435 }
4436
4437 func TestCompiler_compileV128SubSat(t *testing.T) {
4438 tests := []struct {
4439 name string
4440 shape wazeroir.Shape
4441 signed bool
4442 x1, x2, exp [16]byte
4443 }{
4444 {
4445 name: "i8x16s",
4446 shape: wazeroir.ShapeI8x16,
4447 signed: true,
4448 x1: [16]byte{
4449 0: i8ToU8(math.MinInt8),
4450 5: i8ToU8(-1),
4451 15: i8ToU8(math.MaxInt8),
4452 },
4453 x2: [16]byte{
4454 0: 1,
4455 5: 0,
4456 15: i8ToU8(-1),
4457 },
4458 exp: [16]byte{
4459 0: i8ToU8(math.MinInt8),
4460 5: i8ToU8(-1),
4461 15: i8ToU8(math.MaxInt8),
4462 },
4463 },
4464 {
4465 name: "i8x16u",
4466 shape: wazeroir.ShapeI8x16,
4467 signed: false,
4468 x1: [16]byte{
4469 0: i8ToU8(math.MinInt8),
4470 5: i8ToU8(-1),
4471 15: 0,
4472 },
4473 x2: [16]byte{
4474 0: 1,
4475 5: 0,
4476 15: 1,
4477 },
4478 exp: [16]byte{
4479 0: i8ToU8(math.MinInt8) - 1,
4480 5: i8ToU8(-1),
4481 15: 0,
4482 },
4483 },
4484 {
4485 name: "i16x8s",
4486 shape: wazeroir.ShapeI16x8,
4487 signed: true,
4488 x1: i16x8(i16ToU16(math.MinInt16), 0, 123, 1, 1, 6, i16ToU16(-123), i16ToU16(math.MaxInt16)),
4489 x2: i16x8(1, 123, i16ToU16(-123), 3, 1, 4, 5, i16ToU16(-123)),
4490 exp: i16x8(i16ToU16(math.MinInt16), i16ToU16(-123), 246, i16ToU16(-2), 0, 2, i16ToU16(-128), i16ToU16(math.MaxInt16)),
4491 },
4492 {
4493 name: "i16x8u",
4494 shape: wazeroir.ShapeI16x8,
4495 signed: false,
4496 x1: i16x8(1123, 0, 123, 1, 1, 6, 200, math.MaxUint16),
4497 x2: i16x8(0, 123, math.MaxUint16, 3, 1, 4, i16ToU16(-1), 12),
4498 exp: i16x8(1123, 0, 0, 0, 0, 2, 0, math.MaxUint16-12),
4499 },
4500 }
4501
4502 for _, tc := range tests {
4503 tc := tc
4504 t.Run(tc.name, func(t *testing.T) {
4505 env := newCompilerEnvironment()
4506 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
4507 &wazeroir.CompilationResult{HasMemory: true})
4508
4509 err := compiler.compilePreamble()
4510 require.NoError(t, err)
4511
4512 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
4513 require.NoError(t, err)
4514
4515 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
4516 require.NoError(t, err)
4517
4518 err = compiler.compileV128SubSat(operationPtr(wazeroir.NewOperationV128SubSat(tc.shape, tc.signed)))
4519 require.NoError(t, err)
4520
4521 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
4522 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
4523
4524 err = compiler.compileReturnFunction()
4525 require.NoError(t, err)
4526
4527 code := asm.CodeSegment{}
4528 defer func() { require.NoError(t, code.Unmap()) }()
4529
4530
4531 _, err = compiler.compile(code.NextCodeSection())
4532 require.NoError(t, err)
4533 env.exec(code.Bytes())
4534
4535 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
4536
4537 lo, hi := env.stackTopAsV128()
4538 var actual [16]byte
4539 binary.LittleEndian.PutUint64(actual[:8], lo)
4540 binary.LittleEndian.PutUint64(actual[8:], hi)
4541 require.Equal(t, tc.exp, actual)
4542 })
4543 }
4544 }
4545
4546 func TestCompiler_compileV128Popcnt(t *testing.T) {
4547 tests := []struct {
4548 name string
4549 v, exp [16]byte
4550 }{
4551 {
4552 name: "ones",
4553 v: [16]byte{
4554 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7,
4555 0, 1 << 2, 0, 1 << 4, 0, 1 << 6, 0, 0,
4556 },
4557 exp: [16]byte{
4558 1, 1, 1, 1, 1, 1, 1, 1,
4559 0, 1, 0, 1, 0, 1, 0, 0,
4560 },
4561 },
4562 {
4563 name: "mix",
4564 v: [16]byte{
4565 0b1, 0b11, 0b111, 0b1111, 0b11111, 0b111111, 0b1111111, 0b11111111,
4566 0b10000001, 0b10000010, 0b10000100, 0b10001000, 0b10010000, 0b10100000, 0b11000000, 0,
4567 },
4568 exp: [16]byte{
4569 1, 2, 3, 4, 5, 6, 7, 8,
4570 2, 2, 2, 2, 2, 2, 2, 0,
4571 },
4572 },
4573 }
4574
4575 for _, tc := range tests {
4576 tc := tc
4577 t.Run(tc.name, func(t *testing.T) {
4578 env := newCompilerEnvironment()
4579 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
4580 &wazeroir.CompilationResult{HasMemory: true})
4581
4582 err := compiler.compilePreamble()
4583 require.NoError(t, err)
4584
4585 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
4586 require.NoError(t, err)
4587
4588 err = compiler.compileV128Popcnt(operationPtr(wazeroir.NewOperationV128Popcnt(0)))
4589 require.NoError(t, err)
4590
4591 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
4592 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
4593
4594 err = compiler.compileReturnFunction()
4595 require.NoError(t, err)
4596
4597 code := asm.CodeSegment{}
4598 defer func() { require.NoError(t, code.Unmap()) }()
4599
4600
4601 _, err = compiler.compile(code.NextCodeSection())
4602 require.NoError(t, err)
4603 env.exec(code.Bytes())
4604
4605 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
4606
4607 lo, hi := env.stackTopAsV128()
4608 var actual [16]byte
4609 binary.LittleEndian.PutUint64(actual[:8], lo)
4610 binary.LittleEndian.PutUint64(actual[8:], hi)
4611 require.Equal(t, tc.exp, actual)
4612 })
4613 }
4614 }
4615
4616 func TestCompiler_compileV128Round(t *testing.T) {
4617 tests := []struct {
4618 name string
4619 shape wazeroir.Shape
4620 kind wazeroir.OperationKind
4621 v [16]byte
4622 }{
4623 {
4624 name: "f32 ceil",
4625 shape: wazeroir.ShapeF32x4,
4626 kind: wazeroir.OperationKindV128Ceil,
4627 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4628 },
4629 {
4630 name: "f32 ceil",
4631 shape: wazeroir.ShapeF32x4,
4632 kind: wazeroir.OperationKindV128Ceil,
4633 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))),
4634 },
4635 {
4636 name: "f64 ceil",
4637 shape: wazeroir.ShapeF64x2,
4638 kind: wazeroir.OperationKindV128Ceil,
4639 v: f64x2(1.231, -123.12313),
4640 },
4641 {
4642 name: "f64 ceil",
4643 shape: wazeroir.ShapeF64x2,
4644 kind: wazeroir.OperationKindV128Ceil,
4645 v: f64x2(math.Inf(1), math.NaN()),
4646 },
4647 {
4648 name: "f64 ceil",
4649 shape: wazeroir.ShapeF64x2,
4650 kind: wazeroir.OperationKindV128Ceil,
4651 v: f64x2(math.Inf(-1), math.Pi),
4652 },
4653 {
4654 name: "f32 floor",
4655 shape: wazeroir.ShapeF32x4,
4656 kind: wazeroir.OperationKindV128Floor,
4657 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4658 },
4659 {
4660 name: "f32 floor",
4661 shape: wazeroir.ShapeF32x4,
4662 kind: wazeroir.OperationKindV128Floor,
4663 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))),
4664 },
4665 {
4666 name: "f64 floor",
4667 shape: wazeroir.ShapeF64x2,
4668 kind: wazeroir.OperationKindV128Floor,
4669 v: f64x2(1.231, -123.12313),
4670 },
4671 {
4672 name: "f64 floor",
4673 shape: wazeroir.ShapeF64x2,
4674 kind: wazeroir.OperationKindV128Floor,
4675 v: f64x2(math.Inf(1), math.NaN()),
4676 },
4677 {
4678 name: "f64 floor",
4679 shape: wazeroir.ShapeF64x2,
4680 kind: wazeroir.OperationKindV128Floor,
4681 v: f64x2(math.Inf(-1), math.Pi),
4682 },
4683 {
4684 name: "f32 trunc",
4685 shape: wazeroir.ShapeF32x4,
4686 kind: wazeroir.OperationKindV128Trunc,
4687 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4688 },
4689 {
4690 name: "f32 trunc",
4691 shape: wazeroir.ShapeF32x4,
4692 kind: wazeroir.OperationKindV128Trunc,
4693 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))),
4694 },
4695 {
4696 name: "f64 trunc",
4697 shape: wazeroir.ShapeF64x2,
4698 kind: wazeroir.OperationKindV128Trunc,
4699 v: f64x2(1.231, -123.12313),
4700 },
4701 {
4702 name: "f64 trunc",
4703 shape: wazeroir.ShapeF64x2,
4704 kind: wazeroir.OperationKindV128Trunc,
4705 v: f64x2(math.Inf(1), math.NaN()),
4706 },
4707 {
4708 name: "f64 trunc",
4709 shape: wazeroir.ShapeF64x2,
4710 kind: wazeroir.OperationKindV128Trunc,
4711 v: f64x2(math.Inf(-1), math.Pi),
4712 },
4713 {
4714 name: "f32 nearest",
4715 shape: wazeroir.ShapeF32x4,
4716 kind: wazeroir.OperationKindV128Nearest,
4717 v: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4718 },
4719 {
4720 name: "f32 nearest",
4721 shape: wazeroir.ShapeF32x4,
4722 kind: wazeroir.OperationKindV128Nearest,
4723 v: f32x4(math.Pi, -1231231.123, float32(math.NaN()), float32(math.Inf(-1))),
4724 },
4725 {
4726 name: "f64 nearest",
4727 shape: wazeroir.ShapeF64x2,
4728 kind: wazeroir.OperationKindV128Nearest,
4729 v: f64x2(1.231, -123.12313),
4730 },
4731 {
4732 name: "f64 nearest",
4733 shape: wazeroir.ShapeF64x2,
4734 kind: wazeroir.OperationKindV128Nearest,
4735 v: f64x2(math.Inf(1), math.NaN()),
4736 },
4737 {
4738 name: "f64 nearest",
4739 shape: wazeroir.ShapeF64x2,
4740 kind: wazeroir.OperationKindV128Nearest,
4741 v: f64x2(math.Inf(-1), math.Pi),
4742 },
4743 }
4744
4745 for _, tc := range tests {
4746 tc := tc
4747 t.Run(tc.name, func(t *testing.T) {
4748 env := newCompilerEnvironment()
4749 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
4750 &wazeroir.CompilationResult{HasMemory: true})
4751
4752 err := compiler.compilePreamble()
4753 require.NoError(t, err)
4754
4755 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
4756 require.NoError(t, err)
4757
4758 is32bit := tc.shape == wazeroir.ShapeF32x4
4759 switch tc.kind {
4760 case wazeroir.OperationKindV128Ceil:
4761 err = compiler.compileV128Ceil(operationPtr(wazeroir.NewOperationV128Ceil(tc.shape)))
4762 case wazeroir.OperationKindV128Floor:
4763 err = compiler.compileV128Floor(operationPtr(wazeroir.NewOperationV128Floor(tc.shape)))
4764 case wazeroir.OperationKindV128Trunc:
4765 err = compiler.compileV128Trunc(operationPtr(wazeroir.NewOperationV128Trunc(tc.shape)))
4766 case wazeroir.OperationKindV128Nearest:
4767 err = compiler.compileV128Nearest(operationPtr(wazeroir.NewOperationV128Nearest(tc.shape)))
4768 }
4769 require.NoError(t, err)
4770
4771 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
4772 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
4773
4774 err = compiler.compileReturnFunction()
4775 require.NoError(t, err)
4776
4777 code := asm.CodeSegment{}
4778 defer func() { require.NoError(t, code.Unmap()) }()
4779
4780
4781 _, err = compiler.compile(code.NextCodeSection())
4782 require.NoError(t, err)
4783 env.exec(code.Bytes())
4784
4785 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
4786
4787 lo, hi := env.stackTopAsV128()
4788
4789 if is32bit {
4790 actualFs := [4]float32{
4791 math.Float32frombits(uint32(lo)),
4792 math.Float32frombits(uint32(lo >> 32)),
4793 math.Float32frombits(uint32(hi)),
4794 math.Float32frombits(uint32(hi >> 32)),
4795 }
4796 f1Original, f2Original, f3Original, f4Original := math.Float32frombits(binary.LittleEndian.Uint32(tc.v[:4])),
4797 math.Float32frombits(binary.LittleEndian.Uint32(tc.v[4:8])),
4798 math.Float32frombits(binary.LittleEndian.Uint32(tc.v[8:12])),
4799 math.Float32frombits(binary.LittleEndian.Uint32(tc.v[12:]))
4800
4801 var expFs [4]float32
4802 switch tc.kind {
4803 case wazeroir.OperationKindV128Ceil:
4804 expFs[0] = float32(math.Ceil(float64(f1Original)))
4805 expFs[1] = float32(math.Ceil(float64(f2Original)))
4806 expFs[2] = float32(math.Ceil(float64(f3Original)))
4807 expFs[3] = float32(math.Ceil(float64(f4Original)))
4808 case wazeroir.OperationKindV128Floor:
4809 expFs[0] = float32(math.Floor(float64(f1Original)))
4810 expFs[1] = float32(math.Floor(float64(f2Original)))
4811 expFs[2] = float32(math.Floor(float64(f3Original)))
4812 expFs[3] = float32(math.Floor(float64(f4Original)))
4813 case wazeroir.OperationKindV128Trunc:
4814 expFs[0] = float32(math.Trunc(float64(f1Original)))
4815 expFs[1] = float32(math.Trunc(float64(f2Original)))
4816 expFs[2] = float32(math.Trunc(float64(f3Original)))
4817 expFs[3] = float32(math.Trunc(float64(f4Original)))
4818 case wazeroir.OperationKindV128Nearest:
4819 expFs[0] = moremath.WasmCompatNearestF32(f1Original)
4820 expFs[1] = moremath.WasmCompatNearestF32(f2Original)
4821 expFs[2] = moremath.WasmCompatNearestF32(f3Original)
4822 expFs[3] = moremath.WasmCompatNearestF32(f4Original)
4823 }
4824
4825 for i := range expFs {
4826 exp, actual := expFs[i], actualFs[i]
4827 if math.IsNaN(float64(exp)) {
4828 require.True(t, math.IsNaN(float64(actual)))
4829 } else {
4830 require.Equal(t, exp, actual)
4831 }
4832 }
4833 } else {
4834 actualFs := [2]float64{math.Float64frombits(lo), math.Float64frombits(hi)}
4835 f1Original, f2Original := math.Float64frombits(binary.LittleEndian.Uint64(tc.v[:8])), math.Float64frombits(binary.LittleEndian.Uint64(tc.v[8:]))
4836
4837 var expFs [2]float64
4838 switch tc.kind {
4839 case wazeroir.OperationKindV128Ceil:
4840 expFs[0] = math.Ceil(f1Original)
4841 expFs[1] = math.Ceil(f2Original)
4842 case wazeroir.OperationKindV128Floor:
4843 expFs[0] = math.Floor(f1Original)
4844 expFs[1] = math.Floor(f2Original)
4845 case wazeroir.OperationKindV128Trunc:
4846 expFs[0] = math.Trunc(f1Original)
4847 expFs[1] = math.Trunc(f2Original)
4848 case wazeroir.OperationKindV128Nearest:
4849 expFs[0] = moremath.WasmCompatNearestF64(f1Original)
4850 expFs[1] = moremath.WasmCompatNearestF64(f2Original)
4851 }
4852
4853 for i := range expFs {
4854 exp, actual := expFs[i], actualFs[i]
4855 if math.IsNaN(exp) {
4856 require.True(t, math.IsNaN(actual))
4857 } else {
4858 require.Equal(t, exp, actual)
4859 }
4860 }
4861 }
4862 })
4863 }
4864 }
4865
4866 func TestCompiler_compileV128_Pmax_Pmin(t *testing.T) {
4867 tests := []struct {
4868 name string
4869 shape wazeroir.Shape
4870 kind wazeroir.OperationKind
4871 x1, x2, exp [16]byte
4872 }{
4873 {
4874 name: "f32 pmin",
4875 shape: wazeroir.ShapeF32x4,
4876 kind: wazeroir.OperationKindV128Pmin,
4877 x1: f32x4(float32(math.Inf(1)), -1.5, 1123.5, float32(math.Inf(1))),
4878 x2: f32x4(1.4, float32(math.Inf(-1)), -1231.5, float32(math.Inf(1))),
4879 exp: f32x4(1.4, float32(math.Inf(-1)), -1231.5, float32(math.Inf(1))),
4880 },
4881 {
4882 name: "f32 pmin",
4883 shape: wazeroir.ShapeF32x4,
4884 kind: wazeroir.OperationKindV128Pmin,
4885 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4886 x2: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4887 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4888 },
4889 {
4890 name: "f32 pmin",
4891 shape: wazeroir.ShapeF32x4,
4892 kind: wazeroir.OperationKindV128Pmin,
4893 x1: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4894 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4895 exp: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4896 },
4897 {
4898 name: "f32 pmin",
4899 shape: wazeroir.ShapeF32x4,
4900 kind: wazeroir.OperationKindV128Pmin,
4901 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4902 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4903 exp: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4904 },
4905 {
4906 name: "f32 pmin",
4907 shape: wazeroir.ShapeF32x4,
4908 kind: wazeroir.OperationKindV128Pmin,
4909 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4910 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4911 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4912 },
4913 {
4914 name: "f64 pmin",
4915 shape: wazeroir.ShapeF64x2,
4916 kind: wazeroir.OperationKindV128Pmin,
4917 x1: f64x2(math.Inf(1), -123123.1231),
4918 x2: f64x2(-123123.1, math.Inf(-1)),
4919 exp: f64x2(-123123.1, math.Inf(-1)),
4920 },
4921 {
4922 name: "f64 pmin",
4923 shape: wazeroir.ShapeF64x2,
4924 kind: wazeroir.OperationKindV128Pmin,
4925 x1: f64x2(math.NaN(), math.NaN()),
4926 x2: f64x2(-123123.1, 1.0),
4927 exp: f64x2(math.NaN(), math.NaN()),
4928 },
4929 {
4930 name: "f64 pmin",
4931 shape: wazeroir.ShapeF64x2,
4932 kind: wazeroir.OperationKindV128Pmin,
4933 x1: f64x2(-123123.1, 1.0),
4934 x2: f64x2(math.NaN(), math.NaN()),
4935 exp: f64x2(-123123.1, 1.0),
4936 },
4937 {
4938 name: "f64 pmin",
4939 shape: wazeroir.ShapeF64x2,
4940 kind: wazeroir.OperationKindV128Pmin,
4941 x1: f64x2(math.NaN(), math.NaN()),
4942 x2: f64x2(math.Inf(1), math.Inf(-1)),
4943 exp: f64x2(math.NaN(), math.NaN()),
4944 },
4945 {
4946 name: "f64 pmin",
4947 shape: wazeroir.ShapeF64x2,
4948 kind: wazeroir.OperationKindV128Pmin,
4949 x1: f64x2(math.Inf(1), math.Inf(-1)),
4950 x2: f64x2(math.NaN(), math.NaN()),
4951 exp: f64x2(math.Inf(1), math.Inf(-1)),
4952 },
4953 {
4954 name: "f32 pmax",
4955 shape: wazeroir.ShapeF32x4,
4956 kind: wazeroir.OperationKindV128Pmax,
4957 x1: f32x4(float32(math.Inf(1)), -1.5, 1123.5, float32(math.Inf(1))),
4958 x2: f32x4(1.4, float32(math.Inf(-1)), -1231.5, float32(math.Inf(1))),
4959 exp: f32x4(float32(math.Inf(1)), -1.5, 1123.5, float32(math.Inf(1))),
4960 },
4961 {
4962 name: "f32 pmax",
4963 shape: wazeroir.ShapeF32x4,
4964 kind: wazeroir.OperationKindV128Pmax,
4965 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4966 x2: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4967 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4968 },
4969 {
4970 name: "f32 pmax",
4971 shape: wazeroir.ShapeF32x4,
4972 kind: wazeroir.OperationKindV128Pmax,
4973 x1: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4974 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4975 exp: f32x4(1.4, -1.5, 1.5, float32(math.Inf(1))),
4976 },
4977 {
4978 name: "f32 pmax",
4979 shape: wazeroir.ShapeF32x4,
4980 kind: wazeroir.OperationKindV128Pmax,
4981 x1: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4982 x2: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4983 exp: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4984 },
4985 {
4986 name: "f32 pmax",
4987 shape: wazeroir.ShapeF32x4,
4988 kind: wazeroir.OperationKindV128Pmax,
4989 x1: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4990 x2: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(math.Inf(-1)), float32(math.Inf(1))),
4991 exp: f32x4(float32(math.NaN()), float32(math.NaN()), float32(math.NaN()), float32(math.NaN())),
4992 },
4993 {
4994 name: "f64 pmax",
4995 shape: wazeroir.ShapeF64x2,
4996 kind: wazeroir.OperationKindV128Pmax,
4997 x1: f64x2(math.Inf(1), -123123.1231),
4998 x2: f64x2(-123123.1, math.Inf(-1)),
4999 exp: f64x2(math.Inf(1), -123123.1231),
5000 },
5001 {
5002 name: "f64 pmax",
5003 shape: wazeroir.ShapeF64x2,
5004 kind: wazeroir.OperationKindV128Pmax,
5005 x1: f64x2(math.NaN(), math.NaN()),
5006 x2: f64x2(-123123.1, 1.0),
5007 exp: f64x2(math.NaN(), math.NaN()),
5008 },
5009 {
5010 name: "f64 pmax",
5011 shape: wazeroir.ShapeF64x2,
5012 kind: wazeroir.OperationKindV128Pmax,
5013 x1: f64x2(-123123.1, 1.0),
5014 x2: f64x2(math.NaN(), math.NaN()),
5015 exp: f64x2(-123123.1, 1.0),
5016 },
5017 {
5018 name: "f64 pmax",
5019 shape: wazeroir.ShapeF64x2,
5020 kind: wazeroir.OperationKindV128Pmax,
5021 x1: f64x2(math.NaN(), math.NaN()),
5022 x2: f64x2(math.Inf(1), math.Inf(-1)),
5023 exp: f64x2(math.NaN(), math.NaN()),
5024 },
5025 {
5026 name: "f64 pmax",
5027 shape: wazeroir.ShapeF64x2,
5028 kind: wazeroir.OperationKindV128Pmax,
5029 x1: f64x2(math.Inf(1), math.Inf(-1)),
5030 x2: f64x2(math.NaN(), math.NaN()),
5031 exp: f64x2(math.Inf(1), math.Inf(-1)),
5032 },
5033 }
5034
5035 for _, tc := range tests {
5036 tc := tc
5037 t.Run(tc.name, func(t *testing.T) {
5038 env := newCompilerEnvironment()
5039 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
5040 &wazeroir.CompilationResult{HasMemory: true})
5041
5042 err := compiler.compilePreamble()
5043 require.NoError(t, err)
5044
5045 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
5046 require.NoError(t, err)
5047
5048 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
5049 require.NoError(t, err)
5050
5051 is32bit := tc.shape == wazeroir.ShapeF32x4
5052 switch tc.kind {
5053 case wazeroir.OperationKindV128Pmin:
5054 err = compiler.compileV128Pmin(operationPtr(wazeroir.NewOperationV128Pmin(tc.shape)))
5055 case wazeroir.OperationKindV128Pmax:
5056 err = compiler.compileV128Pmax(operationPtr(wazeroir.NewOperationV128Pmax(tc.shape)))
5057 }
5058 require.NoError(t, err)
5059
5060 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
5061 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
5062
5063 err = compiler.compileReturnFunction()
5064 require.NoError(t, err)
5065
5066 code := asm.CodeSegment{}
5067 defer func() { require.NoError(t, code.Unmap()) }()
5068
5069
5070 _, err = compiler.compile(code.NextCodeSection())
5071 require.NoError(t, err)
5072 env.exec(code.Bytes())
5073
5074 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
5075
5076 lo, hi := env.stackTopAsV128()
5077
5078 if is32bit {
5079 actualFs := [4]float32{
5080 math.Float32frombits(uint32(lo)),
5081 math.Float32frombits(uint32(lo >> 32)),
5082 math.Float32frombits(uint32(hi)),
5083 math.Float32frombits(uint32(hi >> 32)),
5084 }
5085 expFs := [4]float32{
5086 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4])),
5087 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8])),
5088 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12])),
5089 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:])),
5090 }
5091 for i := range expFs {
5092 exp, actual := expFs[i], actualFs[i]
5093 if math.IsNaN(float64(exp)) {
5094 require.True(t, math.IsNaN(float64(actual)))
5095 } else {
5096 require.Equal(t, exp, actual)
5097 }
5098 }
5099 } else {
5100 actualFs := [2]float64{
5101 math.Float64frombits(lo), math.Float64frombits(hi),
5102 }
5103 expFs := [2]float64{
5104 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8])),
5105 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:])),
5106 }
5107 for i := range expFs {
5108 exp, actual := expFs[i], actualFs[i]
5109 if math.IsNaN(exp) {
5110 require.True(t, math.IsNaN(actual))
5111 } else {
5112 require.Equal(t, exp, actual)
5113 }
5114 }
5115 }
5116 })
5117 }
5118 }
5119
5120 func TestCompiler_compileV128ExtMul(t *testing.T) {
5121 tests := []struct {
5122 name string
5123 shape wazeroir.Shape
5124 signed, useLow bool
5125 x1, x2, exp [16]byte
5126 }{
5127 {
5128 name: "i8x16s low",
5129 shape: wazeroir.ShapeI8x16,
5130 signed: true,
5131 useLow: true,
5132 x1: [16]byte{}, x2: [16]byte{},
5133 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5134 },
5135 {
5136 name: "i8x16s low",
5137 shape: wazeroir.ShapeI8x16,
5138 signed: true,
5139 useLow: true,
5140 x1: [16]byte{
5141 255, 255, 255, 255, 255, 255, 255, 255,
5142 0, 0, 0, 0, 0, 0, 0, 0,
5143 },
5144 x2: [16]byte{
5145 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5146 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5147 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5148 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5149 },
5150 exp: i16x8(128, 128, 128, 128, 128, 128, 128, 128),
5151 },
5152 {
5153 name: "i8x16s low",
5154 shape: wazeroir.ShapeI8x16,
5155 signed: true,
5156 useLow: true,
5157 x1: [16]byte{
5158 255, 255, 255, 255, 255, 255, 255, 255,
5159 0, 0, 0, 0, 0, 0, 0, 0,
5160 },
5161 x2: [16]byte{
5162 255, 255, 255, 255, 255, 255, 255, 255,
5163 0, 0, 0, 0, 0, 0, 0, 0,
5164 },
5165 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
5166 },
5167 {
5168 name: "i8x16s low",
5169 shape: wazeroir.ShapeI8x16,
5170 signed: true,
5171 useLow: true,
5172 x1: [16]byte{
5173 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5174 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5175 0, 0, 0, 0, 0, 0, 0, 0,
5176 },
5177 x2: [16]byte{
5178 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5179 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5180 0, 0, 0, 0, 0, 0, 0, 0,
5181 },
5182 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384),
5183 },
5184 {
5185 name: "i8x16s hi",
5186 shape: wazeroir.ShapeI8x16,
5187 signed: true,
5188 useLow: false,
5189 x1: [16]byte{}, x2: [16]byte{},
5190 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5191 },
5192 {
5193 name: "i8x16s hi",
5194 shape: wazeroir.ShapeI8x16,
5195 signed: true,
5196 useLow: false,
5197 x1: [16]byte{
5198 0, 0, 0, 0, 0, 0, 0, 0,
5199 255, 255, 255, 255, 255, 255, 255, 255,
5200 },
5201 x2: [16]byte{
5202 0, 0, 0, 0, 0, 0, 0, 0,
5203 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5204 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5205 },
5206 exp: i16x8(128, 128, 128, 128, 128, 128, 128, 128),
5207 },
5208 {
5209 name: "i8x16s hi",
5210 shape: wazeroir.ShapeI8x16,
5211 signed: true,
5212 useLow: false,
5213 x1: [16]byte{
5214 0, 0, 0, 0, 0, 0, 0, 0,
5215 255, 255, 255, 255, 255, 255, 255, 255,
5216 },
5217 x2: [16]byte{
5218 0, 0, 0, 0, 0, 0, 0, 0,
5219 255, 255, 255, 255, 255, 255, 255, 255,
5220 },
5221 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
5222 },
5223 {
5224 name: "i8x16s hi",
5225 shape: wazeroir.ShapeI8x16,
5226 signed: true,
5227 useLow: false,
5228 x1: [16]byte{
5229 0, 0, 0, 0, 0, 0, 0, 0,
5230 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5231 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5232 },
5233 x2: [16]byte{
5234 0, 0, 0, 0, 0, 0, 0, 0,
5235 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5236 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5237 },
5238 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384),
5239 },
5240 {
5241 name: "i8x16u low",
5242 shape: wazeroir.ShapeI8x16,
5243 signed: false,
5244 useLow: true,
5245 x1: [16]byte{}, x2: [16]byte{},
5246 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5247 },
5248 {
5249 name: "i8x16u low",
5250 shape: wazeroir.ShapeI8x16,
5251 signed: false,
5252 useLow: true,
5253 x1: [16]byte{
5254 255, 255, 255, 255, 255, 255, 255, 255,
5255 0, 0, 0, 0, 0, 0, 0, 0,
5256 },
5257 x2: [16]byte{
5258 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5259 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5260 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5261 0, 0, 0, 0,
5262 },
5263 exp: i16x8(32640, 32640, 32640, 32640, 32640, 32640, 32640, 32640),
5264 },
5265 {
5266 name: "i8x16u low",
5267 shape: wazeroir.ShapeI8x16,
5268 signed: false,
5269 useLow: true,
5270 x1: [16]byte{
5271 255, 255, 255, 255, 255, 255, 255, 255,
5272 0, 0, 0, 0, 0, 0, 0, 0,
5273 },
5274 x2: [16]byte{
5275 255, 255, 255, 255, 255, 255, 255, 255,
5276 0, 0, 0, 0, 0, 0, 0, 0,
5277 },
5278 exp: i16x8(i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511),
5279 i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511)),
5280 },
5281 {
5282 name: "i8x16u low",
5283 shape: wazeroir.ShapeI8x16,
5284 signed: false,
5285 useLow: true,
5286 x1: [16]byte{
5287 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5288 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5289 0, 0, 0, 0, 0, 0, 0, 0,
5290 },
5291 x2: [16]byte{
5292 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5293 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5294 0, 0, 0, 0, 0, 0, 0, 0,
5295 },
5296 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384),
5297 },
5298 {
5299 name: "i8x16u hi",
5300 shape: wazeroir.ShapeI8x16,
5301 signed: false,
5302 useLow: false,
5303 x1: [16]byte{}, x2: [16]byte{},
5304 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5305 },
5306 {
5307 name: "i8x16u hi",
5308 shape: wazeroir.ShapeI8x16,
5309 signed: false,
5310 useLow: false,
5311 x1: [16]byte{
5312 0, 0, 0, 0, 0, 0, 0, 0,
5313 255, 255, 255, 255, 255, 255, 255, 255,
5314 },
5315 x2: [16]byte{
5316 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5317 0, 0, 0, 0,
5318 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5319 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5320 },
5321 exp: i16x8(32640, 32640, 32640, 32640, 32640, 32640, 32640, 32640),
5322 },
5323 {
5324 name: "i8x16u hi",
5325 shape: wazeroir.ShapeI8x16,
5326 signed: false,
5327 useLow: false,
5328 x1: [16]byte{
5329 0, 0, 0, 0, 0, 0, 0, 0,
5330 255, 255, 255, 255, 255, 255, 255, 255,
5331 },
5332 x2: [16]byte{
5333 0, 0, 0, 0, 0, 0, 0, 0,
5334 255, 255, 255, 255, 255, 255, 255, 255,
5335 },
5336 exp: i16x8(i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511),
5337 i16ToU16(-511), i16ToU16(-511), i16ToU16(-511), i16ToU16(-511)),
5338 },
5339 {
5340 name: "i8x16u hi",
5341 shape: wazeroir.ShapeI8x16,
5342 signed: false,
5343 useLow: false,
5344 x1: [16]byte{
5345 0, 0, 0, 0, 0, 0, 0, 0,
5346 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5347 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5348 },
5349 x2: [16]byte{
5350 0, 0, 0, 0, 0, 0, 0, 0,
5351 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5352 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
5353 },
5354 exp: i16x8(16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384),
5355 },
5356 {
5357 name: "i16x8s lo",
5358 shape: wazeroir.ShapeI16x8,
5359 signed: true,
5360 useLow: true,
5361 x1: [16]byte{},
5362 x2: [16]byte{},
5363 exp: [16]byte{},
5364 },
5365 {
5366 name: "i16x8s lo",
5367 shape: wazeroir.ShapeI16x8,
5368 signed: true,
5369 useLow: true,
5370 x1: i16x8(
5371 16383, 16383, 16383, 16383,
5372 0, 0, 1, 0,
5373 ),
5374 x2: i16x8(
5375 16384, 16384, 16384, 16384,
5376 0, 0, 1, 0,
5377 ),
5378 exp: i32x4(268419072, 268419072, 268419072, 268419072),
5379 },
5380 {
5381 name: "i16x8s lo",
5382 shape: wazeroir.ShapeI16x8,
5383 signed: true,
5384 useLow: true,
5385 x1: i16x8(
5386 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
5387 0, 0, 1, 0,
5388 ),
5389 x2: i16x8(
5390 i16ToU16(-32767), 0, i16ToU16(-32767), 0,
5391 0, 0, 1, 0,
5392 ),
5393 exp: i32x4(1073709056, 0, 1073709056, 0),
5394 },
5395 {
5396 name: "i16x8s lo",
5397 shape: wazeroir.ShapeI16x8,
5398 signed: true,
5399 useLow: true,
5400 x1: i16x8(
5401 65535, 65535, 65535, 65535,
5402 0, 0, 1, 0,
5403 ),
5404 x2: i16x8(
5405 65535, 0, 65535, 0,
5406 0, 0, 1, 0,
5407 ),
5408 exp: i32x4(1, 0, 1, 0),
5409 },
5410 {
5411 name: "i16x8s hi",
5412 shape: wazeroir.ShapeI16x8,
5413 signed: true,
5414 useLow: false,
5415 x1: [16]byte{},
5416 x2: [16]byte{},
5417 exp: [16]byte{},
5418 },
5419 {
5420 name: "i16x8s hi",
5421 shape: wazeroir.ShapeI16x8,
5422 signed: true,
5423 useLow: false,
5424 x1: i16x8(
5425 0, 0, 1, 0,
5426 16383, 16383, 16383, 16383,
5427 ),
5428 x2: i16x8(
5429 0, 0, 1, 0,
5430 16384, 16384, 16384, 16384,
5431 ),
5432 exp: i32x4(268419072, 268419072, 268419072, 268419072),
5433 },
5434 {
5435 name: "i16x8s hi",
5436 shape: wazeroir.ShapeI16x8,
5437 signed: true,
5438 useLow: false,
5439 x1: i16x8(
5440 0, 0, 1, 0,
5441 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
5442 ),
5443 x2: i16x8(
5444 0, 0, 1, 0,
5445 i16ToU16(-32767), 0, i16ToU16(-32767), 0,
5446 ),
5447 exp: i32x4(1073709056, 0, 1073709056, 0),
5448 },
5449 {
5450 name: "i16x8s hi",
5451 shape: wazeroir.ShapeI16x8,
5452 signed: true,
5453 useLow: false,
5454 x1: i16x8(
5455 0, 0, 1, 0,
5456 65535, 65535, 65535, 65535,
5457 ),
5458 x2: i16x8(
5459 0, 0, 1, 0,
5460
5461 65535, 0, 65535, 0,
5462 ),
5463 exp: i32x4(1, 0, 1, 0),
5464 },
5465 {
5466 name: "i16x8u lo",
5467 shape: wazeroir.ShapeI16x8,
5468 signed: false,
5469 useLow: true,
5470 x1: [16]byte{},
5471 x2: [16]byte{},
5472 exp: [16]byte{},
5473 },
5474 {
5475 name: "i16x8u lo",
5476 shape: wazeroir.ShapeI16x8,
5477 signed: false,
5478 useLow: true,
5479 x1: i16x8(
5480 16383, 16383, 16383, 16383,
5481 0, 0, 1, 0,
5482 ),
5483 x2: i16x8(
5484 16384, 16384, 16384, 16384,
5485 0, 0, 1, 0,
5486 ),
5487 exp: i32x4(268419072, 268419072, 268419072, 268419072),
5488 },
5489 {
5490 name: "i16x8u lo",
5491 shape: wazeroir.ShapeI16x8,
5492 signed: false,
5493 useLow: true,
5494 x1: i16x8(
5495 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
5496 0, 0, 1, 0,
5497 ),
5498 x2: i16x8(
5499 i16ToU16(-32767), 0, i16ToU16(-32767), 0,
5500 0, 0, 1, 0,
5501 ),
5502 exp: i32x4(1073774592, 0, 1073774592, 0),
5503 },
5504 {
5505 name: "i16x8u lo",
5506 shape: wazeroir.ShapeI16x8,
5507 signed: false,
5508 useLow: true,
5509 x1: i16x8(
5510 65535, 65535, 65535, 65535,
5511 0, 0, 1, 0,
5512 ),
5513 x2: i16x8(
5514 65535, 0, 65535, 0,
5515 0, 0, 1, 0,
5516 ),
5517 exp: i32x4(i32ToU32(-131071), 0, i32ToU32(-131071), 0),
5518 },
5519 {
5520 name: "i16x8u hi",
5521 shape: wazeroir.ShapeI16x8,
5522 signed: false,
5523 useLow: false,
5524 x1: [16]byte{},
5525 x2: [16]byte{},
5526 exp: [16]byte{},
5527 },
5528 {
5529 name: "i16x8u hi",
5530 shape: wazeroir.ShapeI16x8,
5531 signed: false,
5532 useLow: false,
5533 x1: i16x8(
5534 0, 0, 1, 0,
5535 16383, 16383, 16383, 16383,
5536 ),
5537 x2: i16x8(
5538 0, 0, 1, 0,
5539 16384, 16384, 16384, 16384,
5540 ),
5541 exp: i32x4(268419072, 268419072, 268419072, 268419072),
5542 },
5543 {
5544 name: "i16x8u hi",
5545 shape: wazeroir.ShapeI16x8,
5546 signed: false,
5547 useLow: false,
5548 x1: i16x8(
5549 0, 0, 1, 0,
5550 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
5551 ),
5552 x2: i16x8(
5553 0, 0, 1, 0,
5554 i16ToU16(-32767), 0, i16ToU16(-32767), 0,
5555 ),
5556 exp: i32x4(1073774592, 0, 1073774592, 0),
5557 },
5558 {
5559 name: "i16x8u hi",
5560 shape: wazeroir.ShapeI16x8,
5561 signed: false,
5562 useLow: false,
5563 x1: i16x8(
5564 0, 0, 1, 0,
5565 65535, 65535, 65535, 65535,
5566 ),
5567 x2: i16x8(
5568 0, 0, 1, 0,
5569 65535, 0, 65535, 0,
5570 ),
5571 exp: i32x4(i32ToU32(-131071), 0, i32ToU32(-131071), 0),
5572 },
5573 {
5574 name: "i32x4s lo",
5575 shape: wazeroir.ShapeI32x4,
5576 signed: true,
5577 useLow: true,
5578 x1: [16]byte{},
5579 x2: [16]byte{},
5580 exp: [16]byte{},
5581 },
5582 {
5583 name: "i32x4s lo",
5584 shape: wazeroir.ShapeI32x4,
5585 signed: true,
5586 useLow: true,
5587 x1: i32x4(
5588 1, i32ToU32(-1),
5589 0, 0,
5590 ),
5591 x2: i32x4(
5592 i32ToU32(-1), 1,
5593 0, 0,
5594 ),
5595 exp: i64x2(i64ToU64(-1), i64ToU64(-1)),
5596 },
5597 {
5598 name: "i32x4s lo",
5599 shape: wazeroir.ShapeI32x4,
5600 signed: true,
5601 useLow: true,
5602 x1: i32x4(
5603 1073741824, 4294967295,
5604 0, 0,
5605 ),
5606 x2: i32x4(
5607 1073741824, 4294967295,
5608 0, 0,
5609 ),
5610 exp: i64x2(1152921504606846976, 1),
5611 },
5612 {
5613 name: "i32x4s hi",
5614 shape: wazeroir.ShapeI32x4,
5615 signed: true,
5616 useLow: false,
5617 x1: [16]byte{},
5618 x2: [16]byte{},
5619 exp: [16]byte{},
5620 },
5621 {
5622 name: "i32x4s hi",
5623 shape: wazeroir.ShapeI32x4,
5624 signed: true,
5625 useLow: false,
5626 x1: i32x4(
5627 0, 0,
5628 1, i32ToU32(-1),
5629 ),
5630 x2: i32x4(
5631 0, 0,
5632 i32ToU32(-1), 1,
5633 ),
5634 exp: i64x2(i64ToU64(-1), i64ToU64(-1)),
5635 },
5636 {
5637 name: "i32x4s hi",
5638 shape: wazeroir.ShapeI32x4,
5639 signed: true,
5640 useLow: false,
5641 x1: i32x4(
5642 0, 0,
5643 1073741824, 4294967295,
5644 ),
5645 x2: i32x4(
5646 0, 0,
5647 1073741824, 4294967295,
5648 ),
5649 exp: i64x2(1152921504606846976, 1),
5650 },
5651 {
5652 name: "i32x4u lo",
5653 shape: wazeroir.ShapeI32x4,
5654 signed: false,
5655 useLow: true,
5656 x1: [16]byte{},
5657 x2: [16]byte{},
5658 exp: [16]byte{},
5659 },
5660 {
5661 name: "i32x4u lo",
5662 shape: wazeroir.ShapeI32x4,
5663 signed: false,
5664 useLow: true,
5665 x1: i32x4(
5666 1, i32ToU32(-1),
5667 0, 0,
5668 ),
5669 x2: i32x4(
5670 i32ToU32(-1), 1,
5671 0, 0,
5672 ),
5673 exp: i64x2(4294967295, 4294967295),
5674 },
5675 {
5676 name: "i32x4u lo",
5677 shape: wazeroir.ShapeI32x4,
5678 signed: false,
5679 useLow: true,
5680 x1: i32x4(
5681 1073741824, 4294967295,
5682 0, 0,
5683 ),
5684 x2: i32x4(
5685 1073741824, 4294967295,
5686 0, 0,
5687 ),
5688 exp: i64x2(1152921504606846976, i64ToU64(-8589934591)),
5689 },
5690 {
5691 name: "i32x4u hi",
5692 shape: wazeroir.ShapeI32x4,
5693 signed: false,
5694 useLow: false,
5695 x1: [16]byte{},
5696 x2: [16]byte{},
5697 exp: [16]byte{},
5698 },
5699 {
5700 name: "i32x4u hi",
5701 shape: wazeroir.ShapeI32x4,
5702 signed: false,
5703 useLow: false,
5704 x1: i32x4(
5705 0, 0,
5706 1, i32ToU32(-1),
5707 ),
5708 x2: i32x4(
5709 0, 0,
5710 i32ToU32(-1), 1,
5711 ),
5712 exp: i64x2(4294967295, 4294967295),
5713 },
5714 {
5715 name: "i32x4u hi",
5716 shape: wazeroir.ShapeI32x4,
5717 signed: false,
5718 useLow: false,
5719 x1: i32x4(
5720 0, 0,
5721 1073741824, 4294967295,
5722 ),
5723 x2: i32x4(
5724 0, 0,
5725 1073741824, 4294967295,
5726 ),
5727 exp: i64x2(1152921504606846976, i64ToU64(-8589934591)),
5728 },
5729 }
5730
5731 for _, tc := range tests {
5732 tc := tc
5733 t.Run(tc.name, func(t *testing.T) {
5734 env := newCompilerEnvironment()
5735 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
5736 &wazeroir.CompilationResult{HasMemory: true})
5737
5738 err := compiler.compilePreamble()
5739 require.NoError(t, err)
5740
5741 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
5742 require.NoError(t, err)
5743
5744 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
5745 require.NoError(t, err)
5746
5747 err = compiler.compileV128ExtMul(operationPtr(wazeroir.NewOperationV128ExtMul(tc.shape, tc.signed, tc.useLow)))
5748 require.NoError(t, err)
5749
5750 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
5751 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
5752
5753 err = compiler.compileReturnFunction()
5754 require.NoError(t, err)
5755
5756 code := asm.CodeSegment{}
5757 defer func() { require.NoError(t, code.Unmap()) }()
5758
5759
5760 _, err = compiler.compile(code.NextCodeSection())
5761 require.NoError(t, err)
5762 env.exec(code.Bytes())
5763
5764 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
5765
5766 lo, hi := env.stackTopAsV128()
5767 var actual [16]byte
5768 binary.LittleEndian.PutUint64(actual[:8], lo)
5769 binary.LittleEndian.PutUint64(actual[8:], hi)
5770 require.Equal(t, tc.exp, actual)
5771 })
5772 }
5773 }
5774
5775 func TestCompiler_compileV128Extend(t *testing.T) {
5776 tests := []struct {
5777 name string
5778 shape wazeroir.Shape
5779 signed, useLow bool
5780 v, exp [16]byte
5781 }{
5782 {
5783 name: "i8x16s hi",
5784 shape: wazeroir.ShapeI8x16,
5785 signed: true,
5786 useLow: false,
5787 v: [16]byte{},
5788 exp: [16]byte{},
5789 },
5790 {
5791 name: "i8x16s hi",
5792 shape: wazeroir.ShapeI8x16,
5793 signed: true,
5794 useLow: false,
5795 v: [16]byte{
5796 0, 0, 0, 0, 0, 0, 0, 0,
5797 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5798 },
5799 exp: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)),
5800 },
5801 {
5802 name: "i8x16s hi",
5803 shape: wazeroir.ShapeI8x16,
5804 signed: true,
5805 useLow: false,
5806 v: [16]byte{
5807 0, 0, 0, 0, 0, 0, 0, 0,
5808 1, 1, 1, 1, 1, 1, 1, 1,
5809 },
5810 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
5811 },
5812 {
5813 name: "i8x16s hi",
5814 shape: wazeroir.ShapeI8x16,
5815 signed: true,
5816 useLow: false,
5817 v: [16]byte{
5818 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5819 0, 0, 0, 0, 0, 0, 0, 0,
5820 },
5821 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5822 },
5823 {
5824 name: "i8x16s lo",
5825 shape: wazeroir.ShapeI8x16,
5826 signed: true,
5827 useLow: true,
5828 v: [16]byte{},
5829 exp: [16]byte{},
5830 },
5831 {
5832 name: "i8x16s lo",
5833 shape: wazeroir.ShapeI8x16,
5834 signed: true,
5835 useLow: true,
5836 v: [16]byte{
5837 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5838 0, 0, 0, 0, 0, 0, 0, 0,
5839 },
5840 exp: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)),
5841 },
5842 {
5843 name: "i8x16s lo",
5844 shape: wazeroir.ShapeI8x16,
5845 signed: true,
5846 useLow: true,
5847 v: [16]byte{
5848 1, 1, 1, 1, 1, 1, 1, 1,
5849 0, 0, 0, 0, 0, 0, 0, 0,
5850 },
5851 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
5852 },
5853 {
5854 name: "i8x16s lo",
5855 shape: wazeroir.ShapeI8x16,
5856 signed: true,
5857 useLow: true,
5858 v: [16]byte{
5859 0, 0, 0, 0, 0, 0, 0, 0,
5860 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5861 },
5862 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5863 },
5864
5865 {
5866 name: "i8x16u hi",
5867 shape: wazeroir.ShapeI8x16,
5868 signed: false,
5869 useLow: false,
5870 v: [16]byte{},
5871 exp: [16]byte{},
5872 },
5873 {
5874 name: "i8x16u hi",
5875 shape: wazeroir.ShapeI8x16,
5876 signed: false,
5877 useLow: false,
5878 v: [16]byte{
5879 0, 0, 0, 0, 0, 0, 0, 0,
5880 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5881 },
5882 exp: i16x8(255, 255, 255, 255, 255, 255, 255, 255),
5883 },
5884 {
5885 name: "i8x16u hi",
5886 shape: wazeroir.ShapeI8x16,
5887 signed: false,
5888 useLow: false,
5889 v: [16]byte{
5890 0, 0, 0, 0, 0, 0, 0, 0,
5891 1, 1, 1, 1, 1, 1, 1, 1,
5892 },
5893 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
5894 },
5895 {
5896 name: "i8x16u hi",
5897 shape: wazeroir.ShapeI8x16,
5898 signed: false,
5899 useLow: false,
5900 v: [16]byte{
5901 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5902 0, 0, 0, 0, 0, 0, 0, 0,
5903 },
5904 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5905 },
5906 {
5907 name: "i8x16u lo",
5908 shape: wazeroir.ShapeI8x16,
5909 signed: false,
5910 useLow: true,
5911 v: [16]byte{},
5912 exp: [16]byte{},
5913 },
5914 {
5915 name: "i8x16u lo",
5916 shape: wazeroir.ShapeI8x16,
5917 signed: false,
5918 useLow: true,
5919 v: [16]byte{
5920 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5921 0, 0, 0, 0, 0, 0, 0, 0,
5922 },
5923 exp: i16x8(255, 255, 255, 255, 255, 255, 255, 255),
5924 },
5925 {
5926 name: "i8x16u lo",
5927 shape: wazeroir.ShapeI8x16,
5928 signed: false,
5929 useLow: true,
5930 v: [16]byte{
5931 1, 1, 1, 1, 1, 1, 1, 1,
5932 0, 0, 0, 0, 0, 0, 0, 0,
5933 },
5934 exp: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
5935 },
5936 {
5937 name: "i8x16u lo",
5938 shape: wazeroir.ShapeI8x16,
5939 signed: false,
5940 useLow: true,
5941 v: [16]byte{
5942 0, 0, 0, 0, 0, 0, 0, 0,
5943 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
5944 },
5945 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
5946 },
5947 {
5948 name: "i16x8s hi",
5949 shape: wazeroir.ShapeI16x8,
5950 signed: true,
5951 useLow: false,
5952 v: [16]byte{},
5953 exp: [16]byte{},
5954 },
5955 {
5956 name: "i16x8s hi",
5957 shape: wazeroir.ShapeI16x8,
5958 signed: true,
5959 useLow: false,
5960 v: i16x8(1, 1, 1, 1, 0, 0, 0, 0),
5961 exp: i32x4(0, 0, 0, 0),
5962 },
5963 {
5964 name: "i16x8s hi",
5965 shape: wazeroir.ShapeI16x8,
5966 signed: true,
5967 useLow: false,
5968 v: i16x8(0, 0, 0, 0, i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)),
5969 exp: i32x4(i32ToU32(-1), i32ToU32(-1), i32ToU32(-1), i32ToU32(-1)),
5970 },
5971 {
5972 name: "i16x8s hi",
5973 shape: wazeroir.ShapeI16x8,
5974 signed: true,
5975 useLow: false,
5976 v: i16x8(0, 0, 0, 0, 123, 0, 123, 0),
5977 exp: i32x4(123, 0, 123, 0),
5978 },
5979 {
5980 name: "i16x8s lo",
5981 shape: wazeroir.ShapeI16x8,
5982 signed: true,
5983 useLow: true,
5984 v: [16]byte{},
5985 exp: [16]byte{},
5986 },
5987 {
5988 name: "i16x8s lo",
5989 shape: wazeroir.ShapeI16x8,
5990 signed: true,
5991 useLow: true,
5992 v: i16x8(0, 0, 0, 0, 1, 1, 1, 1),
5993 exp: i32x4(0, 0, 0, 0),
5994 },
5995 {
5996 name: "i16x8s lo",
5997 shape: wazeroir.ShapeI16x8,
5998 signed: true,
5999 useLow: true,
6000 v: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 0, 0, 0, 0),
6001 exp: i32x4(i32ToU32(-1), i32ToU32(-1), i32ToU32(-1), i32ToU32(-1)),
6002 },
6003 {
6004 name: "i16x8s lo",
6005 shape: wazeroir.ShapeI16x8,
6006 signed: true,
6007 useLow: true,
6008 v: i16x8(123, 0, 123, 0, 0, 0, 0, 0),
6009 exp: i32x4(123, 0, 123, 0),
6010 },
6011 {
6012 name: "i16x8u hi",
6013 shape: wazeroir.ShapeI16x8,
6014 signed: false,
6015 useLow: false,
6016 v: [16]byte{},
6017 exp: [16]byte{},
6018 },
6019 {
6020 name: "i16x8u hi",
6021 shape: wazeroir.ShapeI16x8,
6022 signed: false,
6023 useLow: false,
6024 v: i16x8(1, 1, 1, 1, 0, 0, 0, 0),
6025 exp: i32x4(0, 0, 0, 0),
6026 },
6027 {
6028 name: "i16x8u hi",
6029 shape: wazeroir.ShapeI16x8,
6030 signed: false,
6031 useLow: false,
6032 v: i16x8(0, 0, 0, 0, i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)),
6033 exp: i32x4(65535, 65535, 65535, 65535),
6034 },
6035 {
6036 name: "i16x8u hi",
6037 shape: wazeroir.ShapeI16x8,
6038 signed: false,
6039 useLow: false,
6040 v: i16x8(0, 0, 0, 0, 123, 0, 123, 0),
6041 exp: i32x4(123, 0, 123, 0),
6042 },
6043 {
6044 name: "i16x8u lo",
6045 shape: wazeroir.ShapeI16x8,
6046 signed: false,
6047 useLow: true,
6048 v: [16]byte{},
6049 exp: [16]byte{},
6050 },
6051 {
6052 name: "i16x8u lo",
6053 shape: wazeroir.ShapeI16x8,
6054 signed: false,
6055 useLow: true,
6056 v: i16x8(0, 0, 0, 0, 1, 1, 1, 1),
6057 exp: i32x4(0, 0, 0, 0),
6058 },
6059 {
6060 name: "i16x8u lo",
6061 shape: wazeroir.ShapeI16x8,
6062 signed: false,
6063 useLow: true,
6064 v: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 0, 0, 0, 0),
6065 exp: i32x4(65535, 65535, 65535, 65535),
6066 },
6067 {
6068 name: "i16x8u lo",
6069 shape: wazeroir.ShapeI16x8,
6070 signed: false,
6071 useLow: true,
6072 v: i16x8(123, 0, 123, 0, 0, 0, 0, 0),
6073 exp: i32x4(123, 0, 123, 0),
6074 },
6075 {
6076 name: "i32x4s hi",
6077 shape: wazeroir.ShapeI32x4,
6078 signed: true,
6079 useLow: false,
6080 v: [16]byte{},
6081 exp: [16]byte{},
6082 },
6083 {
6084 name: "i32x4s hi",
6085 shape: wazeroir.ShapeI32x4,
6086 signed: true,
6087 useLow: false,
6088 v: i32x4(0, 0, 1, i32ToU32(-1)),
6089 exp: i64x2(1, i64ToU64(-1)),
6090 },
6091 {
6092 name: "i32x4s hi",
6093 shape: wazeroir.ShapeI32x4,
6094 signed: true,
6095 useLow: false,
6096 v: i32x4(1, i32ToU32(-1), 0, 0),
6097 exp: i64x2(0, 0),
6098 },
6099 {
6100 name: "i32x4s hi",
6101 shape: wazeroir.ShapeI32x4,
6102 signed: true,
6103 useLow: false,
6104 v: i32x4(1, i32ToU32(-1), 123, 123),
6105 exp: i64x2(123, 123),
6106 },
6107 {
6108 name: "i32x4s lo",
6109 shape: wazeroir.ShapeI32x4,
6110 signed: true,
6111 useLow: true,
6112 v: [16]byte{},
6113 exp: [16]byte{},
6114 },
6115 {
6116 name: "i32x4s lo",
6117 shape: wazeroir.ShapeI32x4,
6118 signed: true,
6119 useLow: true,
6120 v: i32x4(1, i32ToU32(-1), 0, 0),
6121 exp: i64x2(1, i64ToU64(-1)),
6122 },
6123 {
6124 name: "i32x4s lo",
6125 shape: wazeroir.ShapeI32x4,
6126 signed: true,
6127 useLow: true,
6128 v: i32x4(0, 0, 1, i32ToU32(-1)),
6129 exp: i64x2(0, 0),
6130 },
6131 {
6132 name: "i32x4s lo",
6133 shape: wazeroir.ShapeI32x4,
6134 signed: true,
6135 useLow: true,
6136 v: i32x4(123, 123, 1, i32ToU32(-1)),
6137 exp: i64x2(123, 123),
6138 },
6139 {
6140 name: "i32x4u hi",
6141 shape: wazeroir.ShapeI32x4,
6142 signed: false,
6143 useLow: false,
6144 v: [16]byte{},
6145 exp: [16]byte{},
6146 },
6147 {
6148 name: "i32x4u hi",
6149 shape: wazeroir.ShapeI32x4,
6150 signed: false,
6151 useLow: false,
6152 v: i32x4(0, 0, 1, i32ToU32(-1)),
6153 exp: i64x2(1, 4294967295),
6154 },
6155 {
6156 name: "i32x4u hi",
6157 shape: wazeroir.ShapeI32x4,
6158 signed: false,
6159 useLow: false,
6160 v: i32x4(1, i32ToU32(-1), 0, 0),
6161 exp: i64x2(0, 0),
6162 },
6163 {
6164 name: "i32x4u hi",
6165 shape: wazeroir.ShapeI32x4,
6166 signed: false,
6167 useLow: false,
6168 v: i32x4(1, i32ToU32(-1), 123, 123),
6169 exp: i64x2(123, 123),
6170 },
6171 {
6172 name: "i32x4u lo",
6173 shape: wazeroir.ShapeI32x4,
6174 signed: false,
6175 useLow: true,
6176 v: [16]byte{},
6177 exp: [16]byte{},
6178 },
6179 {
6180 name: "i32x4u lo",
6181 shape: wazeroir.ShapeI32x4,
6182 signed: false,
6183 useLow: true,
6184 v: i32x4(1, i32ToU32(-1), 0, 0),
6185 exp: i64x2(1, 4294967295),
6186 },
6187 {
6188 name: "i32x4u lo",
6189 shape: wazeroir.ShapeI32x4,
6190 signed: false,
6191 useLow: true,
6192 v: i32x4(0, 0, 1, i32ToU32(-1)),
6193 exp: i64x2(0, 0),
6194 },
6195 {
6196 name: "i32x4u lo",
6197 shape: wazeroir.ShapeI32x4,
6198 signed: false,
6199 useLow: true,
6200 v: i32x4(123, 123, 1, i32ToU32(-1)),
6201 exp: i64x2(123, 123),
6202 },
6203 }
6204
6205 for _, tc := range tests {
6206 tc := tc
6207 t.Run(tc.name, func(t *testing.T) {
6208 env := newCompilerEnvironment()
6209 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
6210 &wazeroir.CompilationResult{HasMemory: true})
6211
6212 err := compiler.compilePreamble()
6213 require.NoError(t, err)
6214
6215 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
6216 require.NoError(t, err)
6217
6218 err = compiler.compileV128Extend(operationPtr(wazeroir.NewOperationV128Extend(tc.shape, tc.signed, tc.useLow)))
6219 require.NoError(t, err)
6220
6221 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
6222 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
6223
6224 err = compiler.compileReturnFunction()
6225 require.NoError(t, err)
6226
6227 code := asm.CodeSegment{}
6228 defer func() { require.NoError(t, code.Unmap()) }()
6229
6230
6231 _, err = compiler.compile(code.NextCodeSection())
6232 require.NoError(t, err)
6233 env.exec(code.Bytes())
6234
6235 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
6236
6237 lo, hi := env.stackTopAsV128()
6238 var actual [16]byte
6239 binary.LittleEndian.PutUint64(actual[:8], lo)
6240 binary.LittleEndian.PutUint64(actual[8:], hi)
6241 require.Equal(t, tc.exp, actual)
6242 })
6243 }
6244 }
6245
6246 func TestCompiler_compileV128Q15mulrSatS(t *testing.T) {
6247 tests := []struct {
6248 name string
6249 x1, x2, exp [16]byte
6250 }{
6251 {
6252 name: "1",
6253 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6254 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6255 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6256 },
6257 {
6258 name: "2",
6259 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6260 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6261 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6262 },
6263 {
6264 name: "3",
6265 x1: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6266 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6267 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6268 },
6269 {
6270 name: "4",
6271 x1: i16x8(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535),
6272 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6273 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6274 },
6275 {
6276 name: "5",
6277 x1: i16x8(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767),
6278 x2: i16x8(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767),
6279 exp: i16x8(32766, 32766, 32766, 32766, 32766, 32766, 32766, 32766),
6280 },
6281 }
6282
6283 for _, tc := range tests {
6284 tc := tc
6285 t.Run(tc.name, func(t *testing.T) {
6286 env := newCompilerEnvironment()
6287 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
6288 &wazeroir.CompilationResult{HasMemory: true})
6289
6290 err := compiler.compilePreamble()
6291 require.NoError(t, err)
6292
6293 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
6294 require.NoError(t, err)
6295
6296 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
6297 require.NoError(t, err)
6298
6299 err = compiler.compileV128Q15mulrSatS(operationPtr(wazeroir.NewOperationV128Q15mulrSatS()))
6300 require.NoError(t, err)
6301
6302 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
6303 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
6304
6305 err = compiler.compileReturnFunction()
6306 require.NoError(t, err)
6307
6308 code := asm.CodeSegment{}
6309 defer func() { require.NoError(t, code.Unmap()) }()
6310
6311
6312 _, err = compiler.compile(code.NextCodeSection())
6313 require.NoError(t, err)
6314 env.exec(code.Bytes())
6315
6316 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
6317
6318 lo, hi := env.stackTopAsV128()
6319 var actual [16]byte
6320 binary.LittleEndian.PutUint64(actual[:8], lo)
6321 binary.LittleEndian.PutUint64(actual[8:], hi)
6322 require.Equal(t, tc.exp, actual)
6323 })
6324 }
6325 }
6326
6327 func TestCompiler_compileFloatPromote(t *testing.T) {
6328 tests := []struct {
6329 name string
6330 v, exp [16]byte
6331 }{
6332 {
6333 name: "1",
6334 v: f32x4(float32(0x1.8f867ep+125), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)),
6335 exp: f64x2(6.6382536710104395e+37, 6.6382536710104395e+37),
6336 },
6337 {
6338 name: "2",
6339 v: f32x4(float32(0x1.8f867ep+125), float32(0x1.8f867ep+125), 0, 0),
6340 exp: f64x2(6.6382536710104395e+37, 6.6382536710104395e+37),
6341 },
6342 {
6343 name: "3",
6344 v: f32x4(0, 0, float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)),
6345 exp: f64x2(0, 0),
6346 },
6347 {
6348 name: "4",
6349 v: f32x4(float32(math.NaN()), float32(math.NaN()), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)),
6350 exp: f64x2(math.NaN(), math.NaN()),
6351 },
6352 {
6353 name: "5",
6354 v: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), float32(0x1.8f867ep+125), float32(0x1.8f867ep+125)),
6355 exp: f64x2(math.Inf(1), math.Inf(-1)),
6356 },
6357 }
6358
6359 for _, tc := range tests {
6360 tc := tc
6361 t.Run(tc.name, func(t *testing.T) {
6362 env := newCompilerEnvironment()
6363 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
6364 &wazeroir.CompilationResult{HasMemory: true})
6365
6366 err := compiler.compilePreamble()
6367 require.NoError(t, err)
6368
6369 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
6370 require.NoError(t, err)
6371
6372 err = compiler.compileV128FloatPromote(operationPtr(wazeroir.NewOperationV128FloatPromote()))
6373 require.NoError(t, err)
6374
6375 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
6376 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
6377
6378 err = compiler.compileReturnFunction()
6379 require.NoError(t, err)
6380
6381 code := asm.CodeSegment{}
6382 defer func() { require.NoError(t, code.Unmap()) }()
6383
6384
6385 _, err = compiler.compile(code.NextCodeSection())
6386 require.NoError(t, err)
6387 env.exec(code.Bytes())
6388
6389 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
6390
6391 lo, hi := env.stackTopAsV128()
6392 actualFs := [2]float64{
6393 math.Float64frombits(lo), math.Float64frombits(hi),
6394 }
6395 expFs := [2]float64{
6396 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[:8])),
6397 math.Float64frombits(binary.LittleEndian.Uint64(tc.exp[8:])),
6398 }
6399 for i := range expFs {
6400 exp, actual := expFs[i], actualFs[i]
6401 if math.IsNaN(exp) {
6402 require.True(t, math.IsNaN(actual))
6403 } else {
6404 require.Equal(t, exp, actual)
6405 }
6406 }
6407 })
6408 }
6409 }
6410
6411 func TestCompiler_compileV128FloatDemote(t *testing.T) {
6412 tests := []struct {
6413 name string
6414 v, exp [16]byte
6415 }{
6416 {
6417 name: "1",
6418 v: f64x2(0, 0),
6419 exp: f32x4(0, 0, 0, 0),
6420 },
6421 {
6422 name: "2",
6423 v: f64x2(0x1.fffffe0000000p-127, 0x1.fffffe0000000p-127),
6424 exp: f32x4(0x1p-126, 0x1p-126, 0, 0),
6425 },
6426 {
6427 name: "3",
6428 v: f64x2(0x1.fffffep+127, 0x1.fffffep+127),
6429 exp: f32x4(0x1.fffffep+127, 0x1.fffffep+127, 0, 0),
6430 },
6431 {
6432 name: "4",
6433 v: f64x2(math.NaN(), math.NaN()),
6434 exp: f32x4(float32(math.NaN()), float32(math.NaN()), 0, 0),
6435 },
6436 {
6437 name: "5",
6438 v: f64x2(math.Inf(1), math.Inf(-1)),
6439 exp: f32x4(float32(math.Inf(1)), float32(math.Inf(-1)), 0, 0),
6440 },
6441 }
6442
6443 for _, tc := range tests {
6444 tc := tc
6445 t.Run(tc.name, func(t *testing.T) {
6446 env := newCompilerEnvironment()
6447 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
6448 &wazeroir.CompilationResult{HasMemory: true})
6449
6450 err := compiler.compilePreamble()
6451 require.NoError(t, err)
6452
6453 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
6454 require.NoError(t, err)
6455
6456 err = compiler.compileV128FloatDemote(operationPtr(wazeroir.NewOperationV128FloatDemote()))
6457 require.NoError(t, err)
6458
6459 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
6460 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
6461
6462 err = compiler.compileReturnFunction()
6463 require.NoError(t, err)
6464
6465 code := asm.CodeSegment{}
6466 defer func() { require.NoError(t, code.Unmap()) }()
6467
6468
6469 _, err = compiler.compile(code.NextCodeSection())
6470 require.NoError(t, err)
6471 env.exec(code.Bytes())
6472
6473 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
6474
6475 lo, hi := env.stackTopAsV128()
6476 actualFs := [4]float32{
6477 math.Float32frombits(uint32(lo)),
6478 math.Float32frombits(uint32(lo >> 32)),
6479 math.Float32frombits(uint32(hi)),
6480 math.Float32frombits(uint32(hi >> 32)),
6481 }
6482 expFs := [4]float32{
6483 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[:4])),
6484 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[4:8])),
6485 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[8:12])),
6486 math.Float32frombits(binary.LittleEndian.Uint32(tc.exp[12:])),
6487 }
6488 for i := range expFs {
6489 exp, actual := expFs[i], actualFs[i]
6490 if math.IsNaN(float64(exp)) {
6491 require.True(t, math.IsNaN(float64(actual)))
6492 } else {
6493 require.Equal(t, exp, actual)
6494 }
6495 }
6496 })
6497 }
6498 }
6499
6500 func TestCompiler_compileV128ExtAddPairwise(t *testing.T) {
6501 tests := []struct {
6502 name string
6503 shape wazeroir.Shape
6504 signed bool
6505 v, exp [16]byte
6506 }{
6507 {
6508 name: "i8x16 s",
6509 shape: wazeroir.ShapeI8x16,
6510 signed: true,
6511 v: [16]byte{},
6512 exp: [16]byte{},
6513 },
6514 {
6515 name: "i8x16 s",
6516 shape: wazeroir.ShapeI8x16,
6517 signed: true,
6518 v: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
6519 exp: i16x8(2, 2, 2, 2, 2, 2, 2, 2),
6520 },
6521 {
6522 name: "i8x16 s",
6523 shape: wazeroir.ShapeI8x16,
6524 signed: true,
6525 v: [16]byte{
6526 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
6527 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
6528 },
6529 exp: i16x8(
6530 i16ToU16(-2), i16ToU16(-2), i16ToU16(-2), i16ToU16(-2),
6531 i16ToU16(-2), i16ToU16(-2), i16ToU16(-2), i16ToU16(-2),
6532 ),
6533 },
6534 {
6535 name: "i8x16 s",
6536 shape: wazeroir.ShapeI8x16,
6537 signed: true,
6538 v: [16]byte{
6539 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
6540 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
6541 },
6542 exp: i16x8(
6543 i16ToU16(-256), i16ToU16(-256), i16ToU16(-256), i16ToU16(-256),
6544 i16ToU16(-256), i16ToU16(-256), i16ToU16(-256), i16ToU16(-256),
6545 ),
6546 },
6547 {
6548 name: "i8x16 u",
6549 shape: wazeroir.ShapeI8x16,
6550 signed: false,
6551 v: [16]byte{},
6552 exp: [16]byte{},
6553 },
6554 {
6555 name: "i8x16 u",
6556 shape: wazeroir.ShapeI8x16,
6557 signed: false,
6558 v: [16]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
6559 exp: i16x8(2, 2, 2, 2, 2, 2, 2, 2),
6560 },
6561 {
6562 name: "i8x16 u",
6563 shape: wazeroir.ShapeI8x16,
6564 signed: false,
6565 v: [16]byte{
6566 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
6567 i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1), i8ToU8(-1),
6568 },
6569 exp: i16x8(510, 510, 510, 510, 510, 510, 510, 510),
6570 },
6571 {
6572 name: "i8x16 u",
6573 shape: wazeroir.ShapeI8x16,
6574 signed: false,
6575 v: [16]byte{
6576 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
6577 i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128), i8ToU8(-128),
6578 },
6579 exp: i16x8(256, 256, 256, 256, 256, 256, 256, 256),
6580 },
6581 {
6582 name: "i16x8 s",
6583 shape: wazeroir.ShapeI16x8,
6584 signed: true,
6585 v: [16]byte{},
6586 exp: [16]byte{},
6587 },
6588 {
6589 name: "i16x8 s",
6590 shape: wazeroir.ShapeI16x8,
6591 signed: true,
6592 v: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6593 exp: i32x4(2, 2, 2, 2),
6594 },
6595 {
6596 name: "i16x8 s",
6597 shape: wazeroir.ShapeI16x8,
6598 signed: true,
6599 v: i16x8(
6600 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1),
6601 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1),
6602 ),
6603 exp: i32x4(i32ToU32(-2), i32ToU32(-2), i32ToU32(-2), i32ToU32(-2)),
6604 },
6605 {
6606 name: "i16x8 s",
6607 shape: wazeroir.ShapeI16x8,
6608 signed: true,
6609 v: i16x8(
6610 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
6611 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
6612 ),
6613 exp: i32x4(i32ToU32(-65536), i32ToU32(-65536), i32ToU32(-65536), i32ToU32(-65536)),
6614 },
6615 {
6616 name: "i16x8 u",
6617 shape: wazeroir.ShapeI16x8,
6618 signed: false,
6619 v: [16]byte{},
6620 exp: [16]byte{},
6621 },
6622 {
6623 name: "i16x8 u",
6624 shape: wazeroir.ShapeI16x8,
6625 signed: false,
6626 v: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6627 exp: i32x4(2, 2, 2, 2),
6628 },
6629 {
6630 name: "i16x8 u",
6631 shape: wazeroir.ShapeI16x8,
6632 signed: false,
6633 v: i16x8(
6634 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1),
6635 i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1),
6636 ),
6637 exp: i32x4(131070, 131070, 131070, 131070),
6638 },
6639 {
6640 name: "i16x8 u",
6641 shape: wazeroir.ShapeI16x8,
6642 signed: false,
6643 v: i16x8(
6644 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
6645 i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768), i16ToU16(-32768),
6646 ),
6647 exp: i32x4(65536, 65536, 65536, 65536),
6648 },
6649 }
6650
6651 for _, tc := range tests {
6652 tc := tc
6653 t.Run(tc.name, func(t *testing.T) {
6654 env := newCompilerEnvironment()
6655 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
6656 &wazeroir.CompilationResult{HasMemory: true})
6657
6658 err := compiler.compilePreamble()
6659 require.NoError(t, err)
6660
6661 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
6662 require.NoError(t, err)
6663
6664 err = compiler.compileV128ExtAddPairwise(operationPtr(wazeroir.NewOperationV128ExtAddPairwise(tc.shape, tc.signed)))
6665 require.NoError(t, err)
6666
6667 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
6668 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
6669
6670 err = compiler.compileReturnFunction()
6671 require.NoError(t, err)
6672
6673 code := asm.CodeSegment{}
6674 defer func() { require.NoError(t, code.Unmap()) }()
6675
6676
6677 _, err = compiler.compile(code.NextCodeSection())
6678 require.NoError(t, err)
6679 env.exec(code.Bytes())
6680
6681 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
6682
6683 lo, hi := env.stackTopAsV128()
6684 var actual [16]byte
6685 binary.LittleEndian.PutUint64(actual[:8], lo)
6686 binary.LittleEndian.PutUint64(actual[8:], hi)
6687 require.Equal(t, tc.exp, actual)
6688 })
6689 }
6690 }
6691
6692 func TestCompiler_compileV128Narrow(t *testing.T) {
6693 tests := []struct {
6694 name string
6695 shape wazeroir.Shape
6696 signed bool
6697 x1, x2, exp [16]byte
6698 }{
6699 {
6700 name: "i16x8 s",
6701 shape: wazeroir.ShapeI16x8,
6702 signed: true,
6703 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6704 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6705 exp: [16]byte{},
6706 },
6707 {
6708 name: "i16x8 s",
6709 shape: wazeroir.ShapeI16x8,
6710 signed: true,
6711 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6712 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6713 exp: [16]byte{
6714 0, 0, 0, 0, 0, 0, 0, 0,
6715 1, 1, 1, 1, 1, 1, 1, 1,
6716 },
6717 },
6718 {
6719 name: "i16x8 s",
6720 shape: wazeroir.ShapeI16x8,
6721 signed: true,
6722 x1: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6723 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6724 exp: [16]byte{
6725 1, 1, 1, 1, 1, 1, 1, 1,
6726 0, 0, 0, 0, 0, 0, 0, 0,
6727 },
6728 },
6729 {
6730 name: "i16x8 s",
6731 shape: wazeroir.ShapeI16x8,
6732 signed: true,
6733 x1: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0),
6734 x2: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
6735 exp: [16]byte{
6736 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
6737 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
6738 },
6739 },
6740 {
6741 name: "i16x8 s",
6742 shape: wazeroir.ShapeI16x8,
6743 signed: true,
6744 x1: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
6745 x2: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0),
6746 exp: [16]byte{
6747 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
6748 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
6749 },
6750 },
6751 {
6752 name: "i16x8 u",
6753 shape: wazeroir.ShapeI16x8,
6754 signed: false,
6755 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6756 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6757 exp: [16]byte{},
6758 },
6759 {
6760 name: "i16x8 u",
6761 shape: wazeroir.ShapeI16x8,
6762 signed: false,
6763 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6764 x2: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6765 exp: [16]byte{
6766 0, 0, 0, 0, 0, 0, 0, 0,
6767 1, 1, 1, 1, 1, 1, 1, 1,
6768 },
6769 },
6770 {
6771 name: "i16x8 u",
6772 shape: wazeroir.ShapeI16x8,
6773 signed: false,
6774 x1: i16x8(1, 1, 1, 1, 1, 1, 1, 1),
6775 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6776 exp: [16]byte{
6777 1, 1, 1, 1, 1, 1, 1, 1,
6778 0, 0, 0, 0, 0, 0, 0, 0,
6779 },
6780 },
6781 {
6782 name: "i16x8 u",
6783 shape: wazeroir.ShapeI16x8,
6784 signed: false,
6785 x1: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0),
6786 x2: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
6787 exp: [16]byte{
6788 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6789 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6790 },
6791 },
6792 {
6793 name: "i16x8 u",
6794 shape: wazeroir.ShapeI16x8,
6795 signed: false,
6796 x1: i16x8(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
6797 x2: i16x8(i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0, i16ToU16(-0x8000), 0),
6798 exp: [16]byte{
6799 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6800 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6801 },
6802 },
6803 {
6804 name: "i16x8 u",
6805 shape: wazeroir.ShapeI16x8,
6806 signed: false,
6807 x1: i16x8(i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0),
6808 x2: i16x8(0, 0x100, 0, 0x100, 0, 0x100, 0, 0x100),
6809 exp: [16]byte{
6810 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6811 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
6812 },
6813 },
6814 {
6815 name: "i16x8 u",
6816 shape: wazeroir.ShapeI16x8,
6817 signed: false,
6818 x1: i16x8(0, 0x100, 0, 0x100, 0, 0x100, 0, 0x100),
6819 x2: i16x8(i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0, i16ToU16(-1), 0),
6820 exp: [16]byte{
6821 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
6822 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6823 },
6824 },
6825 {
6826 name: "i32x4 s",
6827 shape: wazeroir.ShapeI32x4,
6828 signed: true,
6829 x1: i32x4(0, 0, 0, 0),
6830 x2: i32x4(0, 0, 0, 0),
6831 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6832 },
6833 {
6834 name: "i32x4 s",
6835 shape: wazeroir.ShapeI32x4,
6836 signed: true,
6837 x1: i32x4(0, 0, 0, 0),
6838 x2: i32x4(1, 1, 1, 1),
6839 exp: i16x8(0, 0, 0, 0, 1, 1, 1, 1),
6840 },
6841 {
6842 name: "i32x4 s",
6843 shape: wazeroir.ShapeI32x4,
6844 signed: true,
6845 x1: i32x4(1, 1, 1, 1),
6846 x2: i32x4(0, 0, 0, 0),
6847 exp: i16x8(1, 1, 1, 1, 0, 0, 0, 0),
6848 },
6849 {
6850 name: "i32x4 s",
6851 shape: wazeroir.ShapeI32x4,
6852 signed: true,
6853 x1: i32x4(0x8000, 0x8000, 0x7fff, 0x7fff),
6854 x2: i32x4(0x7fff, 0x7fff, 0x8000, 0x8000),
6855 exp: i16x8(0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff),
6856 },
6857 {
6858 name: "i32x4 u",
6859 shape: wazeroir.ShapeI32x4,
6860 signed: false,
6861 x1: i32x4(0, 0, 0, 0),
6862 x2: i32x4(0, 0, 0, 0),
6863 exp: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
6864 },
6865 {
6866 name: "i32x4 u",
6867 shape: wazeroir.ShapeI32x4,
6868 signed: false,
6869 x1: i32x4(0, 0, 0, 0),
6870 x2: i32x4(1, 1, 1, 1),
6871 exp: i16x8(0, 0, 0, 0, 1, 1, 1, 1),
6872 },
6873 {
6874 name: "i32x4 u",
6875 shape: wazeroir.ShapeI32x4,
6876 signed: false,
6877 x1: i32x4(1, 1, 1, 1),
6878 x2: i32x4(0, 0, 0, 0),
6879 exp: i16x8(1, 1, 1, 1, 0, 0, 0, 0),
6880 },
6881 {
6882 name: "i32x4 u",
6883 shape: wazeroir.ShapeI32x4,
6884 signed: false,
6885 x1: i32x4(0x8000, 0x8000, 0x7fff, 0x7fff),
6886 x2: i32x4(0x7fff, 0x7fff, 0x8000, 0x8000),
6887 exp: i16x8(0x8000, 0x8000, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x8000, 0x8000),
6888 },
6889 }
6890
6891 for _, tc := range tests {
6892 tc := tc
6893 t.Run(tc.name, func(t *testing.T) {
6894 env := newCompilerEnvironment()
6895 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
6896 &wazeroir.CompilationResult{HasMemory: true})
6897
6898 err := compiler.compilePreamble()
6899 require.NoError(t, err)
6900
6901 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
6902 require.NoError(t, err)
6903
6904 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
6905 require.NoError(t, err)
6906
6907 err = compiler.compileV128Narrow(operationPtr(wazeroir.NewOperationV128Narrow(tc.shape, tc.signed)))
6908 require.NoError(t, err)
6909
6910 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
6911 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
6912
6913 err = compiler.compileReturnFunction()
6914 require.NoError(t, err)
6915
6916 code := asm.CodeSegment{}
6917 defer func() { require.NoError(t, code.Unmap()) }()
6918
6919
6920 _, err = compiler.compile(code.NextCodeSection())
6921 require.NoError(t, err)
6922 env.exec(code.Bytes())
6923
6924 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
6925
6926 lo, hi := env.stackTopAsV128()
6927 var actual [16]byte
6928 binary.LittleEndian.PutUint64(actual[:8], lo)
6929 binary.LittleEndian.PutUint64(actual[8:], hi)
6930 require.Equal(t, tc.exp, actual)
6931 })
6932 }
6933 }
6934
6935 func TestCompiler_compileV128FConvertFromI(t *testing.T) {
6936 tests := []struct {
6937 name string
6938 destShape wazeroir.Shape
6939 signed bool
6940 v, exp [16]byte
6941 }{
6942 {
6943 name: "f32x4 s",
6944 destShape: wazeroir.ShapeF32x4,
6945 signed: true,
6946 v: i32x4(0, 0, 0, 0),
6947 exp: f32x4(0, 0, 0, 0),
6948 },
6949 {
6950 name: "f32x4 s",
6951 destShape: wazeroir.ShapeF32x4,
6952 signed: true,
6953 v: i32x4(1, 0, 2, 3),
6954 exp: f32x4(1, 0, 2.0, 3),
6955 },
6956 {
6957 name: "f32x4 s",
6958 destShape: wazeroir.ShapeF32x4,
6959 signed: true,
6960 v: i32x4(1234567890, i32ToU32(-2147483648.0), 2147483647, 1234567890),
6961 exp: f32x4(0x1.26580cp+30, -2147483648.0, 2147483647, 0x1.26580cp+30),
6962 },
6963 {
6964 name: "f32x4 s",
6965 destShape: wazeroir.ShapeF32x4,
6966 signed: false,
6967 v: i32x4(0, 0, 0, 0),
6968 exp: f32x4(0, 0, 0, 0),
6969 },
6970 {
6971 name: "f32x4 s",
6972 destShape: wazeroir.ShapeF32x4,
6973 signed: false,
6974 v: i32x4(1, 0, 2, 3),
6975 exp: f32x4(1, 0, 2.0, 3),
6976 },
6977 {
6978 name: "f32x4 s",
6979 destShape: wazeroir.ShapeF32x4,
6980 signed: false,
6981 v: i32x4(2147483647, i32ToU32(-2147483648.0), 2147483647, i32ToU32(-1)),
6982 exp: f32x4(2147483648.0, 2147483648.0, 2147483648.0, 4294967295.0),
6983 },
6984 {
6985 name: "f64x2 s",
6986 destShape: wazeroir.ShapeF64x2,
6987 signed: true,
6988 v: i32x4(0, 0, 0, 0),
6989 exp: f64x2(0, 0),
6990 },
6991 {
6992 name: "f64x2 s",
6993 destShape: wazeroir.ShapeF64x2,
6994 signed: true,
6995 v: i32x4(0, 0, i32ToU32(-1), i32ToU32(-32)),
6996 exp: f64x2(0, 0),
6997 },
6998 {
6999 name: "f64x2 s",
7000 destShape: wazeroir.ShapeF64x2,
7001 signed: true,
7002 v: i32x4(2147483647, i32ToU32(-2147483648), 0, 0),
7003 exp: f64x2(2147483647, -2147483648),
7004 },
7005 {
7006 name: "f64x2 s",
7007 destShape: wazeroir.ShapeF64x2,
7008 signed: false,
7009 v: i32x4(0, 0, 0, 0),
7010 exp: f64x2(0, 0),
7011 },
7012 {
7013 name: "f64x2 s",
7014 destShape: wazeroir.ShapeF64x2,
7015 signed: false,
7016 v: i32x4(0, 0, i32ToU32(-1), i32ToU32(-32)),
7017 exp: f64x2(0, 0),
7018 },
7019 {
7020 name: "f64x2 s",
7021 destShape: wazeroir.ShapeF64x2,
7022 signed: false,
7023 v: i32x4(2147483647, i32ToU32(-2147483648), 0, 0),
7024 exp: f64x2(2147483647, 2147483648),
7025 },
7026 }
7027
7028 for _, tc := range tests {
7029 tc := tc
7030 t.Run(tc.name, func(t *testing.T) {
7031 env := newCompilerEnvironment()
7032 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
7033 &wazeroir.CompilationResult{HasMemory: true})
7034
7035 err := compiler.compilePreamble()
7036 require.NoError(t, err)
7037
7038 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
7039 require.NoError(t, err)
7040
7041 err = compiler.compileV128FConvertFromI(operationPtr(wazeroir.NewOperationV128FConvertFromI(tc.destShape, tc.signed)))
7042 require.NoError(t, err)
7043
7044 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
7045 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
7046
7047 err = compiler.compileReturnFunction()
7048 require.NoError(t, err)
7049
7050 code := asm.CodeSegment{}
7051 defer func() { require.NoError(t, code.Unmap()) }()
7052
7053
7054 _, err = compiler.compile(code.NextCodeSection())
7055 require.NoError(t, err)
7056 env.exec(code.Bytes())
7057
7058 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
7059
7060 lo, hi := env.stackTopAsV128()
7061 var actual [16]byte
7062 binary.LittleEndian.PutUint64(actual[:8], lo)
7063 binary.LittleEndian.PutUint64(actual[8:], hi)
7064 require.Equal(t, tc.exp, actual)
7065 })
7066 }
7067 }
7068
7069 func TestCompiler_compileV128Dot(t *testing.T) {
7070 tests := []struct {
7071 name string
7072 x1, x2, exp [16]byte
7073 }{
7074 {
7075 name: "1",
7076 x1: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
7077 x2: i16x8(0, 0, 0, 0, 0, 0, 0, 0),
7078 exp: i32x4(0, 0, 0, 0),
7079 },
7080 {
7081 name: "2",
7082 x1: i16x8(1, 1, 1, 1, i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1)),
7083 x2: i16x8(i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), i16ToU16(-1), 2, 2, 2, 2),
7084 exp: i32x4(i32ToU32(-2), i32ToU32(-2), i32ToU32(-4), i32ToU32(-4)),
7085 },
7086 {
7087 name: "3",
7088 x1: i16x8(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535),
7089 x2: i16x8(65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535),
7090 exp: i32x4(2, 2, 2, 2),
7091 },
7092 }
7093
7094 for _, tc := range tests {
7095 tc := tc
7096 t.Run(tc.name, func(t *testing.T) {
7097 env := newCompilerEnvironment()
7098 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
7099 &wazeroir.CompilationResult{HasMemory: true})
7100
7101 err := compiler.compilePreamble()
7102 require.NoError(t, err)
7103
7104 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x2[:8]), binary.LittleEndian.Uint64(tc.x2[8:]))))
7105 require.NoError(t, err)
7106
7107 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.x1[:8]), binary.LittleEndian.Uint64(tc.x1[8:]))))
7108 require.NoError(t, err)
7109
7110 err = compiler.compileV128Dot(operationPtr(wazeroir.NewOperationV128Dot()))
7111 require.NoError(t, err)
7112
7113 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
7114 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
7115
7116 err = compiler.compileReturnFunction()
7117 require.NoError(t, err)
7118
7119 code := asm.CodeSegment{}
7120 defer func() { require.NoError(t, code.Unmap()) }()
7121
7122
7123 _, err = compiler.compile(code.NextCodeSection())
7124 require.NoError(t, err)
7125 env.exec(code.Bytes())
7126
7127 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
7128
7129 lo, hi := env.stackTopAsV128()
7130 var actual [16]byte
7131 binary.LittleEndian.PutUint64(actual[:8], lo)
7132 binary.LittleEndian.PutUint64(actual[8:], hi)
7133 require.Equal(t, tc.exp, actual)
7134 })
7135 }
7136 }
7137
7138 func TestCompiler_compileV128ITruncSatFromF(t *testing.T) {
7139 tests := []struct {
7140 name string
7141 originShape wazeroir.Shape
7142 signed bool
7143 v, exp [16]byte
7144 }{
7145 {
7146 name: "f32x4 s",
7147 originShape: wazeroir.ShapeF32x4,
7148 signed: true,
7149 v: i32x4(0, 0, 0, 0),
7150 exp: f32x4(0, 0, 0, 0),
7151 },
7152 {
7153 name: "f32x4 s",
7154 originShape: wazeroir.ShapeF32x4,
7155 signed: true,
7156 v: f32x4(1.5, -1.9, -1.9, 1.5),
7157 exp: i32x4(1, i32ToU32(-1), i32ToU32(-1), 1),
7158 },
7159 {
7160 name: "f32x4 s",
7161 originShape: wazeroir.ShapeF32x4,
7162 signed: true,
7163 v: f32x4(float32(math.NaN()), -4294967294.0, float32(math.Inf(-1)), float32(math.Inf(1))),
7164 exp: i32x4(0, i32ToU32(-2147483648), i32ToU32(-2147483648), 2147483647),
7165 },
7166 {
7167 name: "f32x4 u",
7168 originShape: wazeroir.ShapeF32x4,
7169 signed: false,
7170 v: i32x4(0, 0, 0, 0),
7171 exp: f32x4(0, 0, 0, 0),
7172 },
7173 {
7174 name: "f32x4 u",
7175 originShape: wazeroir.ShapeF32x4,
7176 signed: false,
7177 v: f32x4(1.5, -1.9, -1.9, 1.5),
7178 exp: i32x4(1, 0, 0, 1),
7179 },
7180 {
7181 name: "f32x4 u",
7182 originShape: wazeroir.ShapeF32x4,
7183 signed: false,
7184 v: f32x4(float32(math.NaN()), -4294967294.0, 4294967294.0, float32(math.Inf(1))),
7185 exp: i32x4(0, 0, 4294967295, 4294967295),
7186 },
7187 {
7188 name: "f64x2 s",
7189 originShape: wazeroir.ShapeF64x2,
7190 signed: true,
7191 v: f64x2(0, 0),
7192 exp: i32x4(0, 0, 0, 0),
7193 },
7194 {
7195 name: "f64x2 s",
7196 originShape: wazeroir.ShapeF64x2,
7197 signed: true,
7198 v: f64x2(5.123, -2.0),
7199 exp: i32x4(5, i32ToU32(-2), 0, 0),
7200 },
7201 {
7202 name: "f64x2 s",
7203 originShape: wazeroir.ShapeF64x2,
7204 signed: true,
7205 v: f64x2(math.NaN(), math.Inf(1)),
7206 exp: i32x4(0, 2147483647, 0, 0),
7207 },
7208 {
7209 name: "f64x2 s",
7210 originShape: wazeroir.ShapeF64x2,
7211 signed: true,
7212 v: f64x2(math.Inf(-1), 4294967294.0),
7213 exp: i32x4(i32ToU32(-2147483648), 2147483647, 0, 0),
7214 },
7215 {
7216 name: "f64x2 u",
7217 originShape: wazeroir.ShapeF64x2,
7218 signed: false,
7219 v: f64x2(0, 0),
7220 exp: i32x4(0, 0, 0, 0),
7221 },
7222 {
7223 name: "f64x2 u",
7224 originShape: wazeroir.ShapeF64x2,
7225 signed: false,
7226 v: f64x2(5.123, -2.0),
7227 exp: i32x4(5, 0, 0, 0),
7228 },
7229 {
7230 name: "f64x2 u",
7231 originShape: wazeroir.ShapeF64x2,
7232 signed: false,
7233 v: f64x2(math.NaN(), math.Inf(1)),
7234 exp: i32x4(0, 4294967295, 0, 0),
7235 },
7236 {
7237 name: "f64x2 u",
7238 originShape: wazeroir.ShapeF64x2,
7239 signed: false,
7240 v: f64x2(math.Inf(-1), 4294967296.0),
7241 exp: i32x4(0, 4294967295, 0, 0),
7242 },
7243 }
7244
7245 for _, tc := range tests {
7246 tc := tc
7247 t.Run(tc.name, func(t *testing.T) {
7248 env := newCompilerEnvironment()
7249 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
7250 &wazeroir.CompilationResult{HasMemory: true})
7251
7252 err := compiler.compilePreamble()
7253 require.NoError(t, err)
7254
7255 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(tc.v[:8]), binary.LittleEndian.Uint64(tc.v[8:]))))
7256 require.NoError(t, err)
7257
7258 err = compiler.compileV128ITruncSatFromF(operationPtr(wazeroir.NewOperationV128ITruncSatFromF(tc.originShape, tc.signed)))
7259 require.NoError(t, err)
7260
7261 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
7262 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
7263
7264 err = compiler.compileReturnFunction()
7265 require.NoError(t, err)
7266
7267 code := asm.CodeSegment{}
7268 defer func() { require.NoError(t, code.Unmap()) }()
7269
7270
7271 _, err = compiler.compile(code.NextCodeSection())
7272 require.NoError(t, err)
7273 env.exec(code.Bytes())
7274
7275 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
7276
7277 lo, hi := env.stackTopAsV128()
7278 var actual [16]byte
7279 binary.LittleEndian.PutUint64(actual[:8], lo)
7280 binary.LittleEndian.PutUint64(actual[8:], hi)
7281 require.Equal(t, tc.exp, actual)
7282 })
7283 }
7284 }
7285
7286
7287 func TestCompiler_compileSelect_v128(t *testing.T) {
7288 const x1Lo, x1Hi = uint64(0x1), uint64(0x2)
7289 const x2Lo, x2Hi = uint64(0x3), uint64(0x4)
7290
7291 for _, selector := range []uint32{0, 1} {
7292 env := newCompilerEnvironment()
7293 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
7294 &wazeroir.CompilationResult{HasMemory: true})
7295
7296 err := compiler.compilePreamble()
7297 require.NoError(t, err)
7298
7299 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(x1Lo, x1Hi)))
7300 require.NoError(t, err)
7301
7302 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(x2Lo, x2Hi)))
7303 require.NoError(t, err)
7304
7305 err = compiler.compileConstI32(operationPtr(wazeroir.NewOperationConstI32(selector)))
7306 require.NoError(t, err)
7307
7308 err = compiler.compileSelect(operationPtr(wazeroir.NewOperationSelect(true)))
7309 require.NoError(t, err)
7310
7311 requireRuntimeLocationStackPointerEqual(t, uint64(2), compiler)
7312 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
7313
7314 err = compiler.compileReturnFunction()
7315 require.NoError(t, err)
7316
7317 code := asm.CodeSegment{}
7318 defer func() { require.NoError(t, code.Unmap()) }()
7319
7320
7321 _, err = compiler.compile(code.NextCodeSection())
7322 require.NoError(t, err)
7323 env.exec(code.Bytes())
7324
7325 require.Equal(t, nativeCallStatusCodeReturned, env.callEngine().statusCode)
7326
7327 lo, hi := env.stackTopAsV128()
7328 if selector == 0 {
7329 require.Equal(t, x2Lo, lo)
7330 require.Equal(t, x2Hi, hi)
7331 } else {
7332 require.Equal(t, x1Lo, lo)
7333 require.Equal(t, x1Hi, hi)
7334 }
7335 }
7336 }
7337
View as plain text