1 package compiler
2
3 import (
4 "testing"
5 "unsafe"
6
7 "github.com/tetratelabs/wazero/internal/asm"
8 arm64 "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
17 func TestArm64Compiler_indirectCallWithTargetOnCallingConvReg(t *testing.T) {
18 env := newCompilerEnvironment()
19 table := make([]wasm.Reference, 1)
20 env.addTable(&wasm.TableInstance{References: table})
21
22
23 operation := operationPtr(wazeroir.NewOperationCallIndirect(0, 0))
24 env.module().TypeIDs = []wasm.FunctionTypeID{0}
25 env.module().Engine = &moduleEngine{functions: []function{}}
26
27 me := env.moduleEngine()
28 {
29 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, nil)
30 err := compiler.compilePreamble()
31 require.NoError(t, err)
32 err = compiler.compileReturnFunction()
33 require.NoError(t, err)
34
35 code := asm.CodeSegment{}
36 defer func() { require.NoError(t, code.Unmap()) }()
37
38 _, err = compiler.compile(code.NextCodeSection())
39 require.NoError(t, err)
40
41 executable := code.Bytes()
42 makeExecutable(executable)
43
44 f := function{
45 parent: &compiledFunction{parent: &compiledCode{executable: code}},
46 codeInitialAddress: code.Addr(),
47 moduleInstance: env.moduleInstance,
48 }
49 me.functions = append(me.functions, f)
50 table[0] = uintptr(unsafe.Pointer(&f))
51 }
52
53 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
54 Types: []wasm.FunctionType{{}},
55 HasTable: true,
56 }).(*arm64Compiler)
57 err := compiler.compilePreamble()
58 require.NoError(t, err)
59
60
61 offsetLoc := compiler.pushRuntimeValueLocationOnRegister(arm64CallingConventionModuleInstanceAddressRegister,
62 runtimeValueTypeI32)
63 compiler.assembler.CompileConstToRegister(arm64.MOVD, 0, offsetLoc.register)
64
65 require.NoError(t, compiler.compileCallIndirect(operation))
66
67 err = compiler.compileReturnFunction()
68 require.NoError(t, err)
69
70 code := asm.CodeSegment{}
71 defer func() { require.NoError(t, code.Unmap()) }()
72
73
74 _, err = compiler.compile(code.NextCodeSection())
75 require.NoError(t, err)
76 env.exec(code.Bytes())
77 }
78
79 func TestArm64Compiler_readInstructionAddress(t *testing.T) {
80 env := newCompilerEnvironment()
81 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newArm64Compiler, nil).(*arm64Compiler)
82
83 err := compiler.compilePreamble()
84 require.NoError(t, err)
85
86
87
88 const addressReg = arm64ReservedRegisterForTemporary
89 compiler.assembler.CompileReadInstructionAddress(addressReg, arm64.RET)
90
91
92
93 compiler.assembler.CompileJumpToRegister(arm64.B, addressReg)
94
95
96
97 compiler.compileExitFromNativeCode(nativeCallStatusCodeUnreachable)
98
99
100
101
102 err = compiler.compileReturnFunction()
103 require.NoError(t, err)
104
105 code := asm.CodeSegment{}
106 defer func() { require.NoError(t, code.Unmap()) }()
107
108 _, err = compiler.compile(code.NextCodeSection())
109 require.NoError(t, err)
110 env.exec(code.Bytes())
111
112 require.Equal(t, nativeCallStatusCodeReturned, env.compilerStatus())
113 }
114
115 func TestArm64Compiler_label(t *testing.T) {
116 c := &arm64Compiler{}
117 c.label(wazeroir.NewLabel(wazeroir.LabelKindContinuation, 100))
118 require.Equal(t, 100, c.frameIDMax)
119 require.Equal(t, 101, len(c.labels[wazeroir.LabelKindContinuation]))
120
121
122 c.label(wazeroir.NewLabel(wazeroir.LabelKindHeader, 2))
123 require.Equal(t, 100, c.frameIDMax)
124 require.Equal(t, 3, len(c.labels[wazeroir.LabelKindHeader]))
125 }
126
127 func TestArm64Compiler_Init(t *testing.T) {
128 c := &arm64Compiler{
129 locationStackForEntrypoint: newRuntimeValueLocationStack(),
130 assembler: arm64.NewAssembler(0),
131 }
132 const stackCap = 12345
133 c.locationStackForEntrypoint.stack = make([]runtimeValueLocation, stackCap)
134 c.locationStackForEntrypoint.sp = 5555
135
136 c.Init(&wasm.FunctionType{}, nil, false)
137
138
139 require.Equal(t, c.locationStack, &c.locationStackForEntrypoint)
140
141 require.Equal(t, stackCap, cap(c.locationStack.stack))
142 require.Equal(t, stackCap, cap(c.locationStackForEntrypoint.stack))
143 }
144
145 func TestArm64Compiler_resetLabels(t *testing.T) {
146 c := newArm64Compiler().(*arm64Compiler)
147 nop := c.compileNOP()
148
149 const (
150 frameIDMax = 50
151 capacity = 12345
152 )
153 c.frameIDMax = frameIDMax
154 for i := range c.labels {
155 ifs := make([]arm64LabelInfo, frameIDMax*2)
156 c.labels[i] = ifs
157 for j := 0; j <= frameIDMax; j++ {
158 ifs[j].stackInitialized = true
159 ifs[j].initialInstruction = nop
160 ifs[j].initialStack = newRuntimeValueLocationStack()
161 ifs[j].initialStack.sp = 5555
162 ifs[j].initialStack.stack = make([]runtimeValueLocation, 0, capacity)
163 }
164 }
165 c.resetLabels()
166 for i := range c.labels {
167 for j := 0; j < len(c.labels[i]); j++ {
168 l := &c.labels[i][j]
169 require.False(t, l.stackInitialized)
170 require.Nil(t, l.initialInstruction)
171 require.Equal(t, 0, len(l.initialStack.stack))
172 if j > frameIDMax {
173 require.Equal(t, 0, cap(l.initialStack.stack))
174 } else {
175 require.Equal(t, capacity, cap(l.initialStack.stack))
176 }
177 require.Equal(t, uint64(0), l.initialStack.sp)
178 }
179 }
180 }
181
182 func TestArm64Compiler_getSavedTemporaryLocationStack(t *testing.T) {
183 t.Run("len(brTableTmp)<len(current)", func(t *testing.T) {
184 st := newRuntimeValueLocationStack()
185 c := &arm64Compiler{locationStack: &st}
186
187 c.locationStack.sp = 3
188 c.locationStack.stack = []runtimeValueLocation{{stackPointer: 150}, {stackPointer: 200}, {stackPointer: 300}}
189
190 actual := c.getSavedTemporaryLocationStack()
191 require.Equal(t, uint64(3), actual.sp)
192 require.Equal(t, 3, len(actual.stack))
193 require.Equal(t, c.locationStack.stack[:3], actual.stack)
194 })
195 t.Run("len(brTableTmp)==len(current)", func(t *testing.T) {
196 st := newRuntimeValueLocationStack()
197 c := &arm64Compiler{locationStack: &st, brTableTmp: make([]runtimeValueLocation, 3)}
198 initSlicePtr := &c.brTableTmp
199
200 c.locationStack.sp = 3
201 c.locationStack.stack = []runtimeValueLocation{{stackPointer: 150}, {stackPointer: 200}, {stackPointer: 300}}
202
203 actual := c.getSavedTemporaryLocationStack()
204 require.Equal(t, uint64(3), actual.sp)
205 require.Equal(t, 3, len(actual.stack))
206 require.Equal(t, c.locationStack.stack[:3], actual.stack)
207
208 require.Equal(t, initSlicePtr, &c.brTableTmp)
209 })
210
211 t.Run("len(brTableTmp)>len(current)", func(t *testing.T) {
212 const temporarySliceSize = 100
213 st := newRuntimeValueLocationStack()
214 c := &arm64Compiler{locationStack: &st, brTableTmp: make([]runtimeValueLocation, temporarySliceSize)}
215
216 c.locationStack.sp = 3
217 c.locationStack.stack = []runtimeValueLocation{
218 {stackPointer: 150},
219 {stackPointer: 200},
220 {stackPointer: 300},
221 {},
222 {},
223 {},
224 {},
225 {stackPointer: 1231455},
226 }
227
228 actual := c.getSavedTemporaryLocationStack()
229 require.Equal(t, uint64(3), actual.sp)
230 require.Equal(t, temporarySliceSize, len(actual.stack))
231 require.Equal(t, c.locationStack.stack[:3], actual.stack[:3])
232 for i := int(actual.sp); i < len(actual.stack); i++ {
233
234 require.Zero(t, actual.stack[i].stackPointer)
235 }
236 })
237 }
238
239
240 func TestArm64Compiler_LargeTrapOffsets(t *testing.T) {
241 env := newCompilerEnvironment()
242 compiler := env.requireNewCompiler(t, &wasm.FunctionType{}, newCompiler, &wazeroir.CompilationResult{
243 Types: []wasm.FunctionType{{}},
244 })
245 err := compiler.compilePreamble()
246 require.NoError(t, err)
247
248 one := operationPtr(wazeroir.NewOperationConstI32(uint32(1)))
249 five := operationPtr(wazeroir.NewOperationConstI32(uint32(5)))
250 div := operationPtr(wazeroir.NewOperationDiv(wazeroir.SignedTypeInt32))
251
252
253 err = compiler.compileConstI32(one)
254 require.NoError(t, err)
255
256
257
258
259
260
261
262 for i := 0; i < 80_000; i++ {
263 err = compiler.compileConstI32(five)
264 require.NoError(t, err)
265
266 err = compiler.compileDiv(div)
267 require.NoError(t, err)
268 }
269
270 err = compiler.compileReturnFunction()
271 require.NoError(t, err)
272
273 code := asm.CodeSegment{}
274 defer func() { require.NoError(t, code.Unmap()) }()
275
276
277 _, err = compiler.compile(code.NextCodeSection())
278 require.NoError(t, err)
279
280 env.exec(code.Bytes())
281
282 require.Equal(t, nativeCallStatusCodeReturned.String(), env.compilerStatus().String())
283 }
284
285
286 func (c *arm64Compiler) setStackPointerCeil(v uint64) {
287 c.stackPointerCeil = v
288 }
289
290
291 func (c *arm64Compiler) setRuntimeValueLocationStack(s *runtimeValueLocationStack) {
292 c.locationStack = s
293 }
294
View as plain text