1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package test
6
7import (
8	"bufio"
9	"cmd/compile/internal/abi"
10	"cmd/compile/internal/base"
11	"cmd/compile/internal/ssagen"
12	"cmd/compile/internal/typecheck"
13	"cmd/compile/internal/types"
14	"cmd/internal/obj"
15	"cmd/internal/obj/x86"
16	"cmd/internal/src"
17	"fmt"
18	"os"
19	"testing"
20)
21
22// AMD64 registers available:
23// - integer: RAX, RBX, RCX, RDI, RSI, R8, R9, r10, R11
24// - floating point: X0 - X14
25var configAMD64 = abi.NewABIConfig(9, 15, 0, 1)
26
27func TestMain(m *testing.M) {
28	ssagen.Arch.LinkArch = &x86.Linkamd64
29	ssagen.Arch.REGSP = x86.REGSP
30	ssagen.Arch.MAXWIDTH = 1 << 50
31	types.MaxWidth = ssagen.Arch.MAXWIDTH
32	base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch)
33	base.Ctxt.DiagFunc = base.Errorf
34	base.Ctxt.DiagFlush = base.FlushErrors
35	base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
36	types.LocalPkg = types.NewPkg("p", "local")
37	types.LocalPkg.Prefix = "p"
38	types.PtrSize = ssagen.Arch.LinkArch.PtrSize
39	types.RegSize = ssagen.Arch.LinkArch.RegSize
40	typecheck.InitUniverse()
41	os.Exit(m.Run())
42}
43
44func TestABIUtilsBasic1(t *testing.T) {
45
46	// func(x int32) int32
47	i32 := types.Types[types.TINT32]
48	ft := mkFuncType(nil, []*types.Type{i32}, []*types.Type{i32})
49
50	// expected results
51	exp := makeExpectedDump(`
52        IN 0: R{ I0 } spilloffset: 0 typ: int32
53        OUT 0: R{ I0 } spilloffset: -1 typ: int32
54        offsetToSpillArea: 0 spillAreaSize: 8
55`)
56
57	abitest(t, ft, exp)
58}
59
60func TestABIUtilsBasic2(t *testing.T) {
61	// func(p1 int8, p2 int16, p3 int32, p4 int64,
62	//      p5 float32, p6 float32, p7 float64, p8 float64,
63	//      p9 int8, p10 int16, p11 int32, p12 int64,
64	//      p13 float32, p14 float32, p15 float64, p16 float64,
65	//      p17 complex128, p18 complex128, p19 complex12, p20 complex128,
66	//      p21 complex64, p22 int8, p23 in16, p24 int32, p25 int64,
67	//      p26 int8, p27 in16, p28 int32, p29 int64)
68	//        (r1 int32, r2 float64, r3 float64) {
69	i8 := types.Types[types.TINT8]
70	i16 := types.Types[types.TINT16]
71	i32 := types.Types[types.TINT32]
72	i64 := types.Types[types.TINT64]
73	f32 := types.Types[types.TFLOAT32]
74	f64 := types.Types[types.TFLOAT64]
75	c64 := types.Types[types.TCOMPLEX64]
76	c128 := types.Types[types.TCOMPLEX128]
77	ft := mkFuncType(nil,
78		[]*types.Type{
79			i8, i16, i32, i64,
80			f32, f32, f64, f64,
81			i8, i16, i32, i64,
82			f32, f32, f64, f64,
83			c128, c128, c128, c128, c64,
84			i8, i16, i32, i64,
85			i8, i16, i32, i64},
86		[]*types.Type{i32, f64, f64})
87	exp := makeExpectedDump(`
88        IN 0: R{ I0 } spilloffset: 0 typ: int8
89        IN 1: R{ I1 } spilloffset: 2 typ: int16
90        IN 2: R{ I2 } spilloffset: 4 typ: int32
91        IN 3: R{ I3 } spilloffset: 8 typ: int64
92        IN 4: R{ F0 } spilloffset: 16 typ: float32
93        IN 5: R{ F1 } spilloffset: 20 typ: float32
94        IN 6: R{ F2 } spilloffset: 24 typ: float64
95        IN 7: R{ F3 } spilloffset: 32 typ: float64
96        IN 8: R{ I4 } spilloffset: 40 typ: int8
97        IN 9: R{ I5 } spilloffset: 42 typ: int16
98        IN 10: R{ I6 } spilloffset: 44 typ: int32
99        IN 11: R{ I7 } spilloffset: 48 typ: int64
100        IN 12: R{ F4 } spilloffset: 56 typ: float32
101        IN 13: R{ F5 } spilloffset: 60 typ: float32
102        IN 14: R{ F6 } spilloffset: 64 typ: float64
103        IN 15: R{ F7 } spilloffset: 72 typ: float64
104        IN 16: R{ F8 F9 } spilloffset: 80 typ: complex128
105        IN 17: R{ F10 F11 } spilloffset: 96 typ: complex128
106        IN 18: R{ F12 F13 } spilloffset: 112 typ: complex128
107        IN 19: R{ } offset: 0 typ: complex128
108        IN 20: R{ } offset: 16 typ: complex64
109        IN 21: R{ I8 } spilloffset: 128 typ: int8
110        IN 22: R{ } offset: 24 typ: int16
111        IN 23: R{ } offset: 28 typ: int32
112        IN 24: R{ } offset: 32 typ: int64
113        IN 25: R{ } offset: 40 typ: int8
114        IN 26: R{ } offset: 42 typ: int16
115        IN 27: R{ } offset: 44 typ: int32
116        IN 28: R{ } offset: 48 typ: int64
117        OUT 0: R{ I0 } spilloffset: -1 typ: int32
118        OUT 1: R{ F0 } spilloffset: -1 typ: float64
119        OUT 2: R{ F1 } spilloffset: -1 typ: float64
120        offsetToSpillArea: 56 spillAreaSize: 136
121`)
122
123	abitest(t, ft, exp)
124}
125
126func TestABIUtilsArrays(t *testing.T) {
127	// func(p1 [1]int32, p2 [0]int32, p3 [1][1]int32, p4 [2]int32)
128	//         (r1 [2]int32, r2 [1]int32, r3 [0]int32, r4 [1][1]int32) {
129	i32 := types.Types[types.TINT32]
130	ae := types.NewArray(i32, 0)
131	a1 := types.NewArray(i32, 1)
132	a2 := types.NewArray(i32, 2)
133	aa1 := types.NewArray(a1, 1)
134	ft := mkFuncType(nil, []*types.Type{a1, ae, aa1, a2},
135		[]*types.Type{a2, a1, ae, aa1})
136
137	exp := makeExpectedDump(`
138        IN 0: R{ I0 } spilloffset: 0 typ: [1]int32
139        IN 1: R{ } offset: 0 typ: [0]int32
140        IN 2: R{ I1 } spilloffset: 4 typ: [1][1]int32
141        IN 3: R{ } offset: 0 typ: [2]int32
142        OUT 0: R{ } offset: 8 typ: [2]int32
143        OUT 1: R{ I0 } spilloffset: -1 typ: [1]int32
144        OUT 2: R{ } offset: 16 typ: [0]int32
145        OUT 3: R{ I1 } spilloffset: -1 typ: [1][1]int32
146        offsetToSpillArea: 16 spillAreaSize: 8
147`)
148
149	abitest(t, ft, exp)
150}
151
152func TestABIUtilsStruct1(t *testing.T) {
153	// type s struct { f1 int8; f2 int8; f3 struct {}; f4 int8; f5 int16) }
154	// func(p1 int6, p2 s, p3 int64)
155	//   (r1 s, r2 int8, r3 int32) {
156	i8 := types.Types[types.TINT8]
157	i16 := types.Types[types.TINT16]
158	i32 := types.Types[types.TINT32]
159	i64 := types.Types[types.TINT64]
160	s := mkstruct(i8, i8, mkstruct(), i8, i16)
161	ft := mkFuncType(nil, []*types.Type{i8, s, i64},
162		[]*types.Type{s, i8, i32})
163
164	exp := makeExpectedDump(`
165        IN 0: R{ I0 } spilloffset: 0 typ: int8
166        IN 1: R{ I1 I2 I3 I4 } spilloffset: 2 typ: struct { int8; int8; struct {}; int8; int16 }
167        IN 2: R{ I5 } spilloffset: 8 typ: int64
168        OUT 0: R{ I0 I1 I2 I3 } spilloffset: -1 typ: struct { int8; int8; struct {}; int8; int16 }
169        OUT 1: R{ I4 } spilloffset: -1 typ: int8
170        OUT 2: R{ I5 } spilloffset: -1 typ: int32
171        offsetToSpillArea: 0 spillAreaSize: 16
172`)
173
174	abitest(t, ft, exp)
175}
176
177func TestABIUtilsStruct2(t *testing.T) {
178	// type s struct { f1 int64; f2 struct { } }
179	// type fs struct { f1 float64; f2 s; f3 struct { } }
180	// func(p1 s, p2 s, p3 fs)
181	//    (r1 fs, r2 fs)
182	f64 := types.Types[types.TFLOAT64]
183	i64 := types.Types[types.TINT64]
184	s := mkstruct(i64, mkstruct())
185	fs := mkstruct(f64, s, mkstruct())
186	ft := mkFuncType(nil, []*types.Type{s, s, fs},
187		[]*types.Type{fs, fs})
188
189	exp := makeExpectedDump(`
190        IN 0: R{ I0 } spilloffset: 0 typ: struct { int64; struct {} }
191        IN 1: R{ I1 } spilloffset: 16 typ: struct { int64; struct {} }
192        IN 2: R{ F0 I2 } spilloffset: 32 typ: struct { float64; struct { int64; struct {} }; struct {} }
193        OUT 0: R{ F0 I0 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
194        OUT 1: R{ F1 I1 } spilloffset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} }
195        offsetToSpillArea: 0 spillAreaSize: 64
196`)
197
198	abitest(t, ft, exp)
199}
200
201// TestABIUtilsEmptyFieldAtEndOfStruct is testing to make sure
202// the abi code is doing the right thing for struct types that have
203// a trailing zero-sized field (where the we need to add padding).
204func TestABIUtilsEmptyFieldAtEndOfStruct(t *testing.T) {
205	// type s struct { f1 [2]int64; f2 struct { } }
206	// type s2 struct { f1 [3]int16; f2 struct { } }
207	// type fs struct { f1 float64; f s; f3 struct { } }
208	// func(p1 s, p2 s, p3 fs)  (r1 fs, r2 fs)
209	f64 := types.Types[types.TFLOAT64]
210	i64 := types.Types[types.TINT64]
211	i16 := types.Types[types.TINT16]
212	tb := types.Types[types.TBOOL]
213	ab2 := types.NewArray(tb, 2)
214	a2 := types.NewArray(i64, 2)
215	a3 := types.NewArray(i16, 3)
216	empty := mkstruct()
217	s := mkstruct(a2, empty)
218	s2 := mkstruct(a3, empty)
219	fs := mkstruct(f64, s, empty)
220	ft := mkFuncType(nil, []*types.Type{s, ab2, s2, fs, fs},
221		[]*types.Type{fs, ab2, fs})
222
223	exp := makeExpectedDump(`
224        IN 0: R{ } offset: 0 typ: struct { [2]int64; struct {} }
225        IN 1: R{ } offset: 24 typ: [2]bool
226        IN 2: R{ } offset: 26 typ: struct { [3]int16; struct {} }
227        IN 3: R{ } offset: 40 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
228        IN 4: R{ } offset: 80 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
229        OUT 0: R{ } offset: 120 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
230        OUT 1: R{ } offset: 160 typ: [2]bool
231        OUT 2: R{ } offset: 168 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
232        offsetToSpillArea: 208 spillAreaSize: 0
233`)
234
235	abitest(t, ft, exp)
236
237	// Test that NumParamRegs doesn't assign registers to trailing padding.
238	typ := mkstruct(i64, i64, mkstruct())
239	have := configAMD64.NumParamRegs(typ)
240	if have != 2 {
241		t.Errorf("NumParams(%v): have %v, want %v", typ, have, 2)
242	}
243}
244
245func TestABIUtilsSliceString(t *testing.T) {
246	// func(p1 []int32, p2 int8, p3 []int32, p4 int8, p5 string,
247	//      p6 int64, p6 []intr32) (r1 string, r2 int64, r3 string, r4 []int32)
248	i32 := types.Types[types.TINT32]
249	sli32 := types.NewSlice(i32)
250	str := types.Types[types.TSTRING]
251	i8 := types.Types[types.TINT8]
252	i64 := types.Types[types.TINT64]
253	ft := mkFuncType(nil, []*types.Type{sli32, i8, sli32, i8, str, i8, i64, sli32},
254		[]*types.Type{str, i64, str, sli32})
255
256	exp := makeExpectedDump(`
257        IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: []int32
258        IN 1: R{ I3 } spilloffset: 24 typ: int8
259        IN 2: R{ I4 I5 I6 } spilloffset: 32 typ: []int32
260        IN 3: R{ I7 } spilloffset: 56 typ: int8
261        IN 4: R{ } offset: 0 typ: string
262        IN 5: R{ I8 } spilloffset: 57 typ: int8
263        IN 6: R{ } offset: 16 typ: int64
264        IN 7: R{ } offset: 24 typ: []int32
265        OUT 0: R{ I0 I1 } spilloffset: -1 typ: string
266        OUT 1: R{ I2 } spilloffset: -1 typ: int64
267        OUT 2: R{ I3 I4 } spilloffset: -1 typ: string
268        OUT 3: R{ I5 I6 I7 } spilloffset: -1 typ: []int32
269        offsetToSpillArea: 48 spillAreaSize: 64
270`)
271
272	abitest(t, ft, exp)
273}
274
275func TestABIUtilsMethod(t *testing.T) {
276	// type s1 struct { f1 int16; f2 int16; f3 int16 }
277	// func(p1 *s1, p2 [7]*s1, p3 float64, p4 int16, p5 int16, p6 int16)
278	//   (r1 [7]*s1, r2 float64, r3 int64)
279	i16 := types.Types[types.TINT16]
280	i64 := types.Types[types.TINT64]
281	f64 := types.Types[types.TFLOAT64]
282	s1 := mkstruct(i16, i16, i16)
283	ps1 := types.NewPtr(s1)
284	a7 := types.NewArray(ps1, 7)
285	ft := mkFuncType(s1, []*types.Type{ps1, a7, f64, i16, i16, i16},
286		[]*types.Type{a7, f64, i64})
287
288	exp := makeExpectedDump(`
289        IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: struct { int16; int16; int16 }
290        IN 1: R{ I3 } spilloffset: 8 typ: *struct { int16; int16; int16 }
291        IN 2: R{ } offset: 0 typ: [7]*struct { int16; int16; int16 }
292        IN 3: R{ F0 } spilloffset: 16 typ: float64
293        IN 4: R{ I4 } spilloffset: 24 typ: int16
294        IN 5: R{ I5 } spilloffset: 26 typ: int16
295        IN 6: R{ I6 } spilloffset: 28 typ: int16
296        OUT 0: R{ } offset: 56 typ: [7]*struct { int16; int16; int16 }
297        OUT 1: R{ F0 } spilloffset: -1 typ: float64
298        OUT 2: R{ I0 } spilloffset: -1 typ: int64
299        offsetToSpillArea: 112 spillAreaSize: 32
300`)
301
302	abitest(t, ft, exp)
303}
304
305func TestABIUtilsInterfaces(t *testing.T) {
306	// type s1 { f1 int16; f2 int16; f3 bool)
307	// type nei interface { ...() string }
308	// func(p1 s1, p2 interface{}, p3 interface{}, p4 nei,
309	//      p5 *interface{}, p6 nei, p7 int64)
310	//    (r1 interface{}, r2 nei, r3 bool)
311	ei := types.Types[types.TINTER] // interface{}
312	pei := types.NewPtr(ei)         // *interface{}
313	fldt := mkFuncType(types.FakeRecvType(), []*types.Type{},
314		[]*types.Type{types.Types[types.TSTRING]})
315	field := types.NewField(src.NoXPos, typecheck.Lookup("F"), fldt)
316	nei := types.NewInterface([]*types.Field{field})
317	i16 := types.Types[types.TINT16]
318	tb := types.Types[types.TBOOL]
319	s1 := mkstruct(i16, i16, tb)
320	ft := mkFuncType(nil, []*types.Type{s1, ei, ei, nei, pei, nei, i16},
321		[]*types.Type{ei, nei, pei})
322
323	exp := makeExpectedDump(`
324        IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: struct { int16; int16; bool }
325        IN 1: R{ I3 I4 } spilloffset: 8 typ: interface {}
326        IN 2: R{ I5 I6 } spilloffset: 24 typ: interface {}
327        IN 3: R{ I7 I8 } spilloffset: 40 typ: interface { F() string }
328        IN 4: R{ } offset: 0 typ: *interface {}
329        IN 5: R{ } offset: 8 typ: interface { F() string }
330        IN 6: R{ } offset: 24 typ: int16
331        OUT 0: R{ I0 I1 } spilloffset: -1 typ: interface {}
332        OUT 1: R{ I2 I3 } spilloffset: -1 typ: interface { F() string }
333        OUT 2: R{ I4 } spilloffset: -1 typ: *interface {}
334        offsetToSpillArea: 32 spillAreaSize: 56
335`)
336
337	abitest(t, ft, exp)
338}
339
340func TestABINumParamRegs(t *testing.T) {
341	i8 := types.Types[types.TINT8]
342	i16 := types.Types[types.TINT16]
343	i32 := types.Types[types.TINT32]
344	i64 := types.Types[types.TINT64]
345	f32 := types.Types[types.TFLOAT32]
346	f64 := types.Types[types.TFLOAT64]
347	c64 := types.Types[types.TCOMPLEX64]
348	c128 := types.Types[types.TCOMPLEX128]
349
350	s := mkstruct(i8, i8, mkstruct(), i8, i16)
351	a := mkstruct(s, s, s)
352
353	nrtest(t, i8, 1)
354	nrtest(t, i16, 1)
355	nrtest(t, i32, 1)
356	nrtest(t, i64, 1)
357	nrtest(t, f32, 1)
358	nrtest(t, f64, 1)
359	nrtest(t, c64, 2)
360	nrtest(t, c128, 2)
361	nrtest(t, s, 4)
362	nrtest(t, a, 12)
363}
364
365func TestABIUtilsComputePadding(t *testing.T) {
366	// type s1 { f1 int8; f2 int16; f3 struct{}; f4 int32; f5 int64 }
367	i8 := types.Types[types.TINT8]
368	i16 := types.Types[types.TINT16]
369	i32 := types.Types[types.TINT32]
370	i64 := types.Types[types.TINT64]
371	emptys := mkstruct()
372	s1 := mkstruct(i8, i16, emptys, i32, i64)
373	// func (p1 int32, p2 s1, p3 emptys, p4 [1]int32)
374	a1 := types.NewArray(i32, 1)
375	ft := mkFuncType(nil, []*types.Type{i32, s1, emptys, a1}, nil)
376
377	// Run abitest() just to document what we're expected to see.
378	exp := makeExpectedDump(`
379        IN 0: R{ I0 } spilloffset: 0 typ: int32
380        IN 1: R{ I1 I2 I3 I4 } spilloffset: 8 typ: struct { int8; int16; struct {}; int32; int64 }
381        IN 2: R{ } offset: 0 typ: struct {}
382        IN 3: R{ I5 } spilloffset: 24 typ: [1]int32
383        offsetToSpillArea: 0 spillAreaSize: 32
384`)
385	abitest(t, ft, exp)
386
387	// Analyze with full set of registers, then call ComputePadding
388	// on the second param, verifying the results.
389	regRes := configAMD64.ABIAnalyze(ft, false)
390	padding := make([]uint64, 32)
391	parm := regRes.InParams()[1]
392	padding = parm.ComputePadding(padding)
393	want := "[1 1 1 0]"
394	got := fmt.Sprintf("%+v", padding)
395	if got != want {
396		t.Errorf("padding mismatch: wanted %q got %q\n", got, want)
397	}
398}
399