]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: pass register parameters to called function
authorDavid Chase <drchase@google.com>
Thu, 18 Feb 2021 20:50:37 +0000 (15:50 -0500)
committerDavid Chase <drchase@google.com>
Thu, 4 Mar 2021 03:30:55 +0000 (03:30 +0000)
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>
src/cmd/compile/internal/ssa/gen/AMD64Ops.go
src/cmd/compile/internal/ssa/op.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/regalloc.go

index fd2c2023e6acfd20454ad193fdd060d8840a4e7f..5f5ebaaa3552217945eb318292216ae7465567e4 100644 (file)
@@ -771,7 +771,8 @@ func init() {
                        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: "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
 
index 0bc7b0ca0d9a517a9d3fe391743e9c99cdb3e529..4082e84c6a69d2e874e1ee35dc21582ecac1f69e 100644 (file)
@@ -96,6 +96,54 @@ type AuxCall struct {
        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).
 func (a *AuxCall) OffsetOfResult(which int64) int64 {
        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 {
                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.
index a9565ffe4bb11ec73ebe8d057b6fa8c28a1f86a2..34445cfbf19ed57257e496c49fab3eb36c984459 100644 (file)
@@ -13229,7 +13229,7 @@ var opcodeTable = [...]opInfo{
        {
                name:         "CALLstatic",
                auxType:      auxCallOff,
-               argLen:       1,
+               argLen:       -1,
                clobberFlags: true,
                call:         true,
                reg: regInfo{
index c11138bf4e5eca433e864fc8c05ba7191beb5a7b..c2d0478e8284ede7cbd02c6cd8b26bcfe3009c4e 100644 (file)
@@ -151,6 +151,14 @@ type register uint8
 
 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.
 // TODO: regMask -> regSet?
 type regMask uint64
@@ -818,9 +826,8 @@ func (s *regAllocState) regspec(v *Value) regInfo {
                return regInfo{outputs: []outputInfo{{regs: 1 << uint(reg)}}}
        }
        if op.IsCall() {
-               // TODO Panic if not okay
                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
@@ -1456,7 +1463,8 @@ func (s *regAllocState) regalloc(f *Func) {
 
                        // 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
                                for _, out := range regspec.outputs {
                                        mask := out.regs & s.allocatable &^ used
@@ -1502,6 +1510,9 @@ func (s *regAllocState) regalloc(f *Func) {
                                                mask &^= desired.avoid
                                        }
                                        r := s.allocReg(mask, v)
+                                       if out.idx > maxOutIdx {
+                                               maxOutIdx = out.idx
+                                       }
                                        outRegs[out.idx] = r
                                        used |= regMask(1) << r
                                        s.tmpused |= regMask(1) << r
@@ -1518,8 +1529,14 @@ func (s *regAllocState) regalloc(f *Func) {
                                        s.f.setHome(v, outLocs)
                                        // Note that subsequent SelectX instructions will do the assignReg calls.
                                } else if v.Type.IsResults() {
-                                       // TODO register arguments need to make this work
-                                       panic("Oops, implement this.")
+                                       // preallocate outLocs to the right size, which is maxOutIdx+1
+                                       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 {
                                        if r := outRegs[0]; r != noRegister {
                                                s.assignReg(r, v, v)