diff options
author | David Chase <drchase@google.com> | 2021-01-08 10:15:36 -0500 |
---|---|---|
committer | David Chase <drchase@google.com> | 2021-01-13 15:54:19 +0000 |
commit | c41b999ad410c74bea222ee76488226a06ba4046 (patch) | |
tree | f50ac1f597df6de6b0a7f5399142d78718d6bfec /src/cmd/compile/internal/gc | |
parent | 861707a8c84f0b1ddbcaea0e9f439398ee2175fb (diff) | |
download | go-c41b999ad410c74bea222ee76488226a06ba4046.tar.gz go-c41b999ad410c74bea222ee76488226a06ba4046.zip |
[dev.regabi] cmd/compile: refactor abiutils from "gc" into new "abi"
Needs to be visible to ssagen, and might as well start clean to avoid
creating a lot of accidental dependencies.
Added some methods for export.
Decided to use a pointer instead of value for ABIConfig uses.
Tests ended up separate from abiutil itself; otherwise there are import cycles.
Change-Id: I5570e1e6a463e303c5e2dc84e8dd4125e7c1adcc
Reviewed-on: https://go-review.googlesource.com/c/go/+/282614
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Diffstat (limited to 'src/cmd/compile/internal/gc')
-rw-r--r-- | src/cmd/compile/internal/gc/abiutils.go | 351 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/abiutils_test.go | 273 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/abiutilsaux_test.go | 155 |
3 files changed, 0 insertions, 779 deletions
diff --git a/src/cmd/compile/internal/gc/abiutils.go b/src/cmd/compile/internal/gc/abiutils.go deleted file mode 100644 index 5822c088f9..0000000000 --- a/src/cmd/compile/internal/gc/abiutils.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gc - -import ( - "cmd/compile/internal/types" - "cmd/internal/src" - "fmt" - "sync" -) - -//...................................................................... -// -// Public/exported bits of the ABI utilities. -// - -// ABIParamResultInfo stores the results of processing a given -// function type to compute stack layout and register assignments. For -// each input and output parameter we capture whether the param was -// register-assigned (and to which register(s)) or the stack offset -// for the param if is not going to be passed in registers according -// to the rules in the Go internal ABI specification (1.17). -type ABIParamResultInfo struct { - inparams []ABIParamAssignment // Includes receiver for method calls. Does NOT include hidden closure pointer. - outparams []ABIParamAssignment - intSpillSlots int - floatSpillSlots int - offsetToSpillArea int64 - config ABIConfig // to enable String() method -} - -// RegIndex stores the index into the set of machine registers used by -// the ABI on a specific architecture for parameter passing. RegIndex -// values 0 through N-1 (where N is the number of integer registers -// used for param passing according to the ABI rules) describe integer -// registers; values N through M (where M is the number of floating -// point registers used). Thus if the ABI says there are 5 integer -// registers and 7 floating point registers, then RegIndex value of 4 -// indicates the 5th integer register, and a RegIndex value of 11 -// indicates the 7th floating point register. -type RegIndex uint8 - -// ABIParamAssignment holds information about how a specific param or -// result will be passed: in registers (in which case 'Registers' is -// populated) or on the stack (in which case 'Offset' is set to a -// non-negative stack offset. The values in 'Registers' are indices (as -// described above), not architected registers. -type ABIParamAssignment struct { - Type *types.Type - Registers []RegIndex - Offset int32 -} - -// RegAmounts holds a specified number of integer/float registers. -type RegAmounts struct { - intRegs int - floatRegs int -} - -// ABIConfig captures the number of registers made available -// by the ABI rules for parameter passing and result returning. -type ABIConfig struct { - // Do we need anything more than this? - regAmounts RegAmounts -} - -// ABIAnalyze takes a function type 't' and an ABI rules description -// 'config' and analyzes the function to determine how its parameters -// and results will be passed (in registers or on the stack), returning -// an ABIParamResultInfo object that holds the results of the analysis. -func ABIAnalyze(t *types.Type, config ABIConfig) ABIParamResultInfo { - setup() - s := assignState{ - rTotal: config.regAmounts, - } - result := ABIParamResultInfo{config: config} - - // Receiver - ft := t.FuncType() - if t.NumRecvs() != 0 { - rfsl := ft.Receiver.FieldSlice() - result.inparams = append(result.inparams, - s.assignParamOrReturn(rfsl[0].Type)) - } - - // Inputs - ifsl := ft.Params.FieldSlice() - for _, f := range ifsl { - result.inparams = append(result.inparams, - s.assignParamOrReturn(f.Type)) - } - s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize)) - - // Record number of spill slots needed. - result.intSpillSlots = s.rUsed.intRegs - result.floatSpillSlots = s.rUsed.floatRegs - - // Outputs - s.rUsed = RegAmounts{} - ofsl := ft.Results.FieldSlice() - for _, f := range ofsl { - result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type)) - } - result.offsetToSpillArea = s.stackOffset - - return result -} - -//...................................................................... -// -// Non-public portions. - -// regString produces a human-readable version of a RegIndex. -func (c *RegAmounts) regString(r RegIndex) string { - if int(r) < c.intRegs { - return fmt.Sprintf("I%d", int(r)) - } else if int(r) < c.intRegs+c.floatRegs { - return fmt.Sprintf("F%d", int(r)-c.intRegs) - } - return fmt.Sprintf("<?>%d", r) -} - -// toString method renders an ABIParamAssignment in human-readable -// form, suitable for debugging or unit testing. -func (ri *ABIParamAssignment) toString(config ABIConfig) string { - regs := "R{" - for _, r := range ri.Registers { - regs += " " + config.regAmounts.regString(r) - } - return fmt.Sprintf("%s } offset: %d typ: %v", regs, ri.Offset, ri.Type) -} - -// toString method renders an ABIParamResultInfo in human-readable -// form, suitable for debugging or unit testing. -func (ri *ABIParamResultInfo) String() string { - res := "" - for k, p := range ri.inparams { - res += fmt.Sprintf("IN %d: %s\n", k, p.toString(ri.config)) - } - for k, r := range ri.outparams { - res += fmt.Sprintf("OUT %d: %s\n", k, r.toString(ri.config)) - } - res += fmt.Sprintf("intspill: %d floatspill: %d offsetToSpillArea: %d", - ri.intSpillSlots, ri.floatSpillSlots, ri.offsetToSpillArea) - return res -} - -// assignState holds intermediate state during the register assigning process -// for a given function signature. -type assignState struct { - rTotal RegAmounts // total reg amounts from ABI rules - rUsed RegAmounts // regs used by params completely assigned so far - pUsed RegAmounts // regs used by the current param (or pieces therein) - stackOffset int64 // current stack offset -} - -// stackSlot returns a stack offset for a param or result of the -// specified type. -func (state *assignState) stackSlot(t *types.Type) int64 { - if t.Align > 0 { - state.stackOffset = types.Rnd(state.stackOffset, int64(t.Align)) - } - rv := state.stackOffset - state.stackOffset += t.Width - return rv -} - -// allocateRegs returns a set of register indices for a parameter or result -// that we've just determined to be register-assignable. The number of registers -// needed is assumed to be stored in state.pUsed. -func (state *assignState) allocateRegs() []RegIndex { - regs := []RegIndex{} - - // integer - for r := state.rUsed.intRegs; r < state.rUsed.intRegs+state.pUsed.intRegs; r++ { - regs = append(regs, RegIndex(r)) - } - state.rUsed.intRegs += state.pUsed.intRegs - - // floating - for r := state.rUsed.floatRegs; r < state.rUsed.floatRegs+state.pUsed.floatRegs; r++ { - regs = append(regs, RegIndex(r+state.rTotal.intRegs)) - } - state.rUsed.floatRegs += state.pUsed.floatRegs - - return regs -} - -// regAllocate creates a register ABIParamAssignment object for a param -// or result with the specified type, as a final step (this assumes -// that all of the safety/suitability analysis is complete). -func (state *assignState) regAllocate(t *types.Type) ABIParamAssignment { - return ABIParamAssignment{ - Type: t, - Registers: state.allocateRegs(), - Offset: -1, - } -} - -// stackAllocate creates a stack memory ABIParamAssignment object for -// a param or result with the specified type, as a final step (this -// assumes that all of the safety/suitability analysis is complete). -func (state *assignState) stackAllocate(t *types.Type) ABIParamAssignment { - return ABIParamAssignment{ - Type: t, - Offset: int32(state.stackSlot(t)), - } -} - -// intUsed returns the number of integer registers consumed -// at a given point within an assignment stage. -func (state *assignState) intUsed() int { - return state.rUsed.intRegs + state.pUsed.intRegs -} - -// floatUsed returns the number of floating point registers consumed at -// a given point within an assignment stage. -func (state *assignState) floatUsed() int { - return state.rUsed.floatRegs + state.pUsed.floatRegs -} - -// regassignIntegral examines a param/result of integral type 't' to -// determines whether it can be register-assigned. Returns TRUE if we -// can register allocate, FALSE otherwise (and updates state -// accordingly). -func (state *assignState) regassignIntegral(t *types.Type) bool { - regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize)) - - // Floating point and complex. - if t.IsFloat() || t.IsComplex() { - if regsNeeded+state.floatUsed() > state.rTotal.floatRegs { - // not enough regs - return false - } - state.pUsed.floatRegs += regsNeeded - return true - } - - // Non-floating point - if regsNeeded+state.intUsed() > state.rTotal.intRegs { - // not enough regs - return false - } - state.pUsed.intRegs += regsNeeded - return true -} - -// regassignArray processes an array type (or array component within some -// other enclosing type) to determine if it can be register assigned. -// Returns TRUE if we can register allocate, FALSE otherwise. -func (state *assignState) regassignArray(t *types.Type) bool { - - nel := t.NumElem() - if nel == 0 { - return true - } - if nel > 1 { - // Not an array of length 1: stack assign - return false - } - // Visit element - return state.regassign(t.Elem()) -} - -// regassignStruct processes a struct type (or struct component within -// some other enclosing type) to determine if it can be register -// assigned. Returns TRUE if we can register allocate, FALSE otherwise. -func (state *assignState) regassignStruct(t *types.Type) bool { - for _, field := range t.FieldSlice() { - if !state.regassign(field.Type) { - return false - } - } - return true -} - -// synthOnce ensures that we only create the synth* fake types once. -var synthOnce sync.Once - -// synthSlice, synthString, and syncIface are synthesized struct types -// meant to capture the underlying implementations of string/slice/interface. -var synthSlice *types.Type -var synthString *types.Type -var synthIface *types.Type - -// setup performs setup for the register assignment utilities, manufacturing -// a small set of synthesized types that we'll need along the way. -func setup() { - synthOnce.Do(func() { - fname := types.BuiltinPkg.Lookup - nxp := src.NoXPos - unsp := types.Types[types.TUNSAFEPTR] - ui := types.Types[types.TUINTPTR] - synthSlice = types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(nxp, fname("ptr"), unsp), - types.NewField(nxp, fname("len"), ui), - types.NewField(nxp, fname("cap"), ui), - }) - synthString = types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(nxp, fname("data"), unsp), - types.NewField(nxp, fname("len"), ui), - }) - synthIface = types.NewStruct(types.NoPkg, []*types.Field{ - types.NewField(nxp, fname("f1"), unsp), - types.NewField(nxp, fname("f2"), unsp), - }) - }) -} - -// regassign examines a given param type (or component within some -// composite) to determine if it can be register assigned. Returns -// TRUE if we can register allocate, FALSE otherwise. -func (state *assignState) regassign(pt *types.Type) bool { - typ := pt.Kind() - if pt.IsScalar() || pt.IsPtrShaped() { - return state.regassignIntegral(pt) - } - switch typ { - case types.TARRAY: - return state.regassignArray(pt) - case types.TSTRUCT: - return state.regassignStruct(pt) - case types.TSLICE: - return state.regassignStruct(synthSlice) - case types.TSTRING: - return state.regassignStruct(synthString) - case types.TINTER: - return state.regassignStruct(synthIface) - default: - panic("not expected") - } -} - -// assignParamOrReturn processes a given receiver, param, or result -// of type 'pt' to determine whether it can be register assigned. -// The result of the analysis is recorded in the result -// ABIParamResultInfo held in 'state'. -func (state *assignState) assignParamOrReturn(pt *types.Type) ABIParamAssignment { - state.pUsed = RegAmounts{} - if pt.Width == types.BADWIDTH { - panic("should never happen") - } else if pt.Width == 0 { - return state.stackAllocate(pt) - } else if state.regassign(pt) { - return state.regAllocate(pt) - } else { - return state.stackAllocate(pt) - } -} diff --git a/src/cmd/compile/internal/gc/abiutils_test.go b/src/cmd/compile/internal/gc/abiutils_test.go deleted file mode 100644 index 6fd0af1b1f..0000000000 --- a/src/cmd/compile/internal/gc/abiutils_test.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gc - -import ( - "bufio" - "cmd/compile/internal/base" - "cmd/compile/internal/ssagen" - "cmd/compile/internal/typecheck" - "cmd/compile/internal/types" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "cmd/internal/src" - "os" - "testing" -) - -// AMD64 registers available: -// - integer: RAX, RBX, RCX, RDI, RSI, R8, R9, r10, R11 -// - floating point: X0 - X14 -var configAMD64 = ABIConfig{ - regAmounts: RegAmounts{ - intRegs: 9, - floatRegs: 15, - }, -} - -func TestMain(m *testing.M) { - ssagen.Arch.LinkArch = &x86.Linkamd64 - ssagen.Arch.REGSP = x86.REGSP - ssagen.Arch.MAXWIDTH = 1 << 50 - types.MaxWidth = ssagen.Arch.MAXWIDTH - base.Ctxt = obj.Linknew(ssagen.Arch.LinkArch) - base.Ctxt.DiagFunc = base.Errorf - base.Ctxt.DiagFlush = base.FlushErrors - base.Ctxt.Bso = bufio.NewWriter(os.Stdout) - types.PtrSize = ssagen.Arch.LinkArch.PtrSize - types.RegSize = ssagen.Arch.LinkArch.RegSize - typecheck.InitUniverse() - os.Exit(m.Run()) -} - -func TestABIUtilsBasic1(t *testing.T) { - - // func(x int32) int32 - i32 := types.Types[types.TINT32] - ft := mkFuncType(nil, []*types.Type{i32}, []*types.Type{i32}) - - // expected results - exp := makeExpectedDump(` - IN 0: R{ I0 } offset: -1 typ: int32 - OUT 0: R{ I0 } offset: -1 typ: int32 - intspill: 1 floatspill: 0 offsetToSpillArea: 0 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsBasic2(t *testing.T) { - // func(x int32, y float64) (int32, float64, float64) - i8 := types.Types[types.TINT8] - i16 := types.Types[types.TINT16] - i32 := types.Types[types.TINT32] - i64 := types.Types[types.TINT64] - f32 := types.Types[types.TFLOAT32] - f64 := types.Types[types.TFLOAT64] - c64 := types.Types[types.TCOMPLEX64] - c128 := types.Types[types.TCOMPLEX128] - ft := mkFuncType(nil, - []*types.Type{ - i8, i16, i32, i64, - f32, f32, f64, f64, - i8, i16, i32, i64, - f32, f32, f64, f64, - c128, c128, c128, c128, c64, - i8, i16, i32, i64, - i8, i16, i32, i64}, - []*types.Type{i32, f64, f64}) - exp := makeExpectedDump(` - IN 0: R{ I0 } offset: -1 typ: int8 - IN 1: R{ I1 } offset: -1 typ: int16 - IN 2: R{ I2 } offset: -1 typ: int32 - IN 3: R{ I3 } offset: -1 typ: int64 - IN 4: R{ F0 } offset: -1 typ: float32 - IN 5: R{ F1 } offset: -1 typ: float32 - IN 6: R{ F2 } offset: -1 typ: float64 - IN 7: R{ F3 } offset: -1 typ: float64 - IN 8: R{ I4 } offset: -1 typ: int8 - IN 9: R{ I5 } offset: -1 typ: int16 - IN 10: R{ I6 } offset: -1 typ: int32 - IN 11: R{ I7 } offset: -1 typ: int64 - IN 12: R{ F4 } offset: -1 typ: float32 - IN 13: R{ F5 } offset: -1 typ: float32 - IN 14: R{ F6 } offset: -1 typ: float64 - IN 15: R{ F7 } offset: -1 typ: float64 - IN 16: R{ F8 F9 } offset: -1 typ: complex128 - IN 17: R{ F10 F11 } offset: -1 typ: complex128 - IN 18: R{ F12 F13 } offset: -1 typ: complex128 - IN 19: R{ } offset: 0 typ: complex128 - IN 20: R{ F14 } offset: -1 typ: complex64 - IN 21: R{ I8 } offset: -1 typ: int8 - IN 22: R{ } offset: 16 typ: int16 - IN 23: R{ } offset: 20 typ: int32 - IN 24: R{ } offset: 24 typ: int64 - IN 25: R{ } offset: 32 typ: int8 - IN 26: R{ } offset: 34 typ: int16 - IN 27: R{ } offset: 36 typ: int32 - IN 28: R{ } offset: 40 typ: int64 - OUT 0: R{ I0 } offset: -1 typ: int32 - OUT 1: R{ F0 } offset: -1 typ: float64 - OUT 2: R{ F1 } offset: -1 typ: float64 - intspill: 9 floatspill: 15 offsetToSpillArea: 48 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsArrays(t *testing.T) { - i32 := types.Types[types.TINT32] - ae := types.NewArray(i32, 0) - a1 := types.NewArray(i32, 1) - a2 := types.NewArray(i32, 2) - aa1 := types.NewArray(a1, 1) - ft := mkFuncType(nil, []*types.Type{a1, ae, aa1, a2}, - []*types.Type{a2, a1, ae, aa1}) - - exp := makeExpectedDump(` - IN 0: R{ I0 } offset: -1 typ: [1]int32 - IN 1: R{ } offset: 0 typ: [0]int32 - IN 2: R{ I1 } offset: -1 typ: [1][1]int32 - IN 3: R{ } offset: 0 typ: [2]int32 - OUT 0: R{ } offset: 8 typ: [2]int32 - OUT 1: R{ I0 } offset: -1 typ: [1]int32 - OUT 2: R{ } offset: 16 typ: [0]int32 - OUT 3: R{ I1 } offset: -1 typ: [1][1]int32 - intspill: 2 floatspill: 0 offsetToSpillArea: 16 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsStruct1(t *testing.T) { - i8 := types.Types[types.TINT8] - i16 := types.Types[types.TINT16] - i32 := types.Types[types.TINT32] - i64 := types.Types[types.TINT64] - s := mkstruct([]*types.Type{i8, i8, mkstruct([]*types.Type{}), i8, i16}) - ft := mkFuncType(nil, []*types.Type{i8, s, i64}, - []*types.Type{s, i8, i32}) - - exp := makeExpectedDump(` - IN 0: R{ I0 } offset: -1 typ: int8 - IN 1: R{ I1 I2 I3 I4 } offset: -1 typ: struct { int8; int8; struct {}; int8; int16 } - IN 2: R{ I5 } offset: -1 typ: int64 - OUT 0: R{ I0 I1 I2 I3 } offset: -1 typ: struct { int8; int8; struct {}; int8; int16 } - OUT 1: R{ I4 } offset: -1 typ: int8 - OUT 2: R{ I5 } offset: -1 typ: int32 - intspill: 6 floatspill: 0 offsetToSpillArea: 0 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsStruct2(t *testing.T) { - f64 := types.Types[types.TFLOAT64] - i64 := types.Types[types.TINT64] - s := mkstruct([]*types.Type{i64, mkstruct([]*types.Type{})}) - fs := mkstruct([]*types.Type{f64, s, mkstruct([]*types.Type{})}) - ft := mkFuncType(nil, []*types.Type{s, s, fs}, - []*types.Type{fs, fs}) - - exp := makeExpectedDump(` - IN 0: R{ I0 } offset: -1 typ: struct { int64; struct {} } - IN 1: R{ I1 } offset: -1 typ: struct { int64; struct {} } - IN 2: R{ I2 F0 } offset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} } - OUT 0: R{ I0 F0 } offset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} } - OUT 1: R{ I1 F1 } offset: -1 typ: struct { float64; struct { int64; struct {} }; struct {} } - intspill: 3 floatspill: 1 offsetToSpillArea: 0 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsSliceString(t *testing.T) { - i32 := types.Types[types.TINT32] - sli32 := types.NewSlice(i32) - str := types.New(types.TSTRING) - i8 := types.Types[types.TINT8] - i64 := types.Types[types.TINT64] - ft := mkFuncType(nil, []*types.Type{sli32, i8, sli32, i8, str, i8, i64, sli32}, - []*types.Type{str, i64, str, sli32}) - - exp := makeExpectedDump(` - IN 0: R{ I0 I1 I2 } offset: -1 typ: []int32 - IN 1: R{ I3 } offset: -1 typ: int8 - IN 2: R{ I4 I5 I6 } offset: -1 typ: []int32 - IN 3: R{ I7 } offset: -1 typ: int8 - IN 4: R{ } offset: 0 typ: string - IN 5: R{ I8 } offset: -1 typ: int8 - IN 6: R{ } offset: 16 typ: int64 - IN 7: R{ } offset: 24 typ: []int32 - OUT 0: R{ I0 I1 } offset: -1 typ: string - OUT 1: R{ I2 } offset: -1 typ: int64 - OUT 2: R{ I3 I4 } offset: -1 typ: string - OUT 3: R{ I5 I6 I7 } offset: -1 typ: []int32 - intspill: 9 floatspill: 0 offsetToSpillArea: 48 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsMethod(t *testing.T) { - i16 := types.Types[types.TINT16] - i64 := types.Types[types.TINT64] - f64 := types.Types[types.TFLOAT64] - - s1 := mkstruct([]*types.Type{i16, i16, i16}) - ps1 := types.NewPtr(s1) - a7 := types.NewArray(ps1, 7) - ft := mkFuncType(s1, []*types.Type{ps1, a7, f64, i16, i16, i16}, - []*types.Type{a7, f64, i64}) - - exp := makeExpectedDump(` - IN 0: R{ I0 I1 I2 } offset: -1 typ: struct { int16; int16; int16 } - IN 1: R{ I3 } offset: -1 typ: *struct { int16; int16; int16 } - IN 2: R{ } offset: 0 typ: [7]*struct { int16; int16; int16 } - IN 3: R{ F0 } offset: -1 typ: float64 - IN 4: R{ I4 } offset: -1 typ: int16 - IN 5: R{ I5 } offset: -1 typ: int16 - IN 6: R{ I6 } offset: -1 typ: int16 - OUT 0: R{ } offset: 56 typ: [7]*struct { int16; int16; int16 } - OUT 1: R{ F0 } offset: -1 typ: float64 - OUT 2: R{ I0 } offset: -1 typ: int64 - intspill: 7 floatspill: 1 offsetToSpillArea: 112 -`) - - abitest(t, ft, exp) -} - -func TestABIUtilsInterfaces(t *testing.T) { - ei := types.Types[types.TINTER] // interface{} - pei := types.NewPtr(ei) // *interface{} - fldt := mkFuncType(types.FakeRecvType(), []*types.Type{}, - []*types.Type{types.UntypedString}) - field := types.NewField(src.NoXPos, nil, fldt) - // interface{ ...() string } - nei := types.NewInterface(types.LocalPkg, []*types.Field{field}) - - i16 := types.Types[types.TINT16] - tb := types.Types[types.TBOOL] - s1 := mkstruct([]*types.Type{i16, i16, tb}) - - ft := mkFuncType(nil, []*types.Type{s1, ei, ei, nei, pei, nei, i16}, - []*types.Type{ei, nei, pei}) - - exp := makeExpectedDump(` - IN 0: R{ I0 I1 I2 } offset: -1 typ: struct { int16; int16; bool } - IN 1: R{ I3 I4 } offset: -1 typ: interface {} - IN 2: R{ I5 I6 } offset: -1 typ: interface {} - IN 3: R{ I7 I8 } offset: -1 typ: interface { () untyped string } - IN 4: R{ } offset: 0 typ: *interface {} - IN 5: R{ } offset: 8 typ: interface { () untyped string } - IN 6: R{ } offset: 24 typ: int16 - OUT 0: R{ I0 I1 } offset: -1 typ: interface {} - OUT 1: R{ I2 I3 } offset: -1 typ: interface { () untyped string } - OUT 2: R{ I4 } offset: -1 typ: *interface {} - intspill: 9 floatspill: 0 offsetToSpillArea: 32 -`) - - abitest(t, ft, exp) -} diff --git a/src/cmd/compile/internal/gc/abiutilsaux_test.go b/src/cmd/compile/internal/gc/abiutilsaux_test.go deleted file mode 100644 index 9386b554b0..0000000000 --- a/src/cmd/compile/internal/gc/abiutilsaux_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gc - -// This file contains utility routines and harness infrastructure used -// by the ABI tests in "abiutils_test.go". - -import ( - "cmd/compile/internal/ir" - "cmd/compile/internal/typecheck" - "cmd/compile/internal/types" - "cmd/internal/src" - "fmt" - "strings" - "testing" - "text/scanner" -) - -func mkParamResultField(t *types.Type, s *types.Sym, which ir.Class) *types.Field { - field := types.NewField(src.NoXPos, s, t) - n := typecheck.NewName(s) - n.Class = which - field.Nname = n - n.SetType(t) - return field -} - -// mkstruct is a helper routine to create a struct type with fields -// of the types specified in 'fieldtypes'. -func mkstruct(fieldtypes []*types.Type) *types.Type { - fields := make([]*types.Field, len(fieldtypes)) - for k, t := range fieldtypes { - if t == nil { - panic("bad -- field has no type") - } - f := types.NewField(src.NoXPos, nil, t) - fields[k] = f - } - s := types.NewStruct(types.LocalPkg, fields) - return s -} - -func mkFuncType(rcvr *types.Type, ins []*types.Type, outs []*types.Type) *types.Type { - q := typecheck.Lookup("?") - inf := []*types.Field{} - for _, it := range ins { - inf = append(inf, mkParamResultField(it, q, ir.PPARAM)) - } - outf := []*types.Field{} - for _, ot := range outs { - outf = append(outf, mkParamResultField(ot, q, ir.PPARAMOUT)) - } - var rf *types.Field - if rcvr != nil { - rf = mkParamResultField(rcvr, q, ir.PPARAM) - } - return types.NewSignature(types.LocalPkg, rf, inf, outf) -} - -type expectedDump struct { - dump string - file string - line int -} - -func tokenize(src string) []string { - var s scanner.Scanner - s.Init(strings.NewReader(src)) - res := []string{} - for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { - res = append(res, s.TokenText()) - } - return res -} - -func verifyParamResultOffset(t *testing.T, f *types.Field, r ABIParamAssignment, which string, idx int) int { - n := ir.AsNode(f.Nname).(*ir.Name) - if n.FrameOffset() != int64(r.Offset) { - t.Errorf("%s %d: got offset %d wanted %d t=%v", - which, idx, r.Offset, n.Offset_, f.Type) - return 1 - } - return 0 -} - -func makeExpectedDump(e string) expectedDump { - return expectedDump{dump: e} -} - -func difftokens(atoks []string, etoks []string) string { - if len(atoks) != len(etoks) { - return fmt.Sprintf("expected %d tokens got %d", - len(etoks), len(atoks)) - } - for i := 0; i < len(etoks); i++ { - if etoks[i] == atoks[i] { - continue - } - - return fmt.Sprintf("diff at token %d: expected %q got %q", - i, etoks[i], atoks[i]) - } - return "" -} - -func abitest(t *testing.T, ft *types.Type, exp expectedDump) { - - types.CalcSize(ft) - - // Analyze with full set of registers. - regRes := ABIAnalyze(ft, configAMD64) - regResString := strings.TrimSpace(regRes.String()) - - // Check results. - reason := difftokens(tokenize(regResString), tokenize(exp.dump)) - if reason != "" { - t.Errorf("\nexpected:\n%s\ngot:\n%s\nreason: %s", - strings.TrimSpace(exp.dump), regResString, reason) - } - - // Analyze again with empty register set. - empty := ABIConfig{} - emptyRes := ABIAnalyze(ft, empty) - emptyResString := emptyRes.String() - - // Walk the results and make sure the offsets assigned match - // up with those assiged by dowidth. This checks to make sure that - // when we have no available registers the ABI assignment degenerates - // back to the original ABI0. - - // receiver - failed := 0 - rfsl := ft.Recvs().Fields().Slice() - poff := 0 - if len(rfsl) != 0 { - failed |= verifyParamResultOffset(t, rfsl[0], emptyRes.inparams[0], "receiver", 0) - poff = 1 - } - // params - pfsl := ft.Params().Fields().Slice() - for k, f := range pfsl { - verifyParamResultOffset(t, f, emptyRes.inparams[k+poff], "param", k) - } - // results - ofsl := ft.Results().Fields().Slice() - for k, f := range ofsl { - failed |= verifyParamResultOffset(t, f, emptyRes.outparams[k], "result", k) - } - - if failed != 0 { - t.Logf("emptyres:\n%s\n", emptyResString) - } -} |