return a.outRegistersUsed
}
-func (a *ABIParamResultInfo) InParam(i int) ABIParamAssignment {
- return a.inparams[i]
+func (a *ABIParamResultInfo) InParam(i int) *ABIParamAssignment {
+ return &a.inparams[i]
}
-func (a *ABIParamResultInfo) OutParam(i int) ABIParamAssignment {
- return a.outparams[i]
+func (a *ABIParamResultInfo) OutParam(i int) *ABIParamAssignment {
+ return &a.outparams[i]
}
func (a *ABIParamResultInfo) SpillAreaOffset() int64 {
return a.offset
}
+// FrameOffset returns the location that a value would spill to, if any exists.
+// For register-allocated inputs, that is their spill offset reserved for morestack
+// (might as well use it, it is there); for stack-allocated inputs and outputs,
+// that is their location on the stack. For register-allocated outputs, there is
+// no defined spill area, so return -1.
+func (a *ABIParamAssignment) FrameOffset(i *ABIParamResultInfo) int64 {
+ if len(a.Registers) == 0 || a.offset == -1 {
+ return int64(a.offset)
+ }
+ return int64(a.offset) + i.SpillAreaOffset()
+}
+
// RegAmounts holds a specified number of integer/float registers.
type RegAmounts struct {
intRegs int
result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
result.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
+ // Fill in the frame offsets for receiver, inputs, results
+ k := 0
+ if t.NumRecvs() != 0 {
+ config.updateOffset(result, ft.Receiver.FieldSlice()[0], result.inparams[0], false)
+ k++
+ }
+ for i, f := range ft.Params.FieldSlice() {
+ config.updateOffset(result, f, result.inparams[k+i], false)
+ }
+ for i, f := range ft.Results.FieldSlice() {
+ config.updateOffset(result, f, result.outparams[i], true)
+ }
return result
}
+func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn bool) {
+ if !isReturn || len(a.Registers) == 0 {
+ // TODO in next CL, assign
+ if f.Offset != a.FrameOffset(result) {
+ if config.regAmounts.intRegs == 0 && config.regAmounts.floatRegs == 0 {
+ panic(fmt.Errorf("Expected node offset %d != abi offset %d", f.Offset, a.FrameOffset(result)))
+ }
+ }
+ }
+}
+
//......................................................................
//
// Non-public portions.
if len(fn.Body) == 0 {
// Initialize ABI wrappers if necessary.
ssagen.InitLSym(fn, false)
+ types.CalcSize(fn.Type()) // TODO register args; remove this once all is done by abiutils
+ a := ssagen.AbiForFunc(fn)
+ a.ABIAnalyze(fn.Type()) // will set parameter spill/home locations correctly
liveness.WriteFuncMap(fn)
return
}
// OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc).
func (a *AuxCall) OffsetOfResult(which int64) int64 {
- return int64(a.results[which].Offset)
+ return int64(a.abiInfo.OutParam(int(which)).Offset())
}
// OffsetOfArg returns the SP offset of argument which (indexed 0, 1, etc).
// If the call is to a method, the receiver is the first argument (i.e., index 0)
func (a *AuxCall) OffsetOfArg(which int64) int64 {
- return int64(a.args[which].Offset)
+ return int64(a.abiInfo.InParam(int(which)).Offset())
}
// RegsOfResult returns the register(s) used for result which (indexed 0, 1, etc).
return fn + "}"
}
+// ACParamsToTypes translates a slice of Param into a slice of *types.Type
+// This is a helper call for ssagen/ssa.go.
+// TODO remove this, as part of replacing fields of AuxCall with abi.ABIParamResultInfo.
func ACParamsToTypes(ps []Param) (ts []*types.Type) {
for _, p := range ps {
ts = append(ts, p.Type)
// StaticAuxCall returns an AuxCall for a static call.
func StaticAuxCall(sym *obj.LSym, args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
- // TODO Create regInfo for AuxCall
if paramResultInfo == nil {
panic(fmt.Errorf("Nil paramResultInfo, sym=%v", sym))
}
}
// InterfaceAuxCall returns an AuxCall for an interface call.
-func InterfaceAuxCall(args []Param, results []Param) *AuxCall {
- // TODO Create regInfo for AuxCall
- return &AuxCall{Fn: nil, args: args, results: results}
+func InterfaceAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
+ return &AuxCall{Fn: nil, args: args, results: results, abiInfo: paramResultInfo}
}
// ClosureAuxCall returns an AuxCall for a closure call.
-func ClosureAuxCall(args []Param, results []Param) *AuxCall {
- // TODO Create regInfo for AuxCall
- return &AuxCall{Fn: nil, args: args, results: results}
+func ClosureAuxCall(args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
+ return &AuxCall{Fn: nil, args: args, results: results, abiInfo: paramResultInfo}
}
func (*AuxCall) CanBeAnSSAAux() {}
import (
"bufio"
"bytes"
+ "cmd/compile/internal/abi"
"encoding/binary"
"fmt"
"go/constant"
ir.Syms.SigPanic = typecheck.LookupRuntimeFunc("sigpanic")
}
+// AbiForFunc returns the ABI for a function, used to figure out arg/result mapping for rtcall and bodyless functions.
+// This follows policy for GOEXPERIMENT=regabi, //go:registerparams, and currently defined ABIInternal.
+// Policy is subject to change....
+// This always returns a freshly copied ABI.
+func AbiForFunc(fn *ir.Func) *abi.ABIConfig {
+ return abiForFunc(fn, ssaConfig.ABI0, ssaConfig.ABI1).Copy() // No idea what races will result, be safe
+}
+
+// abiForFunc implements ABI policy for a function, but does not return a copy of the ABI.
+// Passing a nil function returns ABIInternal.
+func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig {
+ a := abi1
+ if true || objabi.Regabi_enabled == 0 {
+ a = abi0
+ }
+ if fn != nil && fn.Pragma&ir.RegisterParams != 0 { // TODO(register args) remove after register abi is working
+ name := ir.FuncName(fn)
+ if strings.Contains(name, ".") {
+ base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name)
+ }
+ a = abi1
+ base.WarnfAt(fn.Pos(), "declared function %v has register params", fn)
+ }
+ return a
+}
+
// getParam returns the Field of ith param of node n (which is a
// function/method/interface call), where the receiver of a method call is
// considered as the 0th parameter. This does not include the receiver of an
if fn.Pragma&ir.Nosplit != 0 {
s.f.NoSplit = true
}
- s.f.ABI0 = ssaConfig.ABI0.Copy() // Make a copy to avoid racy map operations in type-width cache.
+ s.f.ABI0 = ssaConfig.ABI0.Copy() // Make a copy to avoid racy map operations in type-register-width cache.
s.f.ABI1 = ssaConfig.ABI1.Copy()
-
- s.f.ABIDefault = s.f.ABI1 // Default ABI for function calls with no parsed signature for a pragma, e.g. rtcall
- // TODO(register args) -- remove "true ||"; in the short run, turning on the register ABI experiment still leaves the compiler defaulting to ABI0.
- // TODO(register args) -- remove this conditional entirely when register ABI is not an experiment.
- if true || objabi.Regabi_enabled == 0 {
- s.f.ABIDefault = s.f.ABI0 // reset
- }
-
- s.f.ABISelf = s.f.ABIDefault
-
- if fn.Pragma&ir.RegisterParams != 0 { // TODO(register args) remove after register abi is working
- s.f.ABISelf = s.f.ABI1
- if strings.Contains(name, ".") {
- base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name)
- }
- s.f.Warnl(fn.Pos(), "declared function %v has register params", fn)
- }
+ s.f.ABIDefault = abiForFunc(nil, s.f.ABI0, s.f.ABI1)
+ s.f.ABISelf = abiForFunc(fn, s.f.ABI0, s.f.ABI1)
s.panics = map[funcLine]*ssa.Block{}
s.softFloat = s.config.SoftFloat
v := s.load(r.closure.Type.Elem(), r.closure)
s.maybeNilCheckClosure(v, callDefer)
codeptr := s.rawLoad(types.Types[types.TUINTPTR], v)
- aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+ aux := ssa.ClosureAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
} else {
aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), ACArgs, ACResults,
// critical that we not clobber any arguments already
// stored onto the stack.
codeptr = s.rawLoad(types.Types[types.TUINTPTR], closure)
- aux := ssa.ClosureAuxCall(ACArgs, ACResults)
+ aux := ssa.ClosureAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure)
case codeptr != nil:
- aux := ssa.InterfaceAuxCall(ACArgs, ACResults)
+ // Note that the "receiver" parameter is nil because the actual receiver is the first input parameter.
+ aux := ssa.InterfaceAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr)
case callee != nil:
aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), ACArgs, ACResults, params)
strings.TrimSpace(exp.dump), regResString, reason)
}
- // Analyze again with empty register set.
- empty := abi.NewABIConfig(0, 0)
- emptyRes := empty.ABIAnalyze(ft)
- emptyResString := emptyRes.String()
-
- // Walk the results and make sure the offsets assigned match
- // up with those assiged by CalcSize. 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)
- }
}