]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: spill output parameters passed in registers as autos
authorDavid Chase <drchase@google.com>
Thu, 11 Mar 2021 01:54:11 +0000 (20:54 -0500)
committerDavid Chase <drchase@google.com>
Mon, 15 Mar 2021 20:48:37 +0000 (20:48 +0000)
ALSO:
found evidence that stack maps for bodyless methods are wrong.
gofmt in test/abi
removed never-executed code in types/size.go

Updates #44816.

Change-Id: I658c33f049337fb6f1e625f0c55430d25bfa877e
Reviewed-on: https://go-review.googlesource.com/c/go/+/300749
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>
17 files changed:
src/cmd/compile/internal/abi/abiutils.go
src/cmd/compile/internal/dwarfgen/dwarf.go
src/cmd/compile/internal/gc/compile.go
src/cmd/compile/internal/ir/name.go
src/cmd/compile/internal/liveness/plive.go
src/cmd/compile/internal/ssa/op.go
src/cmd/compile/internal/ssagen/pgen.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/test/abiutilsaux_test.go
src/cmd/compile/internal/types/size.go
src/cmd/compile/internal/types/type.go
test/abi/fibish2.go
test/abi/leaf.go [new file with mode: 0644]
test/abi/leaf2.go [new file with mode: 0644]
test/abi/methods.go
test/abi/spills3.go [new file with mode: 0644]
test/abi/spills4.go [new file with mode: 0644]

index ecde34313ac08e7e8fd3efbce7959f22857db627..e92827fc7f4d5fb9d1530e2f3270ca1f09d33b2d 100644 (file)
@@ -69,6 +69,14 @@ func (a *ABIParamResultInfo) SpillAreaSize() int64 {
        return a.spillAreaSize
 }
 
+// ArgWidth returns the amount of stack needed for all the inputs
+// and outputs of a function or method, including ABI-defined parameter
+// slots and ABI-defined spill slots for register-resident parameters.
+// The name is inherited from (*Type).ArgWidth(), which it replaces.
+func (a *ABIParamResultInfo) ArgWidth() int64 {
+       return a.spillAreaSize + a.offsetToSpillArea
+}
+
 // RegIndex stores the index into the set of machine registers used by
 // the ABI on a specific architecture for parameter passing.  RegIndex
 // values 0 through N-1 (where N is the number of integer registers
@@ -414,20 +422,25 @@ func (config *ABIConfig) ABIAnalyzeFuncType(ft *types.Func) *ABIParamResultInfo
 
 // ABIAnalyze returns the same result as ABIAnalyzeFuncType, but also
 // updates the offsets of all the receiver, input, and output fields.
-func (config *ABIConfig) ABIAnalyze(t *types.Type) *ABIParamResultInfo {
+// If setNname is true, it also sets the FrameOffset of the Nname for
+// the field(s); this is for use when compiling a function and figuring out
+// spill locations.  Doing this for callers can cause races for register
+// outputs because their frame location transitions from BOGUS_FUNARG_OFFSET
+// to zero to an as-if-AUTO offset that has no use for callers.
+func (config *ABIConfig) ABIAnalyze(t *types.Type, setNname bool) *ABIParamResultInfo {
        ft := t.FuncType()
        result := config.ABIAnalyzeFuncType(ft)
        // 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)
+               config.updateOffset(result, ft.Receiver.FieldSlice()[0], result.inparams[0], false, setNname)
                k++
        }
        for i, f := range ft.Params.FieldSlice() {
-               config.updateOffset(result, f, result.inparams[k+i], false)
+               config.updateOffset(result, f, result.inparams[k+i], false, setNname)
        }
        for i, f := range ft.Results.FieldSlice() {
-               config.updateOffset(result, f, result.outparams[i], true)
+               config.updateOffset(result, f, result.outparams[i], true, setNname)
        }
        return result
 }
@@ -442,7 +455,7 @@ func FieldOffsetOf(f *types.Field) int64 {
        return f.Offset
 }
 
-func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn bool) {
+func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn, setNname bool) {
        // Everything except return values in registers has either a frame home (if not in a register) or a frame spill location.
        if !isReturn || len(a.Registers) == 0 {
                // The type frame offset DOES NOT show effects of minimum frame size.
@@ -455,11 +468,19 @@ func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field
                        // Set the Offset the first time. After that, we may recompute it, but it should never change.
                        f.Offset = off
                        if f.Nname != nil {
+                               // always set it in this case.
                                f.Nname.(*ir.Name).SetFrameOffset(off)
+                               f.Nname.(*ir.Name).SetIsOutputParamInRegisters(false)
                        }
                } else if fOffset != off {
                        panic(fmt.Errorf("Offset changed from %d to %d", fOffset, off))
                }
+       } else {
+               if setNname && f.Nname != nil {
+                       fname := f.Nname.(*ir.Name)
+                       fname.SetIsOutputParamInRegisters(true)
+                       fname.SetFrameOffset(0)
+               }
        }
 }
 
index 70168cffebf8110c2d67950703474162b154f66b..53752097ed398b06ff7f65f28c7d71f8fbb444ee 100644 (file)
@@ -265,6 +265,13 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
        var offs int64
 
        switch n.Class {
+       case ir.PPARAM, ir.PPARAMOUT:
+               if !n.IsOutputParamInRegisters() {
+                       abbrev = dwarf.DW_ABRV_PARAM
+                       offs = n.FrameOffset() + base.Ctxt.FixedFrameSize()
+                       break
+               }
+               fallthrough
        case ir.PAUTO:
                offs = n.FrameOffset()
                abbrev = dwarf.DW_ABRV_AUTO
@@ -275,9 +282,6 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
                        offs -= int64(types.PtrSize)
                }
 
-       case ir.PPARAM, ir.PPARAMOUT:
-               abbrev = dwarf.DW_ABRV_PARAM
-               offs = n.FrameOffset() + base.Ctxt.FixedFrameSize()
        default:
                base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
        }
index 2d7a74a403c7b929e4a7996115bbc356597cba6f..83cfceb2c87e22bcaf4184f9d216156c7baf87bb 100644 (file)
@@ -45,7 +45,7 @@ func enqueueFunc(fn *ir.Func) {
                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
+               a.ABIAnalyze(fn.Type(), true) // will set parameter spill/home locations correctly
                liveness.WriteFuncMap(fn)
                return
        }
index 16c30324e5fba869913edca92977a8943484ac64..5738aa1f3fc226ffc6f80b564f780b23a5978c97 100644 (file)
@@ -246,44 +246,47 @@ func (n *Name) Alias() bool { return n.flags&nameAlias != 0 }
 func (n *Name) SetAlias(alias bool) { n.flags.set(nameAlias, alias) }
 
 const (
-       nameReadonly              = 1 << iota
-       nameByval                 // is the variable captured by value or by reference
-       nameNeedzero              // if it contains pointers, needs to be zeroed on function entry
-       nameAutoTemp              // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
-       nameUsed                  // for variable declared and not used error
-       nameIsClosureVar          // PAUTOHEAP closure pseudo-variable; original (if any) at n.Defn
-       nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
-       nameAddrtaken             // address taken, even if not moved to heap
-       nameInlFormal             // PAUTO created by inliner, derived from callee formal
-       nameInlLocal              // PAUTO created by inliner, derived from callee local
-       nameOpenDeferSlot         // if temporary var storing info for open-coded defers
-       nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section
-       nameAlias                 // is type name an alias
+       nameReadonly                 = 1 << iota
+       nameByval                    // is the variable captured by value or by reference
+       nameNeedzero                 // if it contains pointers, needs to be zeroed on function entry
+       nameAutoTemp                 // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
+       nameUsed                     // for variable declared and not used error
+       nameIsClosureVar             // PAUTOHEAP closure pseudo-variable; original (if any) at n.Defn
+       nameIsOutputParamHeapAddr    // pointer to a result parameter's heap copy
+       nameIsOutputParamInRegisters // output parameter in registers spills as an auto
+       nameAddrtaken                // address taken, even if not moved to heap
+       nameInlFormal                // PAUTO created by inliner, derived from callee formal
+       nameInlLocal                 // PAUTO created by inliner, derived from callee local
+       nameOpenDeferSlot            // if temporary var storing info for open-coded defers
+       nameLibfuzzerExtraCounter    // if PEXTERN should be assigned to __libfuzzer_extra_counters section
+       nameAlias                    // is type name an alias
 )
 
-func (n *Name) Readonly() bool              { return n.flags&nameReadonly != 0 }
-func (n *Name) Needzero() bool              { return n.flags&nameNeedzero != 0 }
-func (n *Name) AutoTemp() bool              { return n.flags&nameAutoTemp != 0 }
-func (n *Name) Used() bool                  { return n.flags&nameUsed != 0 }
-func (n *Name) IsClosureVar() bool          { return n.flags&nameIsClosureVar != 0 }
-func (n *Name) IsOutputParamHeapAddr() bool { return n.flags&nameIsOutputParamHeapAddr != 0 }
-func (n *Name) Addrtaken() bool             { return n.flags&nameAddrtaken != 0 }
-func (n *Name) InlFormal() bool             { return n.flags&nameInlFormal != 0 }
-func (n *Name) InlLocal() bool              { return n.flags&nameInlLocal != 0 }
-func (n *Name) OpenDeferSlot() bool         { return n.flags&nameOpenDeferSlot != 0 }
-func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
-
-func (n *Name) setReadonly(b bool)              { n.flags.set(nameReadonly, b) }
-func (n *Name) SetNeedzero(b bool)              { n.flags.set(nameNeedzero, b) }
-func (n *Name) SetAutoTemp(b bool)              { n.flags.set(nameAutoTemp, b) }
-func (n *Name) SetUsed(b bool)                  { n.flags.set(nameUsed, b) }
-func (n *Name) SetIsClosureVar(b bool)          { n.flags.set(nameIsClosureVar, b) }
-func (n *Name) SetIsOutputParamHeapAddr(b bool) { n.flags.set(nameIsOutputParamHeapAddr, b) }
-func (n *Name) SetAddrtaken(b bool)             { n.flags.set(nameAddrtaken, b) }
-func (n *Name) SetInlFormal(b bool)             { n.flags.set(nameInlFormal, b) }
-func (n *Name) SetInlLocal(b bool)              { n.flags.set(nameInlLocal, b) }
-func (n *Name) SetOpenDeferSlot(b bool)         { n.flags.set(nameOpenDeferSlot, b) }
-func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) }
+func (n *Name) Readonly() bool                 { return n.flags&nameReadonly != 0 }
+func (n *Name) Needzero() bool                 { return n.flags&nameNeedzero != 0 }
+func (n *Name) AutoTemp() bool                 { return n.flags&nameAutoTemp != 0 }
+func (n *Name) Used() bool                     { return n.flags&nameUsed != 0 }
+func (n *Name) IsClosureVar() bool             { return n.flags&nameIsClosureVar != 0 }
+func (n *Name) IsOutputParamHeapAddr() bool    { return n.flags&nameIsOutputParamHeapAddr != 0 }
+func (n *Name) IsOutputParamInRegisters() bool { return n.flags&nameIsOutputParamInRegisters != 0 }
+func (n *Name) Addrtaken() bool                { return n.flags&nameAddrtaken != 0 }
+func (n *Name) InlFormal() bool                { return n.flags&nameInlFormal != 0 }
+func (n *Name) InlLocal() bool                 { return n.flags&nameInlLocal != 0 }
+func (n *Name) OpenDeferSlot() bool            { return n.flags&nameOpenDeferSlot != 0 }
+func (n *Name) LibfuzzerExtraCounter() bool    { return n.flags&nameLibfuzzerExtraCounter != 0 }
+
+func (n *Name) setReadonly(b bool)                 { n.flags.set(nameReadonly, b) }
+func (n *Name) SetNeedzero(b bool)                 { n.flags.set(nameNeedzero, b) }
+func (n *Name) SetAutoTemp(b bool)                 { n.flags.set(nameAutoTemp, b) }
+func (n *Name) SetUsed(b bool)                     { n.flags.set(nameUsed, b) }
+func (n *Name) SetIsClosureVar(b bool)             { n.flags.set(nameIsClosureVar, b) }
+func (n *Name) SetIsOutputParamHeapAddr(b bool)    { n.flags.set(nameIsOutputParamHeapAddr, b) }
+func (n *Name) SetIsOutputParamInRegisters(b bool) { n.flags.set(nameIsOutputParamInRegisters, b) }
+func (n *Name) SetAddrtaken(b bool)                { n.flags.set(nameAddrtaken, b) }
+func (n *Name) SetInlFormal(b bool)                { n.flags.set(nameInlFormal, b) }
+func (n *Name) SetInlLocal(b bool)                 { n.flags.set(nameInlLocal, b) }
+func (n *Name) SetOpenDeferSlot(b bool)            { n.flags.set(nameOpenDeferSlot, b) }
+func (n *Name) SetLibfuzzerExtraCounter(b bool)    { n.flags.set(nameLibfuzzerExtraCounter, b) }
 
 // OnStack reports whether variable n may reside on the stack.
 func (n *Name) OnStack() bool {
index 48a26cf66a4987575f5e2d45b1fade5d4425981e..f3fbb8b9b1d4160fac5a29c813ee5f3ef2c30863 100644 (file)
@@ -405,11 +405,17 @@ func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, loc
                }
                node := vars[i]
                switch node.Class {
+               case ir.PPARAM, ir.PPARAMOUT:
+                       if !node.IsOutputParamInRegisters() {
+                               if node.FrameOffset() < 0 {
+                                       lv.f.Fatalf("Node %v has frameoffset %d\n", node.Sym().Name, node.FrameOffset())
+                               }
+                               typebits.Set(node.Type(), node.FrameOffset(), args)
+                               break
+                       }
+                       fallthrough // PPARAMOUT in registers acts memory-allocates like an AUTO
                case ir.PAUTO:
                        typebits.Set(node.Type(), node.FrameOffset()+lv.stkptrsize, locals)
-
-               case ir.PPARAM, ir.PPARAMOUT:
-                       typebits.Set(node.Type(), node.FrameOffset(), args)
                }
        }
 }
@@ -1083,8 +1089,10 @@ func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
        for _, n := range lv.vars {
                switch n.Class {
                case ir.PPARAM, ir.PPARAMOUT:
-                       if maxArgNode == nil || n.FrameOffset() > maxArgNode.FrameOffset() {
-                               maxArgNode = n
+                       if !n.IsOutputParamInRegisters() {
+                               if maxArgNode == nil || n.FrameOffset() > maxArgNode.FrameOffset() {
+                                       maxArgNode = n
+                               }
                        }
                }
        }
@@ -1282,6 +1290,7 @@ func isfat(t *types.Type) bool {
        return false
 }
 
+// TODO THIS IS ALL WRONG AND NEEDS TO USE ABI.
 func WriteFuncMap(fn *ir.Func) {
        if ir.FuncName(fn) == "_" || fn.Sym().Linkname != "" {
                return
index 084098fb64e771cba3c4bf64be4d5a1b0dca2e3f..fe9ba0e156f255384cf8da8323cd4d9e27b19833 100644 (file)
@@ -191,6 +191,16 @@ func archRegForAbiReg(r abi.RegIndex, c *Config) uint8 {
        return uint8(m)
 }
 
+// ArgWidth returns the amount of stack needed for all the inputs
+// and outputs of a function or method, including ABI-defined parameter
+// slots and ABI-defined spill slots for register-resident parameters.
+//
+// The name is taken from the types package's ArgWidth(<function type>),
+// which predated changes to the ABI; this version handles those changes.
+func (a *AuxCall) ArgWidth() int64 {
+       return a.abiInfo.ArgWidth()
+}
+
 // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc).
 func (a *AuxCall) ParamAssignmentForResult(which int64) *abi.ABIParamAssignment {
        return a.abiInfo.OutParam(int(which))
index 8fa5980dabe8933bf259c687eac8e69bb6ded8c6..0088f10fa88a581b4aa2f36ca139ae9537dad2ee 100644 (file)
@@ -104,7 +104,9 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
        // Reassign stack offsets of the locals that are used.
        lastHasPtr := false
        for i, n := range fn.Dcl {
-               if n.Op() != ir.ONAME || n.Class != ir.PAUTO {
+               if n.Op() != ir.ONAME || n.Class != ir.PAUTO && !(n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters()) {
+                       // i.e., stack assign if AUTO, or if PARAMOUT in registers (which has no predefined spill locations)
+                       // TODO figure out when we don't need to spill output params.
                        continue
                }
                if !n.Used() {
@@ -148,9 +150,9 @@ const maxStackSize = 1 << 30
 func Compile(fn *ir.Func, worker int) {
        f := buildssa(fn, worker)
        // Note: check arg size to fix issue 25507.
-       if f.Frontend().(*ssafn).stksize >= maxStackSize || fn.Type().ArgWidth() >= maxStackSize {
+       if f.Frontend().(*ssafn).stksize >= maxStackSize || f.OwnAux.ArgWidth() >= maxStackSize {
                largeStackFramesMu.Lock()
-               largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: fn.Type().ArgWidth(), pos: fn.Pos()})
+               largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: f.OwnAux.ArgWidth(), pos: fn.Pos()})
                largeStackFramesMu.Unlock()
                return
        }
@@ -166,7 +168,7 @@ func Compile(fn *ir.Func, worker int) {
        if pp.Text.To.Offset >= maxStackSize {
                largeStackFramesMu.Lock()
                locals := f.Frontend().(*ssafn).stksize
-               largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: fn.Type().ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
+               largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: f.OwnAux.ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
                largeStackFramesMu.Unlock()
                return
        }
@@ -189,6 +191,12 @@ func StackOffset(slot ssa.LocalSlot) int32 {
        n := slot.N
        var off int64
        switch n.Class {
+       case ir.PPARAM, ir.PPARAMOUT:
+               if !n.IsOutputParamInRegisters() {
+                       off = n.FrameOffset() + base.Ctxt.FixedFrameSize()
+                       break
+               }
+               fallthrough // PPARAMOUT in registers allocates like an AUTO
        case ir.PAUTO:
                off = n.FrameOffset()
                if base.Ctxt.FixedFrameSize() == 0 {
@@ -197,8 +205,6 @@ func StackOffset(slot ssa.LocalSlot) int32 {
                if objabi.Framepointer_enabled {
                        off -= int64(types.PtrSize)
                }
-       case ir.PPARAM, ir.PPARAMOUT:
-               off = n.FrameOffset() + base.Ctxt.FixedFrameSize()
        }
        return int32(off + slot.Off)
 }
index 00295589638c2fc9d2d0f5fb835dc4bb1b17882d..6a3c0d28cbd854a8e660455d5d9ce1f189cd4577 100644 (file)
@@ -372,7 +372,7 @@ func (s *state) emitOpenDeferInfo() {
 }
 
 func okOffset(offset int64) int64 {
-       if offset >= types.BOGUS_FUNARG_OFFSET {
+       if offset == types.BOGUS_FUNARG_OFFSET {
                panic(fmt.Errorf("Bogus offset %d", offset))
        }
        return offset
@@ -516,7 +516,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
        }
 
        var params *abi.ABIParamResultInfo
-       params = s.f.ABISelf.ABIAnalyze(fn.Type())
+       params = s.f.ABISelf.ABIAnalyze(fn.Type(), true)
 
        // Generate addresses of local declarations
        s.decladdrs = map[*ir.Name]*ssa.Value{}
@@ -4914,8 +4914,6 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
                        closure = iclosure
                }
        }
-       types.CalcSize(fn.Type())
-       stksize := fn.Type().ArgWidth() // includes receiver, args, and results
 
        if regAbiForFuncType(n.X.Type().FuncType()) {
                // fmt.Printf("Saw magic last type in call %v\n", n)
@@ -4927,7 +4925,9 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
                callABI = s.f.ABI0
        }
 
-       params := callABI.ABIAnalyze(n.X.Type())
+       params := callABI.ABIAnalyze(n.X.Type(), false /* Do not set (register) nNames from caller side -- can cause races. */ )
+       types.CalcSize(fn.Type())
+       stksize := params.ArgWidth() // includes receiver, args, and results
 
        res := n.X.Type().Results()
        if k == callNormal {
@@ -6838,13 +6838,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
                f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String())
        }
 
-       defframe(&s, e)
+       defframe(&s, e, f)
 
        f.HTMLWriter.Close()
        f.HTMLWriter = nil
 }
 
-func defframe(s *State, e *ssafn) {
+func defframe(s *State, e *ssafn, f *ssa.Func) {
        pp := s.pp
 
        frame := types.Rnd(s.maxarg+e.stksize, int64(types.RegSize))
@@ -6854,7 +6854,7 @@ func defframe(s *State, e *ssafn) {
 
        // Fill in argument and frame size.
        pp.Text.To.Type = obj.TYPE_TEXTSIZE
-       pp.Text.To.Val = int32(types.Rnd(e.curfn.Type().ArgWidth(), int64(types.RegSize)))
+       pp.Text.To.Val = int32(types.Rnd(f.OwnAux.ArgWidth(), int64(types.RegSize)))
        pp.Text.To.Offset = frame
 
        // Insert code to zero ambiguously live variables so that the
@@ -6957,14 +6957,18 @@ func AddAux2(a *obj.Addr, v *ssa.Value, offset int64) {
                a.Name = obj.NAME_EXTERN
                a.Sym = n
        case *ir.Name:
-               if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+               if n.Class == ir.PPARAM || (n.Class == ir.PPARAMOUT && !n.IsOutputParamInRegisters()) {
                        a.Name = obj.NAME_PARAM
                        a.Sym = ir.Orig(n).(*ir.Name).Linksym()
                        a.Offset += n.FrameOffset()
                        break
                }
                a.Name = obj.NAME_AUTO
-               a.Sym = n.Linksym()
+               if n.Class == ir.PPARAMOUT {
+                       a.Sym = ir.Orig(n).(*ir.Name).Linksym()
+               } else {
+                       a.Sym = n.Linksym()
+               }
                a.Offset += n.FrameOffset()
        default:
                v.Fatalf("aux in %s not implemented %#v", v, v.Aux)
@@ -7108,7 +7112,7 @@ func AddrAuto(a *obj.Addr, v *ssa.Value) {
        a.Sym = n.Linksym()
        a.Reg = int16(Arch.REGSP)
        a.Offset = n.FrameOffset() + off
-       if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+       if n.Class == ir.PPARAM || (n.Class == ir.PPARAMOUT && !n.IsOutputParamInRegisters()) {
                a.Name = obj.NAME_PARAM
        } else {
                a.Name = obj.NAME_AUTO
@@ -7545,10 +7549,10 @@ func AddrForParamSlot(slot *ssa.LocalSlot, addr *obj.Addr) {
        addr.Type = obj.TYPE_MEM
        addr.Sym = n.Linksym()
        addr.Offset = off
-       if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+       if n.Class == ir.PPARAM || (n.Class == ir.PPARAMOUT && !n.IsOutputParamInRegisters()) {
                addr.Name = obj.NAME_PARAM
                addr.Offset += n.FrameOffset()
-       } else {
+       } else { // out parameters in registers allocate stack slots like autos.
                addr.Name = obj.NAME_AUTO
        }
 }
index 7eb273273dbcbffdb1ecd95a32f3f5f1eeb985d4..b9456331332b3e33ec5dd1cf0ed10b67fee22673 100644 (file)
@@ -119,7 +119,7 @@ func abitest(t *testing.T, ft *types.Type, exp expectedDump) {
        types.CalcSize(ft)
 
        // Analyze with full set of registers.
-       regRes := configAMD64.ABIAnalyze(ft)
+       regRes := configAMD64.ABIAnalyze(ft, false)
        regResString := strings.TrimSpace(regRes.String())
 
        // Check results.
index ef23cdf5fe3bbbec78a48733b13eea7ac65666c8..a75429f0abadb8dc1c80ce944221b6253f79098a 100644 (file)
@@ -163,19 +163,9 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
                if f.Type.Align > 0 {
                        o = Rnd(o, int64(f.Type.Align))
                }
-               if isStruct { // For receiver/args/results, depends on ABI
+               if isStruct { // For receiver/args/results, do not set, it depends on ABI
                        f.Offset = o
                }
-               if f.Nname != nil {
-                       // addrescapes has similar code to update these offsets.
-                       // Usually addrescapes runs after calcStructOffset,
-                       // in which case we could drop this,
-                       // but function closure functions are the exception.
-                       // NOTE(rsc): This comment may be stale.
-                       // It's possible the ordering has changed and this is
-                       // now the common case. I'm not sure.
-                       f.Nname.(VarObject).RecordFrameOffset(o)
-               }
 
                w := f.Type.Width
                if w < 0 {
index ffaf7553450de1711b0aaf2e190094f322b436da..7bf63764b86e614cfceb3c20f0f309fe8d27a133 100644 (file)
@@ -1750,7 +1750,7 @@ func NewTypeParam(pkg *Pkg) *Type {
        return t
 }
 
-const BOGUS_FUNARG_OFFSET = 1000000000
+const BOGUS_FUNARG_OFFSET = -1000000000
 
 func unzeroFieldOffsets(f []*Field) {
        for i := range f {
@@ -1759,7 +1759,7 @@ func unzeroFieldOffsets(f []*Field) {
 }
 
 // NewSignature returns a new function type for the given receiver,
-// parametes, results, and type parameters, any of which may be nil.
+// parameters, results, and type parameters, any of which may be nil.
 func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Type {
        var recvs []*Field
        if recv != nil {
index 14f3f9ada788c3da1443fcd7230c8c308b3391d1..388aabc8b04956a47cbd966f81e17897d78c5fb5 100644 (file)
@@ -13,12 +13,12 @@ import "fmt"
 
 // Test that register results are correctly returned (and passed)
 
-type MagicLastTypeNameForTestingRegisterABI func(int,MagicLastTypeNameForTestingRegisterABI) int
+type MagicLastTypeNameForTestingRegisterABI func(int, MagicLastTypeNameForTestingRegisterABI) int
 
 //go:registerparams
 //go:noinline
 func minus(decrement int) MagicLastTypeNameForTestingRegisterABI {
-       return MagicLastTypeNameForTestingRegisterABI( func(x int, _ MagicLastTypeNameForTestingRegisterABI) int { return x-decrement} )
+       return MagicLastTypeNameForTestingRegisterABI(func(x int, _ MagicLastTypeNameForTestingRegisterABI) int { return x - decrement })
 }
 
 //go:noinline
diff --git a/test/abi/leaf.go b/test/abi/leaf.go
new file mode 100644 (file)
index 0000000..f893f5d
--- /dev/null
@@ -0,0 +1,36 @@
+// run
+
+//go:build !wasm
+// +build !wasm
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// wasm is excluded because the compiler chatter about register abi pragma ends up
+// on stdout, and causes the expected output to not match.
+
+package main
+
+import "fmt"
+
+type i5f5 struct {
+       a, b          int16
+       c, d, e       int32
+       r, s, t, u, v float32
+}
+
+//go:registerparams
+//go:noinline
+func F(x i5f5) i5f5 {
+       return x
+}
+
+func main() {
+       x := i5f5{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+       y := x
+       z := F(x)
+       if y != z {
+               fmt.Printf("y=%v, z=%v\n", y, z)
+       }
+}
diff --git a/test/abi/leaf2.go b/test/abi/leaf2.go
new file mode 100644 (file)
index 0000000..d2018d5
--- /dev/null
@@ -0,0 +1,43 @@
+// run
+
+//go:build !wasm
+// +build !wasm
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// wasm is excluded because the compiler chatter about register abi pragma ends up
+// on stdout, and causes the expected output to not match.
+
+package main
+
+import "fmt"
+
+type i4 struct {
+       a, b, c, d int
+}
+
+//go:registerparams
+//go:noinline
+func F(x i4) i4 {
+       ab := x.a + x.b
+       bc := x.b + x.c
+       cd := x.c + x.d
+       ad := x.a + x.d
+       ba := x.a - x.b
+       cb := x.b - x.c
+       dc := x.c - x.d
+       da := x.a - x.d
+
+       return i4{ab*bc + da, cd*ad + cb, ba*cb + ad, dc*da + bc}
+}
+
+func main() {
+       x := i4{1, 2, 3, 4}
+       y := x
+       z := F(x)
+       if (i4{12, 34, 6, 8}) != z {
+               fmt.Printf("y=%v, z=%v\n", y, z)
+       }
+}
index 9ecae9833e2c8444766741c0caecaceaa9318717..3dcd3e327a7602bf8766a5029db6cc5178008dc4 100644 (file)
@@ -14,7 +14,7 @@ import (
 )
 
 type toobig struct {
-       a,b,c string
+       a, b, c string
 }
 
 //go:registerparams
@@ -29,8 +29,8 @@ type AnInterface interface {
 
 //go:registerparams
 //go:noinline
-func I(a,b,c string) toobig {
-       return toobig{a,b,c}
+func I(a, b, c string) toobig {
+       return toobig{a, b, c}
 }
 
 // AnIid prevents the compiler from figuring out what the interface really is.
@@ -40,12 +40,13 @@ func AnIid(x AnInterface) AnInterface {
 }
 
 var tmp toobig
+
 func main() {
        x := I("Ahoy", "1,", "2")
        y := I("3", "there,", "4")
        z := I("5", "6,", "Matey")
-       tmp = x.MagicMethodNameForTestingRegisterABI(y,z)
+       tmp = x.MagicMethodNameForTestingRegisterABI(y, z)
        fmt.Println(tmp.a, tmp.b, tmp.c)
-       tmp = AnIid(&x).MagicMethodNameForTestingRegisterABI(y,z)
+       tmp = AnIid(&x).MagicMethodNameForTestingRegisterABI(y, z)
        fmt.Println(tmp.a, tmp.b, tmp.c)
 }
diff --git a/test/abi/spills3.go b/test/abi/spills3.go
new file mode 100644 (file)
index 0000000..2478284
--- /dev/null
@@ -0,0 +1,48 @@
+// run
+
+//go:build !wasm
+// +build !wasm
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// wasm is excluded because the compiler chatter about register abi pragma ends up
+// on stdout, and causes the expected output to not match.
+
+package main
+
+import "fmt"
+
+type i4 struct {
+       a, b, c, d int
+}
+
+//go:noinline
+func spills(px *i4) {
+}
+
+//go:registerparams
+//go:noinline
+func F(x i4) i4 {
+       ab := x.a + x.b
+       bc := x.b + x.c
+       cd := x.c + x.d
+       ad := x.a + x.d
+       ba := x.a - x.b
+       cb := x.b - x.c
+       dc := x.c - x.d
+       da := x.a - x.d
+       i := i4{ab*bc + da, cd*ad + cb, ba*cb + ad, dc*da + bc}
+       spills(&i)
+       return i
+}
+
+func main() {
+       x := i4{1, 2, 3, 4}
+       y := x
+       z := F(x)
+       if z != (i4{12, 34, 6, 8}) {
+               fmt.Printf("y=%v, z=%v\n", y, z)
+       }
+}
diff --git a/test/abi/spills4.go b/test/abi/spills4.go
new file mode 100644 (file)
index 0000000..205f5a6
--- /dev/null
@@ -0,0 +1,44 @@
+// run
+
+//go:build !wasm
+// +build !wasm
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// wasm is excluded because the compiler chatter about register abi pragma ends up
+// on stdout, and causes the expected output to not match.
+
+package main
+
+import "fmt"
+
+type i5f5 struct {
+       a, b          int16
+       c, d, e       int32
+       r, s, t, u, v float32
+}
+
+//go:noinline
+func spills(_ *float32) {
+
+}
+
+//go:registerparams
+//go:noinline
+func F(x i5f5) i5f5 {
+       y := x.v
+       spills(&y)
+       x.r = y
+       return x
+}
+
+func main() {
+       x := i5f5{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+       y := x
+       z := F(x)
+       if (i5f5{1, 2, 3, 4, 5, 10, 7, 8, 9, 10}) != z {
+               fmt.Printf("y=%v, z=%v\n", y, z)
+       }
+}