1 package compiler
2
3 import (
4 "encoding/binary"
5 "testing"
6
7 "github.com/tetratelabs/wazero/internal/asm"
8 "github.com/tetratelabs/wazero/internal/asm/arm64"
9 "github.com/tetratelabs/wazero/internal/testing/require"
10 "github.com/tetratelabs/wazero/internal/wasm"
11 "github.com/tetratelabs/wazero/internal/wazeroir"
12 )
13
14
15
16 func TestArm64Compiler_V128Shuffle_ConstTable_MiddleOfFunction(t *testing.T) {
17 env := newCompilerEnvironment()
18 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
19 &wazeroir.CompilationResult{HasMemory: true})
20
21 err := compiler.compilePreamble()
22 require.NoError(t, err)
23
24 lanes := []uint64{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 0}
25 v := [16]byte{0: 0xa, 1: 0xb, 10: 0xc}
26 w := [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
27 exp := [16]byte{
28 0xb, 0xb, 0xb, 0xb,
29 0xa, 0xa, 0xa, 0xa,
30 0xc, 0xc, 0xc, 0xc,
31 0xa, 0xa, 0xa, 0xa,
32 }
33
34 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(v[:8]), binary.LittleEndian.Uint64(v[8:]))))
35 require.NoError(t, err)
36
37 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(w[:8]), binary.LittleEndian.Uint64(w[8:]))))
38 require.NoError(t, err)
39
40 err = compiler.compileV128Shuffle(operationPtr(wazeroir.NewOperationV128Shuffle(lanes)))
41 require.NoError(t, err)
42
43 assembler := compiler.(*arm64Compiler).assembler.(*arm64.AssemblerImpl)
44 assembler.MaxDisplacementForConstantPool = 0
45
46 err = compiler.compileReturnFunction()
47 require.NoError(t, err)
48
49 code := asm.CodeSegment{}
50 defer func() { require.NoError(t, code.Unmap()) }()
51
52
53 _, err = compiler.compile(code.NextCodeSection())
54 require.NoError(t, err)
55
56 env.exec(code.Bytes())
57
58 lo, hi := env.stackTopAsV128()
59 var actual [16]byte
60 binary.LittleEndian.PutUint64(actual[:8], lo)
61 binary.LittleEndian.PutUint64(actual[8:], hi)
62 require.Equal(t, exp, actual)
63 }
64
65 func TestArm64Compiler_V128Shuffle_combinations(t *testing.T) {
66 movValueRegisterToRegister := func(t *testing.T, c *arm64Compiler, src *runtimeValueLocation, dst asm.Register) {
67 c.assembler.CompileTwoVectorRegistersToVectorRegister(arm64.VORR, src.register, src.register, dst,
68 arm64.VectorArrangement16B)
69 c.locationStack.markRegisterUnused(src.register)
70 src.setRegister(dst)
71
72 c.locationStack.stack[src.stackPointer-1].setRegister(dst)
73 c.locationStack.markRegisterUsed(dst)
74 }
75
76 tests := []struct {
77 name string
78 init func(t *testing.T, c *arm64Compiler)
79 wReg, vReg asm.Register
80 verifyFnc func(t *testing.T, env *compilerEnv)
81 expStackPointerAfterShuffle uint64
82 }{
83 {
84 name: "w=v1, v=v2",
85 wReg: arm64.RegV1,
86 vReg: arm64.RegV2,
87 init: func(t *testing.T, c *arm64Compiler) {},
88 verifyFnc: func(t *testing.T, env *compilerEnv) {},
89 expStackPointerAfterShuffle: 2,
90 },
91 {
92 name: "w=v2, v=v1",
93 wReg: arm64.RegV2,
94 vReg: arm64.RegV1,
95 init: func(t *testing.T, c *arm64Compiler) {},
96 verifyFnc: func(t *testing.T, env *compilerEnv) {},
97 expStackPointerAfterShuffle: 2,
98 },
99 {
100 name: "w=v29, v=v30",
101 wReg: arm64.RegV29,
102 vReg: arm64.RegV30,
103 init: func(t *testing.T, c *arm64Compiler) {},
104 verifyFnc: func(t *testing.T, env *compilerEnv) {},
105 expStackPointerAfterShuffle: 2,
106 },
107 {
108 name: "w=v12, v=v30",
109 wReg: arm64.RegV12,
110 vReg: arm64.RegV30,
111 init: func(t *testing.T, c *arm64Compiler) {
112
113 err := c.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(1234, 5678)))
114 require.NoError(t, err)
115 movValueRegisterToRegister(t, c, c.locationStack.peek(), arm64.RegV29)
116 },
117 verifyFnc: func(t *testing.T, env *compilerEnv) {
118
119 lo, hi := env.stack()[callFrameDataSizeInUint64], env.stack()[callFrameDataSizeInUint64+1]
120 require.Equal(t, uint64(1234), lo)
121 require.Equal(t, uint64(5678), hi)
122 },
123 expStackPointerAfterShuffle: 4,
124 },
125 {
126 name: "w=v29, v=v12",
127 wReg: arm64.RegV29,
128 vReg: arm64.RegV12,
129 init: func(t *testing.T, c *arm64Compiler) {
130
131 err := c.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(1234, 5678)))
132 require.NoError(t, err)
133 movValueRegisterToRegister(t, c, c.locationStack.peek(), arm64.RegV30)
134 },
135 verifyFnc: func(t *testing.T, env *compilerEnv) {
136
137 lo, hi := env.stack()[callFrameDataSizeInUint64], env.stack()[callFrameDataSizeInUint64+1]
138 require.Equal(t, uint64(1234), lo)
139 require.Equal(t, uint64(5678), hi)
140 },
141 expStackPointerAfterShuffle: 4,
142 },
143 }
144
145 lanes := []uint64{1, 1, 1, 1, 0, 0, 0, 0, 10, 10, 10, 10, 0, 0, 0, 31}
146 v := [16]byte{0: 0xa, 1: 0xb, 10: 0xc}
147 w := [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1}
148 exp := [16]byte{
149 0xb, 0xb, 0xb, 0xb,
150 0xa, 0xa, 0xa, 0xa,
151 0xc, 0xc, 0xc, 0xc,
152 0xa, 0xa, 0xa, 1,
153 }
154
155 for _, tc := range tests {
156 tc := tc
157 t.Run(tc.name, func(t *testing.T) {
158 env := newCompilerEnvironment()
159 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler,
160 &wazeroir.CompilationResult{HasMemory: true})
161
162 err := compiler.compilePreamble()
163 require.NoError(t, err)
164
165 ac := compiler.(*arm64Compiler)
166 tc.init(t, ac)
167
168 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(v[:8]), binary.LittleEndian.Uint64(v[8:]))))
169 require.NoError(t, err)
170
171 vLocation := compiler.runtimeValueLocationStack().peek()
172 movValueRegisterToRegister(t, ac, vLocation, tc.vReg)
173
174 err = compiler.compileV128Const(operationPtr(wazeroir.NewOperationV128Const(binary.LittleEndian.Uint64(w[:8]), binary.LittleEndian.Uint64(w[8:]))))
175 require.NoError(t, err)
176
177 wLocation := compiler.runtimeValueLocationStack().peek()
178 movValueRegisterToRegister(t, ac, wLocation, tc.wReg)
179
180 err = compiler.compileV128Shuffle(operationPtr(wazeroir.NewOperationV128Shuffle(lanes)))
181 require.NoError(t, err)
182
183 requireRuntimeLocationStackPointerEqual(t, tc.expStackPointerAfterShuffle, compiler)
184 require.Equal(t, 1, len(compiler.runtimeValueLocationStack().usedRegisters.list()))
185
186 err = compiler.compileReturnFunction()
187 require.NoError(t, err)
188
189 code := asm.CodeSegment{}
190 defer func() { require.NoError(t, code.Unmap()) }()
191
192
193 _, err = compiler.compile(code.NextCodeSection())
194 require.NoError(t, err)
195
196 env.exec(code.Bytes())
197
198 lo, hi := env.stackTopAsV128()
199 var actual [16]byte
200 binary.LittleEndian.PutUint64(actual[:8], lo)
201 binary.LittleEndian.PutUint64(actual[8:], hi)
202 require.Equal(t, exp, actual)
203
204 tc.verifyFnc(t, env)
205 })
206 }
207 }
208
View as plain text