Source file src/cmd/compile/internal/test/abiutils_test.go

     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  
     5  package test
     6  
     7  import (
     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
    25  var configAMD64 = abi.NewABIConfig(9, 15, 0)
    26  
    27  func 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("", "local")
    37  	types.LocalPkg.Prefix = `""`
    38  	types.PtrSize = ssagen.Arch.LinkArch.PtrSize
    39  	types.RegSize = ssagen.Arch.LinkArch.RegSize
    40  	typecheck.InitUniverse()
    41  	os.Exit(m.Run())
    42  }
    43  
    44  func 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  
    60  func 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  
   126  func 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  
   152  func 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([]*types.Type{i8, i8, mkstruct([]*types.Type{}), 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  
   177  func 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([]*types.Type{i64, mkstruct([]*types.Type{})})
   185  	fs := mkstruct([]*types.Type{f64, s, mkstruct([]*types.Type{})})
   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).
   204  func 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  	s := mkstruct([]*types.Type{a2, mkstruct([]*types.Type{})})
   217  	s2 := mkstruct([]*types.Type{a3, mkstruct([]*types.Type{})})
   218  	fs := mkstruct([]*types.Type{f64, s, mkstruct([]*types.Type{})})
   219  	ft := mkFuncType(nil, []*types.Type{s, ab2, s2, fs, fs},
   220  		[]*types.Type{fs, ab2, fs})
   221  
   222  	exp := makeExpectedDump(`
   223          IN 0: R{ } offset: 0 typ: struct { [2]int64; struct {} }
   224          IN 1: R{ } offset: 24 typ: [2]bool
   225          IN 2: R{ } offset: 26 typ: struct { [3]int16; struct {} }
   226          IN 3: R{ } offset: 40 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
   227          IN 4: R{ } offset: 80 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
   228          OUT 0: R{ } offset: 120 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
   229          OUT 1: R{ } offset: 160 typ: [2]bool
   230          OUT 2: R{ } offset: 168 typ: struct { float64; struct { [2]int64; struct {} }; struct {} }
   231          offsetToSpillArea: 208 spillAreaSize: 0
   232  `)
   233  
   234  	abitest(t, ft, exp)
   235  
   236  	// Check to make sure that NumParamRegs yields 2 and not 3
   237  	// for struct "s" (e.g. that it handles the padding properly).
   238  	nps := configAMD64.NumParamRegs(s)
   239  	if nps != 2 {
   240  		t.Errorf("NumParams(%v) returned %d expected %d\n",
   241  			s, nps, 2)
   242  	}
   243  }
   244  
   245  func 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  
   275  func 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([]*types.Type{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  
   305  func 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.LocalPkg, []*types.Field{field}, false)
   317  	i16 := types.Types[types.TINT16]
   318  	tb := types.Types[types.TBOOL]
   319  	s1 := mkstruct([]*types.Type{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  
   340  func 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([]*types.Type{i8, i8, mkstruct([]*types.Type{}), i8, i16})
   351  	a := types.NewArray(s, 3)
   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  }
   365  
   366  func TestABIUtilsComputePadding(t *testing.T) {
   367  	// type s1 { f1 int8; f2 int16; f3 struct{}; f4 int32; f5 int64 }
   368  	i8 := types.Types[types.TINT8]
   369  	i16 := types.Types[types.TINT16]
   370  	i32 := types.Types[types.TINT32]
   371  	i64 := types.Types[types.TINT64]
   372  	emptys := mkstruct([]*types.Type{})
   373  	s1 := mkstruct([]*types.Type{i8, i16, emptys, i32, i64})
   374  	// func (p1 int32, p2 s1, p3 emptys, p4 [1]int32)
   375  	a1 := types.NewArray(i32, 1)
   376  	ft := mkFuncType(nil, []*types.Type{i32, s1, emptys, a1}, []*types.Type{})
   377  
   378  	// Run abitest() just to document what we're expected to see.
   379  	exp := makeExpectedDump(`
   380          IN 0: R{ I0 } spilloffset: 0 typ: int32
   381          IN 1: R{ I1 I2 I3 I4 } spilloffset: 8 typ: struct { int8; int16; struct {}; int32; int64 }
   382          IN 2: R{ } offset: 0 typ: struct {}
   383          IN 3: R{ I5 } spilloffset: 24 typ: [1]int32
   384          offsetToSpillArea: 0 spillAreaSize: 32
   385  `)
   386  	abitest(t, ft, exp)
   387  
   388  	// Analyze with full set of registers, then call ComputePadding
   389  	// on the second param, verifying the results.
   390  	regRes := configAMD64.ABIAnalyze(ft, false)
   391  	padding := make([]uint64, 32)
   392  	parm := regRes.InParams()[1]
   393  	padding = parm.ComputePadding(padding)
   394  	want := "[1 1 1 0]"
   395  	got := fmt.Sprintf("%+v", padding)
   396  	if got != want {
   397  		t.Errorf("padding mismatch: wanted %q got %q\n", got, want)
   398  	}
   399  }
   400  

View as plain text