aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorDavid Chase <drchase@google.com>2021-02-23 20:00:31 -0500
committerDavid Chase <drchase@google.com>2021-03-04 20:18:16 +0000
commitc015f76acb73990d4cb7fb056165b64d79b1b037 (patch)
tree264fdffc93c704fea2aee0da55072196d02ea4bd /src/cmd
parent77505c25d83a2130011736d6a2a915eaa3ae230a (diff)
downloadgo-c015f76acb73990d4cb7fb056165b64d79b1b037.tar.gz
go-c015f76acb73990d4cb7fb056165b64d79b1b037.zip
cmd/compile: implement too-big-to-SSA struct passing in registers
Added a test that exercises named results Change-Id: Ie228b68f4f846266595a95e0f65a6e4b8bf79635 Reviewed-on: https://go-review.googlesource.com/c/go/+/297029 Trust: David Chase <drchase@google.com> Run-TryBot: David Chase <drchase@google.com> Reviewed-by: Cherry Zhang <cherryyz@google.com>
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/compile/internal/abi/abiutils.go59
-rw-r--r--src/cmd/compile/internal/ssa/expand_calls.go26
-rw-r--r--src/cmd/compile/internal/ssagen/ssa.go27
3 files changed, 86 insertions, 26 deletions
diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go
index ffa709965c..3c07be62e0 100644
--- a/src/cmd/compile/internal/abi/abiutils.go
+++ b/src/cmd/compile/internal/abi/abiutils.go
@@ -118,12 +118,22 @@ func RegisterTypes(apa []ABIParamAssignment) []*types.Type {
if len(pa.Registers) == 0 {
continue
}
- rts = appendParamRegs(rts, pa.Type)
+ rts = appendParamTypes(rts, pa.Type)
}
return rts
}
-func appendParamRegs(rts []*types.Type, t *types.Type) []*types.Type {
+func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64) {
+ l := len(pa.Registers)
+ if l == 0 {
+ return nil, nil
+ }
+ typs := make([]*types.Type, 0, l)
+ offs := make([]int64, 0, l)
+ return appendParamTypes(typs, pa.Type), appendParamOffsets(offs, 0, pa.Type)
+}
+
+func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
if t.IsScalar() || t.IsPtrShaped() {
if t.IsComplex() {
c := types.FloatForComplex(t)
@@ -146,25 +156,60 @@ func appendParamRegs(rts []*types.Type, t *types.Type) []*types.Type {
switch typ {
case types.TARRAY:
for i := int64(0); i < t.Size(); i++ { // 0 gets no registers, plus future-proofing.
- rts = appendParamRegs(rts, t.Elem())
+ rts = appendParamTypes(rts, t.Elem())
}
case types.TSTRUCT:
for _, f := range t.FieldSlice() {
if f.Type.Size() > 0 { // embedded zero-width types receive no registers
- rts = appendParamRegs(rts, f.Type)
+ rts = appendParamTypes(rts, f.Type)
}
}
case types.TSLICE:
- return appendParamRegs(rts, synthSlice)
+ return appendParamTypes(rts, synthSlice)
case types.TSTRING:
- return appendParamRegs(rts, synthString)
+ return appendParamTypes(rts, synthString)
case types.TINTER:
- return appendParamRegs(rts, synthIface)
+ return appendParamTypes(rts, synthIface)
}
}
return rts
}
+// appendParamOffsets appends the offset(s) of type t, starting from "at",
+// to input offsets, and returns the longer slice.
+func appendParamOffsets(offsets []int64, at int64, t *types.Type) []int64 {
+ at = align(at, t)
+ if t.IsScalar() || t.IsPtrShaped() {
+ if t.IsComplex() || int(t.Width) > types.RegSize { // complex and *int64 on 32-bit
+ s := t.Width / 2
+ return append(offsets, at, at+s)
+ } else {
+ return append(offsets, at)
+ }
+ } else {
+ typ := t.Kind()
+ switch typ {
+ case types.TARRAY:
+ for i := int64(0); i < t.NumElem(); i++ {
+ offsets = appendParamOffsets(offsets, at, t.Elem())
+ }
+ return offsets
+ case types.TSTRUCT:
+ for _, f := range t.FieldSlice() {
+ offsets = appendParamOffsets(offsets, at, f.Type)
+ at += f.Type.Width
+ }
+ case types.TSLICE:
+ return appendParamOffsets(offsets, at, synthSlice)
+ case types.TSTRING:
+ return appendParamOffsets(offsets, at, synthString)
+ case types.TINTER:
+ return appendParamOffsets(offsets, at, synthIface)
+ }
+ }
+ return offsets
+}
+
// SpillOffset returns the offset *within the spill area* for the parameter that "a" describes.
// Registers will be spilled here; if a memory home is needed (for a pointer method e.g.)
// then that will be the address.
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 6e14a90e79..fd8ae30caf 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -657,7 +657,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
case OpCopy:
return x.storeArgOrLoad(pos, b, source.Args[0], mem, t, offset, loadRegOffset, storeRc)
- case OpLoad:
+ case OpLoad, OpDereference:
ret := x.decomposeArgOrLoad(pos, b, source, mem, t, offset, loadRegOffset, storeRc, storeOneLoad, storeTwoLoad)
if ret != nil {
return ret
@@ -820,6 +820,9 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
}
s := mem
+ if source.Op == OpDereference {
+ source.Op = OpLoad // For purposes of parameter passing expansion, a Dereference is a Load.
+ }
if storeRc.hasRegs() {
storeRc.addArg(source)
} else {
@@ -846,14 +849,11 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) (*Value, []*Value) {
auxI := int64(i)
aRegs := aux.RegsOfArg(auxI)
aType := aux.TypeOfArg(auxI)
- if a.Op == OpDereference {
+ if len(aRegs) == 0 && a.Op == OpDereference {
aOffset := aux.OffsetOfArg(auxI)
if a.MemoryArg() != m0 {
x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString())
}
- if len(aRegs) > 0 {
- x.f.Fatalf("Not implemented yet, not-SSA-type %v passed in registers", aType)
- }
// "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
// TODO(register args) this will be more complicated with registers in the picture.
mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, pos)
@@ -969,10 +969,7 @@ func expandCalls(f *Func) {
auxOffset := int64(0)
auxSize := aux.SizeOfResult(i)
aRegs := aux.RegsOfResult(int64(j))
- if a.Op == OpDereference {
- if len(aRegs) > 0 {
- x.f.Fatalf("Not implemented yet, not-SSA-type %v returned in register", auxType)
- }
+ if len(aRegs) == 0 && a.Op == OpDereference {
// Avoid a self-move, and if one is detected try to remove the already-inserted VarDef for the assignment that won't happen.
if dAddr, dMem := a.Args[0], a.Args[1]; dAddr.Op == OpLocalAddr && dAddr.Args[0].Op == OpSP &&
dAddr.Args[1] == dMem && dAddr.Aux == aux.results[i].Name {
@@ -1392,3 +1389,14 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
}
}
}
+
+// argOpAndRegisterFor converts an abi register index into an ssa Op and corresponding
+// arg register index.
+// TODO could call this in at least two places earlier in this file.
+func ArgOpAndRegisterFor(r abi.RegIndex, abiConfig *abi.ABIConfig) (Op, int64) {
+ i := abiConfig.FloatIndexFor(r)
+ if i >= 0 { // float PR
+ return OpArgFloatReg, i
+ }
+ return OpArgIntReg, int64(r)
+}
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 9ee855343f..2a281860af 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -555,19 +555,26 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
}
s.vars[n] = v
s.addNamedValue(n, v) // This helps with debugging information, not needed for compilation itself.
- } else if !s.canSSAName(n) { // I.e., the address was taken. The type may or may not be okay.
- // If the value will arrive in registers,
- // AND if it can be SSA'd (if it cannot, panic for now),
- // THEN
- // (1) receive it as an OpArg (but do not store its name in the var table)
- // (2) store it to its spill location, which is its address as well.
+ } else { // address was taken AND/OR too large for SSA
paramAssignment := ssa.ParamAssignmentForArgName(s.f, n)
if len(paramAssignment.Registers) > 0 {
- if !TypeOK(n.Type()) { // TODO register args -- if v is not an SSA-able type, must decompose, here.
- panic(fmt.Errorf("Arg in registers is too big to be SSA'd, need to implement decomposition, type=%v, n=%v", n.Type(), n))
+ if TypeOK(n.Type()) { // SSA-able type, so address was taken -- receive value in OpArg, DO NOT bind to var, store immediately to memory.
+ v := s.newValue0A(ssa.OpArg, n.Type(), n)
+ s.store(n.Type(), s.decladdrs[n], v)
+ } else { // Too big for SSA.
+ // Brute force, and early, do a bunch of stores from registers
+ // TODO fix the nasty storeArgOrLoad recursion in ssa/expand_calls.go so this Just Works with store of a big Arg.
+ typs, offs := paramAssignment.RegisterTypesAndOffsets()
+ for i, t := range typs {
+ o := offs[i]
+ r := paramAssignment.Registers[i]
+ op, reg := ssa.ArgOpAndRegisterFor(r, s.f.ABISelf)
+ v := s.newValue0I(op, t, reg)
+ v.Aux = &ssa.AuxNameOffset{Name: n, Offset: o}
+ p := s.newValue1I(ssa.OpOffPtr, types.NewPtr(n.Type()), o, s.decladdrs[n])
+ s.store(t, p, v)
+ }
}
- v := s.newValue0A(ssa.OpArg, n.Type(), n)
- s.store(n.Type(), s.decladdrs[n], v)
}
}
}