cmd/compile: pass register parameters to called function

still needs morestack
still needs results
lots of corner cases also not dealt with.

For #40724.

Change-Id: I03abdf1e8363d75c52969560b427e488a48cd37a
Reviewed-on: https://go-review.googlesource.com/c/go/+/293889
Trust: David Chase <drchase@google.com>
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
This commit is contained in:
David Chase 2021-02-18 15:50:37 -05:00
parent 95ff296a11
commit 4532467c18
4 changed files with 78 additions and 8 deletions

View file

@ -771,7 +771,8 @@ func init() {
faultOnNilArg0: true, faultOnNilArg0: true,
}, },
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem // With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific registers.
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem

View file

@ -96,6 +96,54 @@ type AuxCall struct {
abiInfo *abi.ABIParamResultInfo // TODO remove fields above redundant with this information. abiInfo *abi.ABIParamResultInfo // TODO remove fields above redundant with this information.
} }
// Reg returns the regInfo for a given call, combining the derived in/out register masks
// with the machine-specific register information in the input i. (The machine-specific
// regInfo is much handier at the call site than it is when the AuxCall is being constructed,
// therefore do this lazily).
//
// TODO: there is a Clever Hack that allows pre-generation of a small-ish number of the slices
// of inputInfo and outputInfo used here, provided that we are willing to reorder the inputs
// and outputs from calls, so that all integer registers come first, then all floating registers.
// At this point (active development of register ABI) that is very premature,
// but if this turns out to be a cost, we could do it.
func (a *AuxCall) Reg(i *regInfo, c *Config) *regInfo {
if a.reg.clobbers != 0 {
// Already updated
return a.reg
}
if a.abiInfo.InRegistersUsed()+a.abiInfo.OutRegistersUsed() == 0 {
// Shortcut for zero case, also handles old ABI.
a.reg = i
return a.reg
}
a.reg.inputs = append(a.reg.inputs, i.inputs...)
for _, p := range a.abiInfo.InParams() {
for _, r := range p.Registers {
m := archRegForAbiReg(r, c)
a.reg.inputs = append(a.reg.inputs, inputInfo{idx: len(a.reg.inputs), regs: (1 << m)})
}
}
a.reg.outputs = append(a.reg.outputs, i.outputs...)
for _, p := range a.abiInfo.OutParams() {
for _, r := range p.Registers {
m := archRegForAbiReg(r, c)
a.reg.outputs = append(a.reg.outputs, outputInfo{idx: len(a.reg.outputs), regs: (1 << m)})
}
}
a.reg.clobbers = i.clobbers
return a.reg
}
func archRegForAbiReg(r abi.RegIndex, c *Config) uint8 {
var m int8
if int(r) < len(c.intParamRegs) {
m = c.intParamRegs[r]
} else {
m = c.floatParamRegs[int(r)-len(c.intParamRegs)]
}
return uint8(m)
}
// OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc). // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc).
func (a *AuxCall) OffsetOfResult(which int64) int64 { func (a *AuxCall) OffsetOfResult(which int64) int64 {
n := int64(a.abiInfo.OutParam(int(which)).Offset()) n := int64(a.abiInfo.OutParam(int(which)).Offset())
@ -217,7 +265,11 @@ func StaticAuxCall(sym *obj.LSym, args []Param, results []Param, paramResultInfo
if paramResultInfo == nil { if paramResultInfo == nil {
panic(fmt.Errorf("Nil paramResultInfo, sym=%v", sym)) panic(fmt.Errorf("Nil paramResultInfo, sym=%v", sym))
} }
return &AuxCall{Fn: sym, args: args, results: results, abiInfo: paramResultInfo} var reg *regInfo
if paramResultInfo.InRegistersUsed()+paramResultInfo.OutRegistersUsed() > 0 {
reg = &regInfo{}
}
return &AuxCall{Fn: sym, args: args, results: results, abiInfo: paramResultInfo, reg: reg}
} }
// InterfaceAuxCall returns an AuxCall for an interface call. // InterfaceAuxCall returns an AuxCall for an interface call.

View file

@ -13229,7 +13229,7 @@ var opcodeTable = [...]opInfo{
{ {
name: "CALLstatic", name: "CALLstatic",
auxType: auxCallOff, auxType: auxCallOff,
argLen: 1, argLen: -1,
clobberFlags: true, clobberFlags: true,
call: true, call: true,
reg: regInfo{ reg: regInfo{

View file

@ -151,6 +151,14 @@ type register uint8
const noRegister register = 255 const noRegister register = 255
// For bulk initializing
var noRegisters [32]register = [32]register{
noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
}
// A regMask encodes a set of machine registers. // A regMask encodes a set of machine registers.
// TODO: regMask -> regSet? // TODO: regMask -> regSet?
type regMask uint64 type regMask uint64
@ -818,9 +826,8 @@ func (s *regAllocState) regspec(v *Value) regInfo {
return regInfo{outputs: []outputInfo{{regs: 1 << uint(reg)}}} return regInfo{outputs: []outputInfo{{regs: 1 << uint(reg)}}}
} }
if op.IsCall() { if op.IsCall() {
// TODO Panic if not okay
if ac, ok := v.Aux.(*AuxCall); ok && ac.reg != nil { if ac, ok := v.Aux.(*AuxCall); ok && ac.reg != nil {
return *ac.reg return *ac.Reg(&opcodeTable[op].reg, s.f.Config)
} }
} }
return opcodeTable[op].reg return opcodeTable[op].reg
@ -1456,7 +1463,8 @@ func (s *regAllocState) regalloc(f *Func) {
// Pick registers for outputs. // Pick registers for outputs.
{ {
outRegs := [2]register{noRegister, noRegister} outRegs := noRegisters // TODO if this is costly, hoist and clear incrementally below.
maxOutIdx := -1
var used regMask var used regMask
for _, out := range regspec.outputs { for _, out := range regspec.outputs {
mask := out.regs & s.allocatable &^ used mask := out.regs & s.allocatable &^ used
@ -1502,6 +1510,9 @@ func (s *regAllocState) regalloc(f *Func) {
mask &^= desired.avoid mask &^= desired.avoid
} }
r := s.allocReg(mask, v) r := s.allocReg(mask, v)
if out.idx > maxOutIdx {
maxOutIdx = out.idx
}
outRegs[out.idx] = r outRegs[out.idx] = r
used |= regMask(1) << r used |= regMask(1) << r
s.tmpused |= regMask(1) << r s.tmpused |= regMask(1) << r
@ -1518,8 +1529,14 @@ func (s *regAllocState) regalloc(f *Func) {
s.f.setHome(v, outLocs) s.f.setHome(v, outLocs)
// Note that subsequent SelectX instructions will do the assignReg calls. // Note that subsequent SelectX instructions will do the assignReg calls.
} else if v.Type.IsResults() { } else if v.Type.IsResults() {
// TODO register arguments need to make this work // preallocate outLocs to the right size, which is maxOutIdx+1
panic("Oops, implement this.") outLocs := make(LocResults, maxOutIdx+1, maxOutIdx+1)
for i := 0; i <= maxOutIdx; i++ {
if r := outRegs[i]; r != noRegister {
outLocs[i] = &s.registers[r]
}
}
s.f.setHome(v, outLocs)
} else { } else {
if r := outRegs[0]; r != noRegister { if r := outRegs[0]; r != noRegister {
s.assignReg(r, v, v) s.assignReg(r, v, v)