From 0ec2c4abbad7d678ebc4afc4c69af7d952fc3404 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 10 Mar 2021 20:54:11 -0500 Subject: cmd/compile: (fixed) spill output parameters passed in registers as autos Repair of CL 300749. 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. Updates #40724. Change-Id: Ifeb5fee60f60e7c7b58ee0457f58a3265d6cf3f6 Reviewed-on: https://go-review.googlesource.com/c/go/+/302071 Trust: David Chase Reviewed-by: Cherry Zhang --- src/cmd/compile/internal/abi/abiutils.go | 31 ++++++++-- src/cmd/compile/internal/dwarfgen/dwarf.go | 10 ++- src/cmd/compile/internal/gc/compile.go | 2 +- src/cmd/compile/internal/ir/name.go | 75 ++++++++++++----------- src/cmd/compile/internal/liveness/plive.go | 19 ++++-- src/cmd/compile/internal/ssa/op.go | 10 +++ src/cmd/compile/internal/ssagen/pgen.go | 18 ++++-- src/cmd/compile/internal/ssagen/ssa.go | 30 +++++---- src/cmd/compile/internal/test/abiutilsaux_test.go | 2 +- src/cmd/compile/internal/types/size.go | 12 +--- src/cmd/compile/internal/types/type.go | 4 +- test/abi/fibish2.go | 4 +- test/abi/leaf.go | 36 +++++++++++ test/abi/leaf2.go | 43 +++++++++++++ test/abi/methods.go | 11 ++-- test/abi/spills3.go | 48 +++++++++++++++ test/abi/spills4.go | 44 +++++++++++++ 17 files changed, 309 insertions(+), 90 deletions(-) create mode 100644 test/abi/leaf.go create mode 100644 test/abi/leaf2.go create mode 100644 test/abi/spills3.go create mode 100644 test/abi/spills4.go diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go index ecde34313a..7573c13158 100644 --- a/src/cmd/compile/internal/abi/abiutils.go +++ b/src/cmd/compile/internal/abi/abiutils.go @@ -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 - a.config.LocalsOffset() +} + // 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) + } } } diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index 70168cffeb..53752097ed 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go @@ -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) } diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go index 2d7a74a403..83cfceb2c8 100644 --- a/src/cmd/compile/internal/gc/compile.go +++ b/src/cmd/compile/internal/gc/compile.go @@ -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 } diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go index 16c30324e5..5738aa1f3f 100644 --- a/src/cmd/compile/internal/ir/name.go +++ b/src/cmd/compile/internal/ir/name.go @@ -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 { diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go index 48a26cf66a..f3fbb8b9b1 100644 --- a/src/cmd/compile/internal/liveness/plive.go +++ b/src/cmd/compile/internal/liveness/plive.go @@ -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 diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go index 084098fb64..fe9ba0e156 100644 --- a/src/cmd/compile/internal/ssa/op.go +++ b/src/cmd/compile/internal/ssa/op.go @@ -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(), +// 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)) diff --git a/src/cmd/compile/internal/ssagen/pgen.go b/src/cmd/compile/internal/ssagen/pgen.go index 8fa5980dab..0088f10fa8 100644 --- a/src/cmd/compile/internal/ssagen/pgen.go +++ b/src/cmd/compile/internal/ssagen/pgen.go @@ -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) } diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 0029558963..6a3c0d28cb 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -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 } } diff --git a/src/cmd/compile/internal/test/abiutilsaux_test.go b/src/cmd/compile/internal/test/abiutilsaux_test.go index 7eb273273d..b945633133 100644 --- a/src/cmd/compile/internal/test/abiutilsaux_test.go +++ b/src/cmd/compile/internal/test/abiutilsaux_test.go @@ -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. diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go index ef23cdf5fe..a75429f0ab 100644 --- a/src/cmd/compile/internal/types/size.go +++ b/src/cmd/compile/internal/types/size.go @@ -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 { diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index ffaf755345..7bf63764b8 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -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 { diff --git a/test/abi/fibish2.go b/test/abi/fibish2.go index 14f3f9ada7..388aabc8b0 100644 --- a/test/abi/fibish2.go +++ b/test/abi/fibish2.go @@ -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 index 0000000000..f893f5dddb --- /dev/null +++ b/test/abi/leaf.go @@ -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 index 0000000000..d2018d5313 --- /dev/null +++ b/test/abi/leaf2.go @@ -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) + } +} diff --git a/test/abi/methods.go b/test/abi/methods.go index 9ecae9833e..3dcd3e327a 100644 --- a/test/abi/methods.go +++ b/test/abi/methods.go @@ -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 index 0000000000..247828437b --- /dev/null +++ b/test/abi/spills3.go @@ -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 index 0000000000..205f5a64c0 --- /dev/null +++ b/test/abi/spills4.go @@ -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) + } +} -- cgit v1.2.3-54-g00ecf