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