mirror of
https://github.com/golang/go
synced 2024-11-18 17:54:57 -07:00
cmd/compile/internal/abi: use Type.Registers
Now that types can self-report how many registers they need, it's much easier to determine whether a parameter will fit into the available registers: simply compare the number of registers needed against the number of registers still available. This also eliminates the need for the NumParamRegs cache. Also, the new code in NumParamRegs is stricter in only allowing it to be called on types that can actually be passed in registers, which requires a test to be corrected for that. While here, change mkstruct to a variadic function, so the call sites require less boilerplate. Change-Id: Iebe1a0456a8053a10e551e5da796014e5b1b695b Reviewed-on: https://go-review.googlesource.com/c/go/+/527339 Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Auto-Submit: Matthew Dempsky <mdempsky@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
parent
c80a9f172b
commit
c0e2a9dffd
@ -10,6 +10,7 @@ import (
|
|||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
"cmd/internal/src"
|
"cmd/internal/src"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -260,70 +261,44 @@ type ABIConfig struct {
|
|||||||
// Do we need anything more than this?
|
// Do we need anything more than this?
|
||||||
offsetForLocals int64 // e.g., obj.(*Link).Arch.FixedFrameSize -- extra linkage information on some architectures.
|
offsetForLocals int64 // e.g., obj.(*Link).Arch.FixedFrameSize -- extra linkage information on some architectures.
|
||||||
regAmounts RegAmounts
|
regAmounts RegAmounts
|
||||||
regsForTypeCache map[*types.Type]int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewABIConfig returns a new ABI configuration for an architecture with
|
// NewABIConfig returns a new ABI configuration for an architecture with
|
||||||
// iRegsCount integer/pointer registers and fRegsCount floating point registers.
|
// iRegsCount integer/pointer registers and fRegsCount floating point registers.
|
||||||
func NewABIConfig(iRegsCount, fRegsCount int, offsetForLocals int64) *ABIConfig {
|
func NewABIConfig(iRegsCount, fRegsCount int, offsetForLocals int64) *ABIConfig {
|
||||||
return &ABIConfig{offsetForLocals: offsetForLocals, regAmounts: RegAmounts{iRegsCount, fRegsCount}, regsForTypeCache: make(map[*types.Type]int)}
|
return &ABIConfig{offsetForLocals: offsetForLocals, regAmounts: RegAmounts{iRegsCount, fRegsCount}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of an ABIConfig for use in a function's compilation so that access to the cache does not need to be protected with a mutex.
|
// Copy returns config.
|
||||||
func (a *ABIConfig) Copy() *ABIConfig {
|
//
|
||||||
b := *a
|
// TODO(mdempsky): Remove.
|
||||||
b.regsForTypeCache = make(map[*types.Type]int)
|
func (config *ABIConfig) Copy() *ABIConfig {
|
||||||
return &b
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalsOffset returns the architecture-dependent offset from SP for args and results.
|
// LocalsOffset returns the architecture-dependent offset from SP for args and results.
|
||||||
// In theory this is only used for debugging; it ought to already be incorporated into
|
// In theory this is only used for debugging; it ought to already be incorporated into
|
||||||
// results from the ABI-related methods
|
// results from the ABI-related methods
|
||||||
func (a *ABIConfig) LocalsOffset() int64 {
|
func (config *ABIConfig) LocalsOffset() int64 {
|
||||||
return a.offsetForLocals
|
return config.offsetForLocals
|
||||||
}
|
}
|
||||||
|
|
||||||
// FloatIndexFor translates r into an index in the floating point parameter
|
// FloatIndexFor translates r into an index in the floating point parameter
|
||||||
// registers. If the result is negative, the input index was actually for the
|
// registers. If the result is negative, the input index was actually for the
|
||||||
// integer parameter registers.
|
// integer parameter registers.
|
||||||
func (a *ABIConfig) FloatIndexFor(r RegIndex) int64 {
|
func (config *ABIConfig) FloatIndexFor(r RegIndex) int64 {
|
||||||
return int64(r) - int64(a.regAmounts.intRegs)
|
return int64(r) - int64(config.regAmounts.intRegs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumParamRegs returns the number of parameter registers used for a given type,
|
// NumParamRegs returns the total number of registers used to
|
||||||
// without regard for the number available.
|
// represent a parameter of the given type, which must be register
|
||||||
func (a *ABIConfig) NumParamRegs(t *types.Type) int {
|
// assignable.
|
||||||
var n int
|
func (config *ABIConfig) NumParamRegs(typ *types.Type) int {
|
||||||
if n, ok := a.regsForTypeCache[t]; ok {
|
intRegs, floatRegs := typ.Registers()
|
||||||
return n
|
if intRegs == math.MaxUint8 && floatRegs == math.MaxUint8 {
|
||||||
|
base.Fatalf("cannot represent parameters of type %v in registers", typ)
|
||||||
}
|
}
|
||||||
|
return int(intRegs) + int(floatRegs)
|
||||||
if t.IsScalar() || t.IsPtrShaped() {
|
|
||||||
if t.IsComplex() {
|
|
||||||
n = 2
|
|
||||||
} else {
|
|
||||||
n = (int(t.Size()) + types.RegSize - 1) / types.RegSize
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
typ := t.Kind()
|
|
||||||
switch typ {
|
|
||||||
case types.TARRAY:
|
|
||||||
n = a.NumParamRegs(t.Elem()) * int(t.NumElem())
|
|
||||||
case types.TSTRUCT:
|
|
||||||
for _, f := range t.Fields() {
|
|
||||||
n += a.NumParamRegs(f.Type)
|
|
||||||
}
|
|
||||||
case types.TSLICE:
|
|
||||||
n = a.NumParamRegs(synthSlice)
|
|
||||||
case types.TSTRING:
|
|
||||||
n = a.NumParamRegs(synthString)
|
|
||||||
case types.TINTER:
|
|
||||||
n = a.NumParamRegs(synthIface)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.regsForTypeCache[t] = n
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ABIAnalyzeTypes takes slices of parameter and result types, and returns an ABIParamResultInfo,
|
// ABIAnalyzeTypes takes slices of parameter and result types, and returns an ABIParamResultInfo,
|
||||||
@ -498,7 +473,6 @@ func (ri *ABIParamResultInfo) String() string {
|
|||||||
type assignState struct {
|
type assignState struct {
|
||||||
rTotal RegAmounts // total reg amounts from ABI rules
|
rTotal RegAmounts // total reg amounts from ABI rules
|
||||||
rUsed RegAmounts // regs used by params completely assigned so far
|
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
|
stackOffset int64 // current stack offset
|
||||||
spillOffset int64 // current spill offset
|
spillOffset int64 // current spill offset
|
||||||
}
|
}
|
||||||
@ -516,12 +490,11 @@ func alignTo(a int64, t int) int64 {
|
|||||||
return types.RoundUp(a, int64(t))
|
return types.RoundUp(a, int64(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
// stackSlot returns a stack offset for a param or result of the
|
// nextSlot allocates the next available slot for typ.
|
||||||
// specified type.
|
func nextSlot(offsetp *int64, typ *types.Type) int64 {
|
||||||
func (state *assignState) stackSlot(t *types.Type) int64 {
|
offset := align(*offsetp, typ)
|
||||||
rv := align(state.stackOffset, t)
|
*offsetp = offset + typ.Size()
|
||||||
state.stackOffset = rv + t.Size()
|
return offset
|
||||||
return rv
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocateRegs returns an ordered list of register indices for a parameter or result
|
// allocateRegs returns an ordered list of register indices for a parameter or result
|
||||||
@ -575,105 +548,6 @@ func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegInde
|
|||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, name types.Object, isResult bool) ABIParamAssignment {
|
|
||||||
spillLoc := int64(-1)
|
|
||||||
if !isResult {
|
|
||||||
// Spill for register-resident t must be aligned for storage of a t.
|
|
||||||
spillLoc = align(state.spillOffset, t)
|
|
||||||
state.spillOffset = spillLoc + t.Size()
|
|
||||||
}
|
|
||||||
return ABIParamAssignment{
|
|
||||||
Type: t,
|
|
||||||
Name: name,
|
|
||||||
Registers: state.allocateRegs([]RegIndex{}, t),
|
|
||||||
offset: int32(spillLoc),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, name types.Object) ABIParamAssignment {
|
|
||||||
return ABIParamAssignment{
|
|
||||||
Type: t,
|
|
||||||
Name: name,
|
|
||||||
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.RoundUp(t.Size(), int64(types.PtrSize)) / int64(types.PtrSize))
|
|
||||||
if t.IsComplex() {
|
|
||||||
regsNeeded = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.Fields() {
|
|
||||||
if !state.regassign(field.Type) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// synthOnce ensures that we only create the synth* fake types once.
|
// synthOnce ensures that we only create the synth* fake types once.
|
||||||
var synthOnce sync.Once
|
var synthOnce sync.Once
|
||||||
|
|
||||||
@ -711,47 +585,42 @@ func setup() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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:
|
|
||||||
base.Fatalf("not expected")
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assignParam processes a given receiver, param, or result
|
// assignParam processes a given receiver, param, or result
|
||||||
// of field f to determine whether it can be register assigned.
|
// of field f to determine whether it can be register assigned.
|
||||||
// The result of the analysis is recorded in the result
|
// The result of the analysis is recorded in the result
|
||||||
// ABIParamResultInfo held in 'state'.
|
// ABIParamResultInfo held in 'state'.
|
||||||
func (state *assignState) assignParam(pt *types.Type, n types.Object, isResult bool) ABIParamAssignment {
|
func (state *assignState) assignParam(typ *types.Type, name types.Object, isResult bool) ABIParamAssignment {
|
||||||
state.pUsed = RegAmounts{}
|
registers := state.tryAllocRegs(typ)
|
||||||
if pt.Size() == types.BADWIDTH {
|
|
||||||
base.Fatalf("should never happen")
|
var offset int64 = -1
|
||||||
panic("unreachable")
|
if registers == nil { // stack allocated; needs stack slot
|
||||||
} else if pt.Size() == 0 {
|
offset = nextSlot(&state.stackOffset, typ)
|
||||||
return state.stackAllocate(pt, n)
|
} else if !isResult { // register-allocated param; needs spill slot
|
||||||
} else if state.regassign(pt) {
|
offset = nextSlot(&state.spillOffset, typ)
|
||||||
return state.regAllocate(pt, n, isResult)
|
|
||||||
} else {
|
|
||||||
return state.stackAllocate(pt, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ABIParamAssignment{
|
||||||
|
Type: typ,
|
||||||
|
Name: name,
|
||||||
|
Registers: registers,
|
||||||
|
offset: int32(offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryAllocRegs attempts to allocate registers to represent a
|
||||||
|
// parameter of the given type. If unsuccessful, it returns nil.
|
||||||
|
func (state *assignState) tryAllocRegs(typ *types.Type) []RegIndex {
|
||||||
|
if typ.Size() == 0 {
|
||||||
|
return nil // zero-size parameters are defined as being stack allocated
|
||||||
|
}
|
||||||
|
|
||||||
|
intRegs, floatRegs := typ.Registers()
|
||||||
|
if int(intRegs) > state.rTotal.intRegs-state.rUsed.intRegs || int(floatRegs) > state.rTotal.floatRegs-state.rUsed.floatRegs {
|
||||||
|
return nil // too few available registers
|
||||||
|
}
|
||||||
|
|
||||||
|
regs := make([]RegIndex, 0, int(intRegs)+int(floatRegs))
|
||||||
|
return state.allocateRegs(regs, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComputePadding returns a list of "post element" padding values in
|
// ComputePadding returns a list of "post element" padding values in
|
||||||
|
@ -157,7 +157,7 @@ func TestABIUtilsStruct1(t *testing.T) {
|
|||||||
i16 := types.Types[types.TINT16]
|
i16 := types.Types[types.TINT16]
|
||||||
i32 := types.Types[types.TINT32]
|
i32 := types.Types[types.TINT32]
|
||||||
i64 := types.Types[types.TINT64]
|
i64 := types.Types[types.TINT64]
|
||||||
s := mkstruct([]*types.Type{i8, i8, mkstruct([]*types.Type{}), i8, i16})
|
s := mkstruct(i8, i8, mkstruct(), i8, i16)
|
||||||
ft := mkFuncType(nil, []*types.Type{i8, s, i64},
|
ft := mkFuncType(nil, []*types.Type{i8, s, i64},
|
||||||
[]*types.Type{s, i8, i32})
|
[]*types.Type{s, i8, i32})
|
||||||
|
|
||||||
@ -181,8 +181,8 @@ func TestABIUtilsStruct2(t *testing.T) {
|
|||||||
// (r1 fs, r2 fs)
|
// (r1 fs, r2 fs)
|
||||||
f64 := types.Types[types.TFLOAT64]
|
f64 := types.Types[types.TFLOAT64]
|
||||||
i64 := types.Types[types.TINT64]
|
i64 := types.Types[types.TINT64]
|
||||||
s := mkstruct([]*types.Type{i64, mkstruct([]*types.Type{})})
|
s := mkstruct(i64, mkstruct())
|
||||||
fs := mkstruct([]*types.Type{f64, s, mkstruct([]*types.Type{})})
|
fs := mkstruct(f64, s, mkstruct())
|
||||||
ft := mkFuncType(nil, []*types.Type{s, s, fs},
|
ft := mkFuncType(nil, []*types.Type{s, s, fs},
|
||||||
[]*types.Type{fs, fs})
|
[]*types.Type{fs, fs})
|
||||||
|
|
||||||
@ -213,9 +213,10 @@ func TestABIUtilsEmptyFieldAtEndOfStruct(t *testing.T) {
|
|||||||
ab2 := types.NewArray(tb, 2)
|
ab2 := types.NewArray(tb, 2)
|
||||||
a2 := types.NewArray(i64, 2)
|
a2 := types.NewArray(i64, 2)
|
||||||
a3 := types.NewArray(i16, 3)
|
a3 := types.NewArray(i16, 3)
|
||||||
s := mkstruct([]*types.Type{a2, mkstruct([]*types.Type{})})
|
empty := mkstruct()
|
||||||
s2 := mkstruct([]*types.Type{a3, mkstruct([]*types.Type{})})
|
s := mkstruct(a2, empty)
|
||||||
fs := mkstruct([]*types.Type{f64, s, mkstruct([]*types.Type{})})
|
s2 := mkstruct(a3, empty)
|
||||||
|
fs := mkstruct(f64, s, empty)
|
||||||
ft := mkFuncType(nil, []*types.Type{s, ab2, s2, fs, fs},
|
ft := mkFuncType(nil, []*types.Type{s, ab2, s2, fs, fs},
|
||||||
[]*types.Type{fs, ab2, fs})
|
[]*types.Type{fs, ab2, fs})
|
||||||
|
|
||||||
@ -233,12 +234,11 @@ func TestABIUtilsEmptyFieldAtEndOfStruct(t *testing.T) {
|
|||||||
|
|
||||||
abitest(t, ft, exp)
|
abitest(t, ft, exp)
|
||||||
|
|
||||||
// Check to make sure that NumParamRegs yields 2 and not 3
|
// Test that NumParamRegs doesn't assign registers to trailing padding.
|
||||||
// for struct "s" (e.g. that it handles the padding properly).
|
typ := mkstruct(i64, i64, mkstruct())
|
||||||
nps := configAMD64.NumParamRegs(s)
|
have := configAMD64.NumParamRegs(typ)
|
||||||
if nps != 2 {
|
if have != 2 {
|
||||||
t.Errorf("NumParams(%v) returned %d expected %d\n",
|
t.Errorf("NumParams(%v): have %v, want %v", typ, have, 2)
|
||||||
s, nps, 2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ func TestABIUtilsMethod(t *testing.T) {
|
|||||||
i16 := types.Types[types.TINT16]
|
i16 := types.Types[types.TINT16]
|
||||||
i64 := types.Types[types.TINT64]
|
i64 := types.Types[types.TINT64]
|
||||||
f64 := types.Types[types.TFLOAT64]
|
f64 := types.Types[types.TFLOAT64]
|
||||||
s1 := mkstruct([]*types.Type{i16, i16, i16})
|
s1 := mkstruct(i16, i16, i16)
|
||||||
ps1 := types.NewPtr(s1)
|
ps1 := types.NewPtr(s1)
|
||||||
a7 := types.NewArray(ps1, 7)
|
a7 := types.NewArray(ps1, 7)
|
||||||
ft := mkFuncType(s1, []*types.Type{ps1, a7, f64, i16, i16, i16},
|
ft := mkFuncType(s1, []*types.Type{ps1, a7, f64, i16, i16, i16},
|
||||||
@ -316,7 +316,7 @@ func TestABIUtilsInterfaces(t *testing.T) {
|
|||||||
nei := types.NewInterface([]*types.Field{field})
|
nei := types.NewInterface([]*types.Field{field})
|
||||||
i16 := types.Types[types.TINT16]
|
i16 := types.Types[types.TINT16]
|
||||||
tb := types.Types[types.TBOOL]
|
tb := types.Types[types.TBOOL]
|
||||||
s1 := mkstruct([]*types.Type{i16, i16, tb})
|
s1 := mkstruct(i16, i16, tb)
|
||||||
ft := mkFuncType(nil, []*types.Type{s1, ei, ei, nei, pei, nei, i16},
|
ft := mkFuncType(nil, []*types.Type{s1, ei, ei, nei, pei, nei, i16},
|
||||||
[]*types.Type{ei, nei, pei})
|
[]*types.Type{ei, nei, pei})
|
||||||
|
|
||||||
@ -347,8 +347,8 @@ func TestABINumParamRegs(t *testing.T) {
|
|||||||
c64 := types.Types[types.TCOMPLEX64]
|
c64 := types.Types[types.TCOMPLEX64]
|
||||||
c128 := types.Types[types.TCOMPLEX128]
|
c128 := types.Types[types.TCOMPLEX128]
|
||||||
|
|
||||||
s := mkstruct([]*types.Type{i8, i8, mkstruct([]*types.Type{}), i8, i16})
|
s := mkstruct(i8, i8, mkstruct(), i8, i16)
|
||||||
a := types.NewArray(s, 3)
|
a := mkstruct(s, s, s)
|
||||||
|
|
||||||
nrtest(t, i8, 1)
|
nrtest(t, i8, 1)
|
||||||
nrtest(t, i16, 1)
|
nrtest(t, i16, 1)
|
||||||
@ -360,7 +360,6 @@ func TestABINumParamRegs(t *testing.T) {
|
|||||||
nrtest(t, c128, 2)
|
nrtest(t, c128, 2)
|
||||||
nrtest(t, s, 4)
|
nrtest(t, s, 4)
|
||||||
nrtest(t, a, 12)
|
nrtest(t, a, 12)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABIUtilsComputePadding(t *testing.T) {
|
func TestABIUtilsComputePadding(t *testing.T) {
|
||||||
@ -369,11 +368,11 @@ func TestABIUtilsComputePadding(t *testing.T) {
|
|||||||
i16 := types.Types[types.TINT16]
|
i16 := types.Types[types.TINT16]
|
||||||
i32 := types.Types[types.TINT32]
|
i32 := types.Types[types.TINT32]
|
||||||
i64 := types.Types[types.TINT64]
|
i64 := types.Types[types.TINT64]
|
||||||
emptys := mkstruct([]*types.Type{})
|
emptys := mkstruct()
|
||||||
s1 := mkstruct([]*types.Type{i8, i16, emptys, i32, i64})
|
s1 := mkstruct(i8, i16, emptys, i32, i64)
|
||||||
// func (p1 int32, p2 s1, p3 emptys, p4 [1]int32)
|
// func (p1 int32, p2 s1, p3 emptys, p4 [1]int32)
|
||||||
a1 := types.NewArray(i32, 1)
|
a1 := types.NewArray(i32, 1)
|
||||||
ft := mkFuncType(nil, []*types.Type{i32, s1, emptys, a1}, []*types.Type{})
|
ft := mkFuncType(nil, []*types.Type{i32, s1, emptys, a1}, nil)
|
||||||
|
|
||||||
// Run abitest() just to document what we're expected to see.
|
// Run abitest() just to document what we're expected to see.
|
||||||
exp := makeExpectedDump(`
|
exp := makeExpectedDump(`
|
||||||
|
@ -29,7 +29,7 @@ func mkParamResultField(t *types.Type, s *types.Sym, which ir.Class) *types.Fiel
|
|||||||
|
|
||||||
// mkstruct is a helper routine to create a struct type with fields
|
// mkstruct is a helper routine to create a struct type with fields
|
||||||
// of the types specified in 'fieldtypes'.
|
// of the types specified in 'fieldtypes'.
|
||||||
func mkstruct(fieldtypes []*types.Type) *types.Type {
|
func mkstruct(fieldtypes ...*types.Type) *types.Type {
|
||||||
fields := make([]*types.Field, len(fieldtypes))
|
fields := make([]*types.Field, len(fieldtypes))
|
||||||
for k, t := range fieldtypes {
|
for k, t := range fieldtypes {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user