]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: use abiutils for all rcvr/in/out frame offsets.
authorDavid Chase <drchase@google.com>
Tue, 9 Feb 2021 20:14:43 +0000 (15:14 -0500)
committerDavid Chase <drchase@google.com>
Wed, 3 Mar 2021 01:45:33 +0000 (01:45 +0000)
types thought it knew how to do this, but that's a lie, because types
doesn't know what the ABI is.

includes extra checking to help prevent things from accidentally working
if they need to be changed but aren't.

For #40724.

Change-Id: I166cd948f262344b7bebde6a2c25e7a7f878bbfb
Reviewed-on: https://go-review.googlesource.com/c/go/+/293393
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>
src/cmd/compile/internal/abi/abiutils.go
src/cmd/compile/internal/ir/expr.go
src/cmd/compile/internal/ssa/expand_calls.go
src/cmd/compile/internal/ssa/op.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/typecheck/typecheck.go
src/cmd/compile/internal/types/size.go
src/cmd/compile/internal/types/type.go
src/cmd/compile/internal/walk/assign.go

index a5c85a89fb9a5b1d23821913eb5a65891d4d309e..8549c0325dba9f56c60b647e0ec2e8eb73e0ed55 100644 (file)
@@ -151,6 +151,13 @@ func (a *ABIConfig) Copy() *ABIConfig {
        return &b
 }
 
+// LocalsOffset returns the architecture-dependent offset from SP for args and results.
+// In theory this is only used for debugging; it ought to already be incorporated into
+// results from the ABI-related methods
+func (a *ABIConfig) LocalsOffset() int64 {
+       return a.offsetForLocals
+}
+
 // NumParamRegs returns the number of parameter registers used for a given type,
 // without regard for the number available.
 func (a *ABIConfig) NumParamRegs(t *types.Type) int {
@@ -237,23 +244,22 @@ func (config *ABIConfig) ABIAnalyzeTypes(rcvr *types.Type, ins, outs []*types.Ty
        return result
 }
 
-// ABIAnalyze takes a function type 't' and an ABI rules description
+// ABIAnalyzeFuncType takes a function type 'ft' and an ABI rules description
 // 'config' and analyzes the function to determine how its parameters
 // and results will be passed (in registers or on the stack), returning
 // an ABIParamResultInfo object that holds the results of the analysis.
-func (config *ABIConfig) ABIAnalyze(t *types.Type) *ABIParamResultInfo {
+func (config *ABIConfig) ABIAnalyzeFuncType(ft *types.Func) *ABIParamResultInfo {
        setup()
        s := assignState{
                stackOffset: config.offsetForLocals,
                rTotal:      config.regAmounts,
        }
        result := &ABIParamResultInfo{config: config}
-       ft := t.FuncType()
-       result.preAllocateParams(t.NumRecvs() != 0, ft.Params.NumFields(), ft.Results.NumFields())
+       result.preAllocateParams(ft.Receiver != nil, ft.Params.NumFields(), ft.Results.NumFields())
 
        // Receiver
        // TODO(register args) ? seems like "struct" and "fields" is not right anymore for describing function parameters
-       if t.NumRecvs() != 0 {
+       if ft.Receiver != nil && ft.Receiver.NumFields() != 0 {
                r := ft.Receiver.FieldSlice()[0]
                result.inparams = append(result.inparams,
                        s.assignParamOrReturn(r.Type, r.Nname, false))
@@ -279,7 +285,14 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type) *ABIParamResultInfo {
        result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
        result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
        result.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
+       return result
+}
 
+// 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 {
+       ft := t.FuncType()
+       result := config.ABIAnalyzeFuncType(ft)
        // Fill in the frame offsets for receiver, inputs, results
        k := 0
        if t.NumRecvs() != 0 {
@@ -296,13 +309,11 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type) *ABIParamResultInfo {
 }
 
 func (config *ABIConfig) updateOffset(result *ABIParamResultInfo, f *types.Field, a ABIParamAssignment, isReturn 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 {
-               // TODO in next CL, assign
-               if f.Offset+config.offsetForLocals != a.FrameOffset(result) {
-                       if config.regAmounts.intRegs == 0 && config.regAmounts.floatRegs == 0 {
-                               panic(fmt.Errorf("Expected node offset %d != abi offset %d", f.Offset, a.FrameOffset(result)))
-                       }
-               }
+               // The type frame offset DOES NOT show effects of minimum frame size.
+               // Getting this wrong breaks stackmaps, see liveness/plive.go:WriteFuncMap and typebits/typebits.go:Set
+               f.Offset = a.FrameOffset(result)-config.LocalsOffset()
        }
 }
 
index d68bcfe60c45d1ddf055dcefc9e7abef7fe13f1d..65ed3cff667b60209d0e461446c1ddf1f152bfe4 100644 (file)
@@ -447,14 +447,14 @@ func (n *ParenExpr) SetOTYPE(t *types.Type) {
        t.SetNod(n)
 }
 
-// A ResultExpr represents a direct access to a result slot on the stack frame.
+// A ResultExpr represents a direct access to a result.
 type ResultExpr struct {
        miniExpr
-       Offset int64
+       Index int64 // index of the result expr.
 }
 
-func NewResultExpr(pos src.XPos, typ *types.Type, offset int64) *ResultExpr {
-       n := &ResultExpr{Offset: offset}
+func NewResultExpr(pos src.XPos, typ *types.Type, index int64) *ResultExpr {
+       n := &ResultExpr{Index: index}
        n.pos = pos
        n.op = ORESULT
        n.typ = typ
index 292b0b41ce9dfbb41457e7da557208b3b7194d34..741d59258b1d624a255b67a339324ff77b5f9f93 100644 (file)
@@ -32,6 +32,10 @@ func isBlockMultiValueExit(b *Block) bool {
        return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult
 }
 
+func badVal(s string, v *Value) error {
+       return fmt.Errorf("%s %s", s, v.LongString())
+}
+
 // removeTrivialWrapperTypes unwraps layers of
 // struct { singleField SomeType } and [1]SomeType
 // until a non-wrapper type is reached.  This is useful
@@ -231,6 +235,9 @@ func (x *expandState) splitSlots(ls []LocalSlot, sfx string, offset int64, ty *t
 
 // prAssignForArg returns the ABIParamAssignment for v, assumed to be an OpArg.
 func (x *expandState) prAssignForArg(v *Value) abi.ABIParamAssignment {
+       if v.Op != OpArg {
+               panic(badVal("Wanted OpArg, instead saw", v))
+       }
        name := v.Aux.(*ir.Name)
        fPri := x.f.OwnAux.abiInfo
        for _, a := range fPri.InParams() {
@@ -275,9 +282,6 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
        }
        switch selector.Op {
        case OpArg:
-               paramAssignment := x.prAssignForArg(selector)
-               _ = paramAssignment
-               // TODO(register args)
                if !x.isAlreadyExpandedAggregateType(selector.Type) {
                        if leafType == selector.Type { // OpIData leads us here, sometimes.
                                leaf.copyOf(selector)
@@ -364,7 +368,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
                                // StaticCall selector will address last element of Result.
                                // TODO do this for all the other call types eventually.
                                if aux.abiInfo == nil {
-                                       panic(fmt.Errorf("aux.abiInfo nil for call %s", call.LongString()))
+                                       panic(badVal("aux.abiInfo nil for call", call))
                                }
                                if existing := x.memForCall[call.ID]; existing == nil {
                                        selector.AuxInt = int64(aux.abiInfo.OutRegistersUsed())
@@ -566,9 +570,6 @@ func (x *expandState) decomposeArgOrLoad(pos src.XPos, b *Block, base, source, m
 // pos and b locate the store instruction, base is the base of the store target, source is the "base" of the value input,
 // mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
 func storeOneArg(x *expandState, pos src.XPos, b *Block, base, source, mem *Value, t *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
-       paramAssignment := x.prAssignForArg(source)
-       _ = paramAssignment
-       // TODO(register args)
        w := x.common[selKey{source, offArg, t.Width, t}]
        if w == nil {
                w = source.Block.NewValue0IA(source.Pos, OpArg, t, offArg, source.Aux)
@@ -1198,10 +1199,24 @@ func expandCalls(f *Func) {
 
        deleteNamedVals(f, toDelete)
 
-       // Step 4: rewrite the calls themselves, correcting the type
+       // Step 4: rewrite the calls themselves, correcting the type.
        for _, b := range f.Blocks {
                for _, v := range b.Values {
                        switch v.Op {
+                       case OpArg:
+                               pa := x.prAssignForArg(v)
+                               switch len(pa.Registers) {
+                               case 0:
+                                       frameOff := v.Aux.(*ir.Name).FrameOffset()
+                                       if pa.Offset() != int32(frameOff+x.f.ABISelf.LocalsOffset()) {
+                                               panic(fmt.Errorf("Parameter assignment %d and OpArg.Aux frameOffset %d disagree, op=%s\n",
+                                                       pa.Offset(), frameOff, v.LongString()))
+                                       }
+                               case 1:
+                               default:
+                                       panic(badVal("Saw unexpeanded OpArg", v))
+                               }
+
                        case OpStaticLECall:
                                v.Op = OpStaticCall
                                // TODO need to insert all the register types.
index 6d2ca962939e2c97108586f338075eb750001caa..506c745f7c4a0e45eaa66e61c8978c6ef2f09a1e 100644 (file)
@@ -86,63 +86,39 @@ type AuxCall struct {
        abiInfo *abi.ABIParamResultInfo // TODO remove fields above redundant with this information.
 }
 
-// ResultForOffsetAndType returns the index of a t-typed result at *A* particular offset among the results.
-// An arbitrary number of zero-width-typed results may reside at the same offset with a single not-zero-width
-// typed result, but the ones with the same type are all indistinguishable so it doesn't matter "which one"
-// is obtained.
-// This does not include the mem result for the call opcode.
-func (a *AuxCall) ResultForOffsetAndType(offset int64, t *types.Type) int64 {
-       which := int64(-1)
-       for i := int64(0); i < a.NResults(); i++ { // note aux NResults does not include mem result.
-               if a.OffsetOfResult(i) == offset && a.TypeOfResult(i) == t {
-                       which = i
-                       break
-               }
-       }
-       return which
-}
-
 // OffsetOfResult returns the SP offset of result which (indexed 0, 1, etc).
 func (a *AuxCall) OffsetOfResult(which int64) int64 {
-       o := int64(a.results[which].Offset)
        n := int64(a.abiInfo.OutParam(int(which)).Offset())
-       if o != n {
-               panic(fmt.Errorf("Result old=%d, new=%d, auxcall=%s, oparams=%v", o, n, a, a.abiInfo.OutParams()))
-       }
-       return int64(a.abiInfo.OutParam(int(which)).Offset())
+       return n
 }
 
 // OffsetOfArg returns the SP offset of argument which (indexed 0, 1, etc).
 // If the call is to a method, the receiver is the first argument (i.e., index 0)
 func (a *AuxCall) OffsetOfArg(which int64) int64 {
-       o := int64(a.args[which].Offset)
        n := int64(a.abiInfo.InParam(int(which)).Offset())
-       if o != n {
-               panic(fmt.Errorf("Arg old=%d, new=%d, auxcall=%s, iparams=%v", o, n, a, a.abiInfo.InParams()))
-       }
-       return int64(a.abiInfo.InParam(int(which)).Offset())
+       return n
 }
 
 // RegsOfResult returns the register(s) used for result which (indexed 0, 1, etc).
 func (a *AuxCall) RegsOfResult(which int64) []abi.RegIndex {
-       return a.results[which].Reg
+       return a.abiInfo.OutParam(int(which)).Registers
 }
 
 // RegsOfArg returns the register(s) used for argument which (indexed 0, 1, etc).
 // If the call is to a method, the receiver is the first argument (i.e., index 0)
 func (a *AuxCall) RegsOfArg(which int64) []abi.RegIndex {
-       return a.args[which].Reg
+       return a.abiInfo.InParam(int(which)).Registers
 }
 
 // TypeOfResult returns the type of result which (indexed 0, 1, etc).
 func (a *AuxCall) TypeOfResult(which int64) *types.Type {
-       return a.results[which].Type
+       return a.abiInfo.OutParam(int(which)).Type
 }
 
 // TypeOfArg returns the type of argument which (indexed 0, 1, etc).
 // If the call is to a method, the receiver is the first argument (i.e., index 0)
 func (a *AuxCall) TypeOfArg(which int64) *types.Type {
-       return a.args[which].Type
+       return a.abiInfo.InParam(int(which)).Type
 }
 
 // SizeOfResult returns the size of result which (indexed 0, 1, etc).
@@ -158,7 +134,7 @@ func (a *AuxCall) SizeOfArg(which int64) int64 {
 
 // NResults returns the number of results
 func (a *AuxCall) NResults() int64 {
-       return int64(len(a.results))
+       return int64(len(a.abiInfo.OutParams()))
 }
 
 // LateExpansionResultType returns the result type (including trailing mem)
@@ -174,7 +150,7 @@ func (a *AuxCall) LateExpansionResultType() *types.Type {
 
 // NArgs returns the number of arguments (including receiver, if there is one).
 func (a *AuxCall) NArgs() int64 {
-       return int64(len(a.args))
+       return int64(len(a.abiInfo.InParams()))
 }
 
 // String returns
index 938c1e8b6245a5560dfb050486507c7c1ebf48c9..210150d8727c5d43d130d12f925aaaee1c546828 100644 (file)
@@ -301,7 +301,7 @@ func (s *state) emitOpenDeferInfo() {
        var maxargsize int64
        for i := len(s.openDefers) - 1; i >= 0; i-- {
                r := s.openDefers[i]
-               argsize := r.n.X.Type().ArgWidth()
+               argsize := r.n.X.Type().ArgWidth() // TODO register args: but maybe use of abi0 will make this easy
                if argsize > maxargsize {
                        maxargsize = argsize
                }
@@ -324,19 +324,30 @@ func (s *state) emitOpenDeferInfo() {
                }
                off = dvarint(x, off, int64(numArgs))
                if r.rcvrNode != nil {
-                       off = dvarint(x, off, -r.rcvrNode.FrameOffset())
+                       off = dvarint(x, off, -okOffset(r.rcvrNode.FrameOffset()))
                        off = dvarint(x, off, s.config.PtrSize)
-                       off = dvarint(x, off, 0)
+                       off = dvarint(x, off, 0) // This is okay because defer records use ABI0 (for now)
                }
+
+               // TODO(register args) assume abi0 for this?
+               ab := s.f.ABI0
+               pri := ab.ABIAnalyzeFuncType(r.n.X.Type().FuncType())
                for j, arg := range r.argNodes {
                        f := getParam(r.n, j)
-                       off = dvarint(x, off, -arg.FrameOffset())
+                       off = dvarint(x, off, -okOffset(arg.FrameOffset()))
                        off = dvarint(x, off, f.Type.Size())
-                       off = dvarint(x, off, f.Offset)
+                       off = dvarint(x, off, okOffset(pri.InParam(j).FrameOffset(pri))-ab.LocalsOffset()) // defer does not want the fixed frame adjustment
                }
        }
 }
 
+func okOffset(offset int64) int64 {
+       if offset >= types.BOGUS_FUNARG_OFFSET {
+               panic(fmt.Errorf("Bogus offset %d", offset))
+       }
+       return offset
+}
+
 // buildssa builds an SSA function for fn.
 // worker indicates which of the backend workers is doing the processing.
 func buildssa(fn *ir.Func, worker int) *ssa.Func {
@@ -528,7 +539,13 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
        // Populate SSAable arguments.
        for _, n := range fn.Dcl {
                if n.Class == ir.PPARAM && s.canSSA(n) {
-                       v := s.newValue0A(ssa.OpArg, n.Type(), n)
+                       var v *ssa.Value
+                       if n.Sym().Name == ".fp" {
+                               // Race-detector's get-caller-pc incantation is NOT a real Arg.
+                               v = s.newValue0(ssa.OpGetCallerPC, n.Type())
+                       } else {
+                               v = s.newValue0A(ssa.OpArg, n.Type(), n)
+                       }
                        s.vars[n] = v
                        s.addNamedValue(n, v) // This helps with debugging information, not needed for compilation itself.
                }
@@ -2917,15 +2934,11 @@ func (s *state) expr(n ir.Node) *ssa.Value {
        case ir.ORESULT:
                n := n.(*ir.ResultExpr)
                if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
-                       // Do the old thing
-                       addr := s.constOffPtrSP(types.NewPtr(n.Type()), n.Offset)
-                       return s.rawLoad(n.Type(), addr)
+                       panic("Expected to see a previous call")
                }
-               which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffsetAndType(n.Offset, n.Type())
+               which := n.Index
                if which == -1 {
-                       // Do the old thing // TODO: Panic instead.
-                       addr := s.constOffPtrSP(types.NewPtr(n.Type()), n.Offset)
-                       return s.rawLoad(n.Type(), addr)
+                       panic(fmt.Errorf("ORESULT %v does not match call %s", n, s.prevCall))
                }
                if TypeOK(n.Type()) {
                        return s.newValue1I(ssa.OpSelectN, n.Type(), which, s.prevCall)
@@ -4889,7 +4902,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
 
                // Then, store all the arguments of the defer call.
                ft := fn.Type()
-               off := t.FieldOff(12)
+               off := t.FieldOff(12) // TODO register args: be sure this isn't a hardcoded param stack offset.
                args := n.Args
 
                // Set receiver (for interface calls). Always a pointer.
@@ -5131,15 +5144,7 @@ func (s *state) addr(n ir.Node) *ssa.Value {
        case ir.ORESULT:
                // load return from callee
                n := n.(*ir.ResultExpr)
-               if s.prevCall == nil || s.prevCall.Op != ssa.OpStaticLECall && s.prevCall.Op != ssa.OpInterLECall && s.prevCall.Op != ssa.OpClosureLECall {
-                       return s.constOffPtrSP(t, n.Offset)
-               }
-               which := s.prevCall.Aux.(*ssa.AuxCall).ResultForOffsetAndType(n.Offset, n.Type())
-               if which == -1 {
-                       // Do the old thing // TODO: Panic instead.
-                       return s.constOffPtrSP(t, n.Offset)
-               }
-               x := s.newValue1I(ssa.OpSelectNAddr, t, which, s.prevCall)
+               x := s.newValue1I(ssa.OpSelectNAddr, t, n.Index, s.prevCall)
                return x
 
        case ir.OINDEX:
index cb434578dd37e6b758aa7d9e100a3968b3333319..5a3446b358f02adb5cf17fa23bdef42def40b1d0 100644 (file)
@@ -1175,7 +1175,7 @@ func lookdot(n *ir.SelectorExpr, t *types.Type, dostrcmp int) *types.Field {
                        base.Errorf("%v is both field and method", n.Sel)
                }
                if f1.Offset == types.BADWIDTH {
-                       base.Fatalf("lookdot badwidth %v %p", f1, f1)
+                       base.Fatalf("lookdot badwidth t=%v, f1=%v@%p", t, f1, f1)
                }
                n.Selection = f1
                n.SetType(f1.Type)
index 799cf3a1f6a2e9d967d5b4d5db9265e476bb733f..4c7378560cc532a1fb7998b787632fa48b20f8c0 100644 (file)
@@ -141,6 +141,8 @@ func expandiface(t *Type) {
 }
 
 func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
+       // flag is 0 (receiver), 1 (actual struct), or RegSize (in/out parameters)
+       isStruct := flag == 1
        starto := o
        maxalign := int32(flag)
        if maxalign < 1 {
@@ -161,7 +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))
                }
-               f.Offset = o
+               if isStruct { // For receiver/args/results, depends on ABI
+                       f.Offset = o
+               }
                if f.Nname != nil {
                        // addrescapes has similar code to update these offsets.
                        // Usually addrescapes runs after calcStructOffset,
index 9fb6d689941fd60679c1b4307d38e75c17ac1a69..b0516a8259f72d9b902736b761db5eb774f45b03 100644 (file)
@@ -411,7 +411,8 @@ type Field struct {
        Nname Object
 
        // Offset in bytes of this field or method within its enclosing struct
-       // or interface Type.
+       // or interface Type.  Exception: if field is function receiver, arg or
+       // result, then this is BOGUS_FUNARG_OFFSET; types does not know the Abi.
        Offset int64
 }
 
@@ -1719,6 +1720,14 @@ func NewTypeParam(pkg *Pkg, constraint *Type) *Type {
        return t
 }
 
+const BOGUS_FUNARG_OFFSET = 1000000000
+
+func unzeroFieldOffsets(f []*Field) {
+       for i := range f {
+               f[i].Offset = BOGUS_FUNARG_OFFSET // This will cause an explosion if it is not corrected
+       }
+}
+
 // NewSignature returns a new function type for the given receiver,
 // parametes, results, and type parameters, any of which may be nil.
 func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Type {
@@ -1739,6 +1748,11 @@ func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Typ
                return s
        }
 
+       if recv != nil {
+               recv.Offset = BOGUS_FUNARG_OFFSET
+       }
+       unzeroFieldOffsets(params)
+       unzeroFieldOffsets(results)
        ft.Receiver = funargs(recvs, FunargRcvr)
        ft.TParams = funargs(tparams, FunargTparams)
        ft.Params = funargs(params, FunargParams)
index 230b544148ee021f659f0d58b227cafe3c6a6078..44622c741d3ac52eafb9b9d60308178f31fd37d2 100644 (file)
@@ -270,7 +270,7 @@ func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node {
                }
 
                res := ir.NewResultExpr(base.Pos, nil, types.BADWIDTH)
-               res.Offset = base.Ctxt.FixedFrameSize() + r.Offset
+               res.Index = int64(i)
                res.SetType(r.Type)
                res.SetTypecheck(1)