]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: change StaticCall to return a "Results"
authorDavid Chase <drchase@google.com>
Thu, 4 Feb 2021 21:42:35 +0000 (16:42 -0500)
committerDavid Chase <drchase@google.com>
Fri, 26 Feb 2021 02:52:33 +0000 (02:52 +0000)
StaticLECall (multiple value in +mem, multiple value result +mem) ->
StaticCall (multiple ergister value in +mem,
   multiple register-sized-value result +mem) ->
ARCH CallStatic (multiple ergister value in +mem,
   multiple register-sized-value result +mem)

But the architecture-dependent stuff is indifferent to whether
it is mem->mem or (mem)->(mem) until Prog generation.

Deal with OpSelectN -> Prog in ssagen/ssa.go, others, as they
appear.

For #40724.

Change-Id: I1d0436f6371054f1881862641d8e7e418e4a6a16
Reviewed-on: https://go-review.googlesource.com/c/go/+/293391
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>
18 files changed:
src/cmd/compile/internal/abi/abiutils.go
src/cmd/compile/internal/ssa/cse.go
src/cmd/compile/internal/ssa/expand_calls.go
src/cmd/compile/internal/ssa/gen/generic.rules
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/location.go
src/cmd/compile/internal/ssa/lower.go
src/cmd/compile/internal/ssa/op.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/ssa/rewrite.go
src/cmd/compile/internal/ssa/rewritegeneric.go
src/cmd/compile/internal/ssa/schedule.go
src/cmd/compile/internal/ssa/tighten.go
src/cmd/compile/internal/ssa/tuple.go
src/cmd/compile/internal/ssa/writebarrier.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/types/type.go

index 4bd27efb59d033872495ae7c769910f2d140c674..b43d95e976acfcf995c4acda1279e27390b6a1e9 100644 (file)
@@ -27,6 +27,8 @@ type ABIParamResultInfo struct {
        outparams         []ABIParamAssignment
        offsetToSpillArea int64
        spillAreaSize     int64
+       inRegistersUsed   int
+       outRegistersUsed  int
        config            *ABIConfig // to enable String() method
 }
 
@@ -42,6 +44,14 @@ func (a *ABIParamResultInfo) OutParams() []ABIParamAssignment {
        return a.outparams
 }
 
+func (a *ABIParamResultInfo) InRegistersUsed() int {
+       return a.inRegistersUsed
+}
+
+func (a *ABIParamResultInfo) OutRegistersUsed() int {
+       return a.outRegistersUsed
+}
+
 func (a *ABIParamResultInfo) InParam(i int) ABIParamAssignment {
        return a.inparams[i]
 }
@@ -164,6 +174,55 @@ func (a *ABIConfig) NumParamRegs(t *types.Type) int {
        return n
 }
 
+// preAllocateParams gets the slice sizes right for inputs and outputs.
+func (a *ABIParamResultInfo) preAllocateParams(hasRcvr bool, nIns, nOuts int) {
+       if hasRcvr {
+               nIns++
+       }
+       a.inparams = make([]ABIParamAssignment, 0, nIns)
+       a.outparams = make([]ABIParamAssignment, 0, nOuts)
+}
+
+// ABIAnalyzeTypes takes an optional receiver type, arrays of ins and outs, and returns an ABIParamResultInfo,
+// based on the given configuration.  This is the same result computed by config.ABIAnalyze applied to the
+// corresponding method/function type, except that all the embedded parameter names are nil.
+// This is intended for use by ssagen/ssa.go:(*state).rtcall, for runtime functions that lack a parsed function type.
+func (config *ABIConfig) ABIAnalyzeTypes(rcvr *types.Type, ins, outs []*types.Type) *ABIParamResultInfo {
+       setup()
+       s := assignState{
+               rTotal: config.regAmounts,
+       }
+       result := &ABIParamResultInfo{config: config}
+       result.preAllocateParams(rcvr != nil, len(ins), len(outs))
+
+       // Receiver
+       if rcvr != nil {
+               result.inparams = append(result.inparams,
+                       s.assignParamOrReturn(rcvr, nil, false))
+       }
+
+       // Inputs
+       for _, t := range ins {
+               result.inparams = append(result.inparams,
+                       s.assignParamOrReturn(t, nil, false))
+       }
+       s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
+       result.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
+
+       // Outputs
+       s.rUsed = RegAmounts{}
+       for _, t := range outs {
+               result.outparams = append(result.outparams, s.assignParamOrReturn(t, nil, true))
+       }
+       // The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
+       // TODO in theory could align offset only to minimum required by spilled data types.
+       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 takes a function type 't' 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
@@ -174,33 +233,37 @@ func (config *ABIConfig) ABIAnalyze(t *types.Type) *ABIParamResultInfo {
                rTotal: config.regAmounts,
        }
        result := &ABIParamResultInfo{config: config}
+       ft := t.FuncType()
+       result.preAllocateParams(t.NumRecvs() != 0, ft.Params.NumFields(), ft.Results.NumFields())
 
        // Receiver
-       ft := t.FuncType()
+       // TODO(register args) ? seems like "struct" and "fields" is not right anymore for describing function parameters
        if t.NumRecvs() != 0 {
-               rfsl := ft.Receiver.FieldSlice()
+               r := ft.Receiver.FieldSlice()[0]
                result.inparams = append(result.inparams,
-                       s.assignParamOrReturn(rfsl[0], false))
+                       s.assignParamOrReturn(r.Type, r.Nname, false))
        }
 
        // Inputs
        ifsl := ft.Params.FieldSlice()
        for _, f := range ifsl {
                result.inparams = append(result.inparams,
-                       s.assignParamOrReturn(f, false))
+                       s.assignParamOrReturn(f.Type, f.Nname, false))
        }
        s.stackOffset = types.Rnd(s.stackOffset, int64(types.RegSize))
+       result.inRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
 
        // Outputs
        s.rUsed = RegAmounts{}
        ofsl := ft.Results.FieldSlice()
        for _, f := range ofsl {
-               result.outparams = append(result.outparams, s.assignParamOrReturn(f, true))
+               result.outparams = append(result.outparams, s.assignParamOrReturn(f.Type, f.Nname, true))
        }
        // The spill area is at a register-aligned offset and its size is rounded up to a register alignment.
        // TODO in theory could align offset only to minimum required by spilled data types.
        result.offsetToSpillArea = alignTo(s.stackOffset, types.RegSize)
        result.spillAreaSize = alignTo(s.spillOffset, types.RegSize)
+       result.outRegistersUsed = s.rUsed.intRegs + s.rUsed.floatRegs
 
        return result
 }
@@ -460,17 +523,15 @@ func (state *assignState) regassign(pt *types.Type) bool {
 // of field f to determine whether it can be register assigned.
 // The result of the analysis is recorded in the result
 // ABIParamResultInfo held in 'state'.
-func (state *assignState) assignParamOrReturn(f *types.Field, isReturn bool) ABIParamAssignment {
-       // TODO(register args) ? seems like "struct" and "fields" is not right anymore for describing function parameters
-       pt := f.Type
+func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, isReturn bool) ABIParamAssignment {
        state.pUsed = RegAmounts{}
        if pt.Width == types.BADWIDTH {
                panic("should never happen")
        } else if pt.Width == 0 {
-               return state.stackAllocate(pt, f.Nname)
+               return state.stackAllocate(pt, n)
        } else if state.regassign(pt) {
-               return state.regAllocate(pt, f.Nname, isReturn)
+               return state.regAllocate(pt, n, isReturn)
        } else {
-               return state.stackAllocate(pt, f.Nname)
+               return state.stackAllocate(pt, n)
        }
 }
index f78527410c8dde5952b96661c2a15ef166e7afcc..ade5e0648e78d8ed992747e4865b49ffa7715e1e 100644 (file)
@@ -299,7 +299,7 @@ func cmpVal(v, w *Value, auxIDs auxmap) types.Cmp {
        // OpSelect is a pseudo-op. We need to be more aggressive
        // regarding CSE to keep multiple OpSelect's of the same
        // argument from existing.
-       if v.Op != OpSelect0 && v.Op != OpSelect1 {
+       if v.Op != OpSelect0 && v.Op != OpSelect1 && v.Op != OpSelectN {
                if tc := v.Type.Compare(w.Type); tc != types.CMPeq {
                        return tc
                }
index 85d6fda4279eecf0ecba21f877bd98fd6b81d72d..292b0b41ce9dfbb41457e7da557208b3b7194d34 100644 (file)
@@ -170,6 +170,7 @@ type expandState struct {
        sdom         SparseTree
        common       map[selKey]*Value
        offsets      map[offsetKey]*Value
+       memForCall   map[ID]*Value // For a call, need to know the unique selector that gets the mem.
 }
 
 // intPairTypes returns the pair of 32-bit int types needed to encode a 64-bit integer type on a target
@@ -280,7 +281,6 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
                if !x.isAlreadyExpandedAggregateType(selector.Type) {
                        if leafType == selector.Type { // OpIData leads us here, sometimes.
                                leaf.copyOf(selector)
-
                        } else {
                                x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
                        }
@@ -357,13 +357,43 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
                which := selector.AuxInt
                if which == aux.NResults() { // mem is after the results.
                        // rewrite v as a Copy of call -- the replacement call will produce a mem.
-                       leaf.copyOf(call)
+                       if call.Op == OpStaticLECall {
+                               if leaf != selector {
+                                       panic("Unexpected selector of memory")
+                               }
+                               // 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()))
+                               }
+                               if existing := x.memForCall[call.ID]; existing == nil {
+                                       selector.AuxInt = int64(aux.abiInfo.OutRegistersUsed())
+                                       x.memForCall[call.ID] = selector
+                               } else {
+                                       selector.copyOf(existing)
+                               }
+                       } else {
+                               leaf.copyOf(call)
+                       }
                } else {
                        leafType := removeTrivialWrapperTypes(leaf.Type)
                        if x.canSSAType(leafType) {
                                pt := types.NewPtr(leafType)
                                off := x.offsetFrom(x.sp, offset+aux.OffsetOfResult(which), pt)
                                // Any selection right out of the arg area/registers has to be same Block as call, use call as mem input.
+                               if call.Op == OpStaticLECall { // TODO this is temporary until all calls are register-able
+                                       // Create a "mem" for any loads that need to occur.
+                                       if mem := x.memForCall[call.ID]; mem != nil {
+                                               if mem.Block != call.Block {
+                                                       panic(fmt.Errorf("selector and call need to be in same block, selector=%s; call=%s", selector.LongString(), call.LongString()))
+                                               }
+                                               call = mem
+                                       } else {
+                                               mem = call.Block.NewValue1I(call.Pos.WithNotStmt(), OpSelectN, types.TypeMem, int64(aux.abiInfo.OutRegistersUsed()), call)
+                                               x.memForCall[call.ID] = mem
+                                               call = mem
+                                       }
+                               }
                                if leaf.Block == call.Block {
                                        leaf.reset(OpLoad)
                                        leaf.SetArgs2(off, call)
@@ -835,6 +865,7 @@ func expandCalls(f *Func) {
                sdom:         f.Sdom(),
                common:       make(map[selKey]*Value),
                offsets:      make(map[offsetKey]*Value),
+               memForCall:   make(map[ID]*Value),
        }
 
        // For 32-bit, need to deal with decomposition of 64-bit integers, which depends on endianness.
@@ -1173,7 +1204,8 @@ func expandCalls(f *Func) {
                        switch v.Op {
                        case OpStaticLECall:
                                v.Op = OpStaticCall
-                               v.Type = types.TypeMem
+                               // TODO need to insert all the register types.
+                               v.Type = types.NewResults([]*types.Type{types.TypeMem})
                        case OpClosureLECall:
                                v.Op = OpClosureCall
                                v.Type = types.TypeMem
index 1784923224d2b48d12ae0e1a4a961dcc147728fa..fab45243edae90439052215f0bd76d2e71fd1e66 100644 (file)
 
 (Sqrt (Const64F [c])) && !math.IsNaN(math.Sqrt(c)) => (Const64F [math.Sqrt(c)])
 
-// recognize runtime.newobject and don't Zero/Nilcheck it
-(Zero (Load (OffPtr [c] (SP)) mem) mem)
-       && mem.Op == OpStaticCall
-       && isSameCall(mem.Aux, "runtime.newobject")
-       && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
-       => mem
-(Store (Load (OffPtr [c] (SP)) mem) x mem)
-       && isConstZero(x)
-       && mem.Op == OpStaticCall
-       && isSameCall(mem.Aux, "runtime.newobject")
-       && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
-       => mem
-(Store (OffPtr (Load (OffPtr [c] (SP)) mem)) x mem)
-       && isConstZero(x)
-       && mem.Op == OpStaticCall
-       && isSameCall(mem.Aux, "runtime.newobject")
-       && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
-       => mem
-// nil checks just need to rewrite to something useless.
-// they will be deadcode eliminated soon afterwards.
-(NilCheck (Load (OffPtr [c] (SP)) (StaticCall {sym} _)) _)
-       && isSameCall(sym, "runtime.newobject")
-       && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
-       && warnRule(fe.Debug_checknil(), v, "removed nil check")
-       => (Invalid)
-(NilCheck (OffPtr (Load (OffPtr [c] (SP)) (StaticCall {sym} _))) _)
-       && isSameCall(sym, "runtime.newobject")
-       && c == config.ctxt.FixedFrameSize() + config.RegSize // offset of return value
-       && warnRule(fe.Debug_checknil(), v, "removed nil check")
-       => (Invalid)
-
 // for rewriting results of some late-expanded rewrites (below)
 (SelectN [0] (MakeResult a ___)) => a
 (SelectN [1] (MakeResult a b ___)) => b
        && isSameCall(call.Aux, "runtime.newobject")
        => mem
 
-(NilCheck (SelectN [0] call:(StaticLECall _ _)) (SelectN [1] call))
+(NilCheck (SelectN [0] call:(StaticLECall _ _)) _)
        && isSameCall(call.Aux, "runtime.newobject")
        && warnRule(fe.Debug_checknil(), v, "removed nil check")
        => (Invalid)
 
-(NilCheck (OffPtr (SelectN [0] call:(StaticLECall _ _))) (SelectN [1] call))
+(NilCheck (OffPtr (SelectN [0] call:(StaticLECall _ _))) _)
        && isSameCall(call.Aux, "runtime.newobject")
        && warnRule(fe.Debug_checknil(), v, "removed nil check")
        => (Invalid)
 (IsNonNil (Addr _)) => (ConstBool [true])
 (IsNonNil (LocalAddr _ _)) => (ConstBool [true])
 
+// TODO REGISTER ARGS this will need revision.
+// Because expand calls runs after prove, constants useful to this pattern may not appear
+// In the future both versions need to exist; the memory and register variants.
+
 // Inline small or disjoint runtime.memmove calls with constant length.
 // See the comment in op Move in genericOps.go for discussion of the type.
-(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store  _ src s3:(Store {t} _ dst mem))))
+(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store  _ src s3:(Store {t} _ dst mem)))))
        && sz >= 0
        && isSameCall(sym, "runtime.memmove")
        && t.IsPtr() // avoids TUINTPTR, see issue 30061
        && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
        && isInlinableMemmove(dst, src, int64(sz), config)
-       && clobber(s1, s2, s3)
+       && clobber(s1, s2, s3, call)
        => (Move {t.Elem()} [int64(sz)] dst src mem)
 
 // Inline small or disjoint runtime.memmove calls with constant length.
        && clobber(call)
        => (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
 
-// De-virtualize interface calls into static calls.
-// Note that (ITab (IMake)) doesn't get
-// rewritten until after the first opt pass,
-// so this rule should trigger reliably.
-(InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem) && devirt(v, auxCall, itab, off) != nil =>
-       (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem)
-
 // De-virtualize late-expanded interface calls into late-expanded static calls.
 // Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
 // so this rule should trigger reliably.
                                (Store {t5} (OffPtr <tt5> [o5] dst) d4
                                        (Zero {t1} [n] dst mem)))))
 
-// TODO this does not fire before call expansion; is that acceptable?
-(StaticCall {sym} x) && needRaceCleanup(sym, v) => x
+(SelectN [0] call:(StaticLECall {sym} a x)) && needRaceCleanup(sym, call) && clobber(call) => x
+(SelectN [0] call:(StaticLECall {sym} x)) && needRaceCleanup(sym, call) && clobber(call) => x
 
 // Collapse moving A -> B -> C into just A -> C.
 // Later passes (deadstore, elim unread auto) will remove the A -> B move, if possible.
index 23a2d74b14800608c4d9f4e6091d0c4f838fef14..043f445c163e564e77df384fabbea40f8063fe7d 100644 (file)
@@ -396,7 +396,7 @@ var genericOps = []opData{
        // to match StaticCall's 32 bit arg size limit.
        // TODO(drchase,josharian): could the arg size limit be bundled into the rules for CallOff?
        {name: "ClosureCall", argLength: 3, aux: "CallOff", call: true},    // arg0=code pointer, arg1=context ptr, arg2=memory.  auxint=arg size.  Returns memory.
-       {name: "StaticCall", argLength: 1, aux: "CallOff", call: true},     // call function aux.(*obj.LSym), arg0=memory.  auxint=arg size.  Returns memory.
+       {name: "StaticCall", argLength: -1, aux: "CallOff", call: true},    // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory.  auxint=arg size.  Returns Result of register results, plus memory.
        {name: "InterCall", argLength: 2, aux: "CallOff", call: true},      // interface call.  arg0=code pointer, arg1=memory, auxint=arg size.  Returns memory.
        {name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr,  arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
        {name: "StaticLECall", argLength: -1, aux: "CallOff", call: true},  // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
index 4cd0ac8d777b3468db9a45e0cbad6deb0987652c..af0a913d17e45a64f0e986a3dc93977ef3226ed3 100644 (file)
@@ -88,6 +88,20 @@ func (t LocPair) String() string {
        return fmt.Sprintf("<%s,%s>", n0, n1)
 }
 
+type LocResults []Location
+
+func (t LocResults) String() string {
+       s := "<"
+       a := ""
+       for _, r := range t {
+               a += s
+               s = ","
+               a += r.String()
+       }
+       a += ">"
+       return a
+}
+
 type ArgPair struct {
        reg *Register
        mem LocalSlot
index f332b2e028e72e96b78fd250cd8dc7c0bc920933..f6b2bf86a946e0c5757c4d0ff5dfbf7c38483e0b 100644 (file)
@@ -21,7 +21,7 @@ func checkLower(f *Func) {
                                continue // lowered
                        }
                        switch v.Op {
-                       case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpConvert, OpInlMark:
+                       case OpSP, OpSB, OpInitMem, OpArg, OpPhi, OpVarDef, OpVarKill, OpVarLive, OpKeepAlive, OpSelect0, OpSelect1, OpSelectN, OpConvert, OpInlMark:
                                continue // ok not to lower
                        case OpGetG:
                                if f.Config.hasGReg {
index 55e0602f2f55e3a780611f1c8ed50dca17c406a3..6949bdca31feb31a5fc1aed8291addfa07d458b5 100644 (file)
@@ -203,9 +203,19 @@ func (a *AuxCall) String() string {
        return fn + "}"
 }
 
+func ACParamsToTypes(ps []Param) (ts []*types.Type) {
+       for _, p := range ps {
+               ts = append(ts, p.Type)
+       }
+       return
+}
+
 // StaticAuxCall returns an AuxCall for a static call.
 func StaticAuxCall(sym *obj.LSym, args []Param, results []Param, paramResultInfo *abi.ABIParamResultInfo) *AuxCall {
        // TODO Create regInfo for AuxCall
+       if paramResultInfo == nil {
+               panic(fmt.Errorf("Nil paramResultInfo, sym=%v", sym))
+       }
        return &AuxCall{Fn: sym, args: args, results: results, abiInfo: paramResultInfo}
 }
 
index 10ea57b36bc7352b8813cd75f6a59e235b3bc2df..2d37ae43574e52374a732bbd6bf2a2fbb62325fa 100644 (file)
@@ -35435,7 +35435,7 @@ var opcodeTable = [...]opInfo{
        {
                name:    "StaticCall",
                auxType: auxCallOff,
-               argLen:  1,
+               argLen:  -1,
                call:    true,
                generic: true,
        },
index 8c25b1c81dc0c45bafd1d480b697171b7cd17340..99e101281b09d38b5e9bfe3e75c5416e175e0928 100644 (file)
@@ -761,6 +761,9 @@ func (s *regAllocState) advanceUses(v *Value) {
 // current instruction.
 func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
        u := s.values[v.ID].uses
+       if u == nil {
+               panic(fmt.Errorf("u is nil, v = %s, s.values[v.ID] = %v", v.LongString(), s.values[v.ID]))
+       }
        d := u.dist
        for u != nil && u.dist == d {
                u = u.next
@@ -1208,13 +1211,17 @@ func (s *regAllocState) regalloc(f *Func) {
                                s.sb = v.ID
                                continue
                        }
-                       if v.Op == OpSelect0 || v.Op == OpSelect1 {
+                       if v.Op == OpSelect0 || v.Op == OpSelect1 || v.Op == OpSelectN {
                                if s.values[v.ID].needReg {
-                                       var i = 0
-                                       if v.Op == OpSelect1 {
-                                               i = 1
+                                       if v.Op == OpSelectN {
+                                               s.assignReg(register(s.f.getHome(v.Args[0].ID).(LocResults)[int(v.AuxInt)].(*Register).num), v, v)
+                                       } else {
+                                               var i = 0
+                                               if v.Op == OpSelect1 {
+                                                       i = 1
+                                               }
+                                               s.assignReg(register(s.f.getHome(v.Args[0].ID).(LocPair)[i].(*Register).num), v, v)
                                        }
-                                       s.assignReg(register(s.f.getHome(v.Args[0].ID).(LocPair)[i].(*Register).num), v, v)
                                }
                                b.Values = append(b.Values, v)
                                s.advanceUses(v)
@@ -1767,6 +1774,9 @@ func (s *regAllocState) placeSpills() {
                // put the spill of v.  At the start "best" is the best place
                // we have found so far.
                // TODO: find a way to make this O(1) without arbitrary cutoffs.
+               if v == nil {
+                       panic(fmt.Errorf("nil v, s.orig[%d], vi = %v, spill = %s", i, vi, spill.LongString()))
+               }
                best := v.Block
                bestArg := v
                var bestDepth int16
index ac6278ab9df6a8e347b4caa2a1287f1f42822c8a..19b97a3ed1944ca6b0f12491148f26ca6ee28ad9 100644 (file)
@@ -793,7 +793,10 @@ func devirtLESym(v *Value, aux Aux, sym Sym, offset int64) *obj.LSym {
 
 func devirtLECall(v *Value, sym *obj.LSym) *Value {
        v.Op = OpStaticLECall
-       v.Aux.(*AuxCall).Fn = sym
+       auxcall := v.Aux.(*AuxCall)
+       auxcall.Fn = sym
+       // TODO(register args) this should not be necessary when fully transition to the new register ABI.
+       auxcall.abiInfo = v.Block.Func.ABIDefault.ABIAnalyzeTypes(nil, ACParamsToTypes(auxcall.args), ACParamsToTypes(auxcall.results))
        v.RemoveArg(0)
        return v
 }
@@ -1617,7 +1620,7 @@ func needRaceCleanup(sym *AuxCall, v *Value) bool {
        for _, b := range f.Blocks {
                for _, v := range b.Values {
                        switch v.Op {
-                       case OpStaticCall:
+                       case OpStaticCall, OpStaticLECall:
                                // Check for racefuncenter/racefuncenterfp will encounter racefuncexit and vice versa.
                                // Allow calls to panic*
                                s := v.Aux.(*AuxCall).Fn.String()
@@ -1632,15 +1635,20 @@ func needRaceCleanup(sym *AuxCall, v *Value) bool {
                                return false
                        case OpPanicBounds, OpPanicExtend:
                                // Note: these are panic generators that are ok (like the static calls above).
-                       case OpClosureCall, OpInterCall:
+                       case OpClosureCall, OpInterCall, OpClosureLECall, OpInterLECall:
                                // We must keep the race functions if there are any other call types.
                                return false
                        }
                }
        }
        if isSameCall(sym, "runtime.racefuncenter") {
+               // TODO REGISTER ABI this needs to be cleaned up.
                // If we're removing racefuncenter, remove its argument as well.
                if v.Args[0].Op != OpStore {
+                       if v.Op == OpStaticLECall {
+                               // there is no store, yet.
+                               return true
+                       }
                        return false
                }
                mem := v.Args[0].Args[2]
index 958e24d29f02e167c473fec5b3061c44ba145419..e5a27199a7b681e26b8cb0ff2dc80294406163d4 100644 (file)
@@ -122,8 +122,6 @@ func rewriteValuegeneric(v *Value) bool {
                return rewriteValuegeneric_OpEqSlice(v)
        case OpIMake:
                return rewriteValuegeneric_OpIMake(v)
-       case OpInterCall:
-               return rewriteValuegeneric_OpInterCall(v)
        case OpInterLECall:
                return rewriteValuegeneric_OpInterLECall(v)
        case OpIsInBounds:
@@ -392,8 +390,6 @@ func rewriteValuegeneric(v *Value) bool {
                return rewriteValuegeneric_OpSlicemask(v)
        case OpSqrt:
                return rewriteValuegeneric_OpSqrt(v)
-       case OpStaticCall:
-               return rewriteValuegeneric_OpStaticCall(v)
        case OpStaticLECall:
                return rewriteValuegeneric_OpStaticLECall(v)
        case OpStore:
@@ -8542,52 +8538,6 @@ func rewriteValuegeneric_OpIMake(v *Value) bool {
        }
        return false
 }
-func rewriteValuegeneric_OpInterCall(v *Value) bool {
-       v_1 := v.Args[1]
-       v_0 := v.Args[0]
-       // match: (InterCall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) mem)
-       // cond: devirt(v, auxCall, itab, off) != nil
-       // result: (StaticCall [int32(argsize)] {devirt(v, auxCall, itab, off)} mem)
-       for {
-               argsize := auxIntToInt32(v.AuxInt)
-               auxCall := auxToCall(v.Aux)
-               if v_0.Op != OpLoad {
-                       break
-               }
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpOffPtr {
-                       break
-               }
-               off := auxIntToInt64(v_0_0.AuxInt)
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpITab {
-                       break
-               }
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpIMake {
-                       break
-               }
-               v_0_0_0_0_0 := v_0_0_0_0.Args[0]
-               if v_0_0_0_0_0.Op != OpAddr {
-                       break
-               }
-               itab := auxToSym(v_0_0_0_0_0.Aux)
-               v_0_0_0_0_0_0 := v_0_0_0_0_0.Args[0]
-               if v_0_0_0_0_0_0.Op != OpSB {
-                       break
-               }
-               mem := v_1
-               if !(devirt(v, auxCall, itab, off) != nil) {
-                       break
-               }
-               v.reset(OpStaticCall)
-               v.AuxInt = int32ToAuxInt(int32(argsize))
-               v.Aux = callToAux(devirt(v, auxCall, itab, off))
-               v.AddArg(mem)
-               return true
-       }
-       return false
-}
 func rewriteValuegeneric_OpInterLECall(v *Value) bool {
        // match: (InterLECall [argsize] {auxCall} (Load (OffPtr [off] (ITab (IMake (Addr {itab} (SB)) _))) _) ___)
        // cond: devirtLESym(v, auxCall, itab, off) != nil
@@ -16113,7 +16063,6 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
        v_1 := v.Args[1]
        v_0 := v.Args[0]
        b := v.Block
-       config := b.Func.Config
        fe := b.Func.fe
        // match: (NilCheck (GetG mem) mem)
        // result: mem
@@ -16128,67 +16077,7 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
                v.copyOf(mem)
                return true
        }
-       // match: (NilCheck (Load (OffPtr [c] (SP)) (StaticCall {sym} _)) _)
-       // cond: isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")
-       // result: (Invalid)
-       for {
-               if v_0.Op != OpLoad {
-                       break
-               }
-               _ = v_0.Args[1]
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpOffPtr {
-                       break
-               }
-               c := auxIntToInt64(v_0_0.AuxInt)
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpSP {
-                       break
-               }
-               v_0_1 := v_0.Args[1]
-               if v_0_1.Op != OpStaticCall {
-                       break
-               }
-               sym := auxToCall(v_0_1.Aux)
-               if !(isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
-                       break
-               }
-               v.reset(OpInvalid)
-               return true
-       }
-       // match: (NilCheck (OffPtr (Load (OffPtr [c] (SP)) (StaticCall {sym} _))) _)
-       // cond: isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")
-       // result: (Invalid)
-       for {
-               if v_0.Op != OpOffPtr {
-                       break
-               }
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpLoad {
-                       break
-               }
-               _ = v_0_0.Args[1]
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpOffPtr {
-                       break
-               }
-               c := auxIntToInt64(v_0_0_0.AuxInt)
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpSP {
-                       break
-               }
-               v_0_0_1 := v_0_0.Args[1]
-               if v_0_0_1.Op != OpStaticCall {
-                       break
-               }
-               sym := auxToCall(v_0_0_1.Aux)
-               if !(isSameCall(sym, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
-                       break
-               }
-               v.reset(OpInvalid)
-               return true
-       }
-       // match: (NilCheck (SelectN [0] call:(StaticLECall _ _)) (SelectN [1] call))
+       // match: (NilCheck (SelectN [0] call:(StaticLECall _ _)) _)
        // cond: isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")
        // result: (Invalid)
        for {
@@ -16196,13 +16085,13 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
                        break
                }
                call := v_0.Args[0]
-               if call.Op != OpStaticLECall || len(call.Args) != 2 || v_1.Op != OpSelectN || auxIntToInt64(v_1.AuxInt) != 1 || call != v_1.Args[0] || !(isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
+               if call.Op != OpStaticLECall || len(call.Args) != 2 || !(isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
                        break
                }
                v.reset(OpInvalid)
                return true
        }
-       // match: (NilCheck (OffPtr (SelectN [0] call:(StaticLECall _ _))) (SelectN [1] call))
+       // match: (NilCheck (OffPtr (SelectN [0] call:(StaticLECall _ _))) _)
        // cond: isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")
        // result: (Invalid)
        for {
@@ -16214,7 +16103,7 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
                        break
                }
                call := v_0_0.Args[0]
-               if call.Op != OpStaticLECall || len(call.Args) != 2 || v_1.Op != OpSelectN || auxIntToInt64(v_1.AuxInt) != 1 || call != v_1.Args[0] || !(isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
+               if call.Op != OpStaticLECall || len(call.Args) != 2 || !(isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
                        break
                }
                v.reset(OpInvalid)
@@ -20799,6 +20688,94 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
                v.copyOf(c)
                return true
        }
+       // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
+       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
+       // result: (Move {t.Elem()} [int64(sz)] dst src mem)
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticCall || len(call.Args) != 1 {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               s1 := call.Args[0]
+               if s1.Op != OpStore {
+                       break
+               }
+               _ = s1.Args[2]
+               s1_1 := s1.Args[1]
+               if s1_1.Op != OpConst64 {
+                       break
+               }
+               sz := auxIntToInt64(s1_1.AuxInt)
+               s2 := s1.Args[2]
+               if s2.Op != OpStore {
+                       break
+               }
+               _ = s2.Args[2]
+               src := s2.Args[1]
+               s3 := s2.Args[2]
+               if s3.Op != OpStore {
+                       break
+               }
+               t := auxToType(s3.Aux)
+               mem := s3.Args[2]
+               dst := s3.Args[1]
+               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
+                       break
+               }
+               v.reset(OpMove)
+               v.AuxInt = int64ToAuxInt(int64(sz))
+               v.Aux = typeToAux(t.Elem())
+               v.AddArg3(dst, src, mem)
+               return true
+       }
+       // match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
+       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
+       // result: (Move {t.Elem()} [int64(sz)] dst src mem)
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticCall || len(call.Args) != 1 {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               s1 := call.Args[0]
+               if s1.Op != OpStore {
+                       break
+               }
+               _ = s1.Args[2]
+               s1_1 := s1.Args[1]
+               if s1_1.Op != OpConst32 {
+                       break
+               }
+               sz := auxIntToInt32(s1_1.AuxInt)
+               s2 := s1.Args[2]
+               if s2.Op != OpStore {
+                       break
+               }
+               _ = s2.Args[2]
+               src := s2.Args[1]
+               s3 := s2.Args[2]
+               if s3.Op != OpStore {
+                       break
+               }
+               t := auxToType(s3.Aux)
+               mem := s3.Args[2]
+               dst := s3.Args[1]
+               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
+                       break
+               }
+               v.reset(OpMove)
+               v.AuxInt = int64ToAuxInt(int64(sz))
+               v.Aux = typeToAux(t.Elem())
+               v.AddArg3(dst, src, mem)
+               return true
+       }
        // match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
        // cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
        // result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
@@ -20857,6 +20834,44 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
                v.AddArg3(dst, src, mem)
                return true
        }
+       // match: (SelectN [0] call:(StaticLECall {sym} a x))
+       // cond: needRaceCleanup(sym, call) && clobber(call)
+       // result: x
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticLECall || len(call.Args) != 2 {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               x := call.Args[1]
+               if !(needRaceCleanup(sym, call) && clobber(call)) {
+                       break
+               }
+               v.copyOf(x)
+               return true
+       }
+       // match: (SelectN [0] call:(StaticLECall {sym} x))
+       // cond: needRaceCleanup(sym, call) && clobber(call)
+       // result: x
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpStaticLECall || len(call.Args) != 1 {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               x := call.Args[0]
+               if !(needRaceCleanup(sym, call) && clobber(call)) {
+                       break
+               }
+               v.copyOf(x)
+               return true
+       }
        return false
 }
 func rewriteValuegeneric_OpSignExt16to32(v *Value) bool {
@@ -21307,98 +21322,6 @@ func rewriteValuegeneric_OpSqrt(v *Value) bool {
        }
        return false
 }
-func rewriteValuegeneric_OpStaticCall(v *Value) bool {
-       v_0 := v.Args[0]
-       b := v.Block
-       config := b.Func.Config
-       // match: (StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))
-       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)
-       // result: (Move {t.Elem()} [int64(sz)] dst src mem)
-       for {
-               sym := auxToCall(v.Aux)
-               s1 := v_0
-               if s1.Op != OpStore {
-                       break
-               }
-               _ = s1.Args[2]
-               s1_1 := s1.Args[1]
-               if s1_1.Op != OpConst64 {
-                       break
-               }
-               sz := auxIntToInt64(s1_1.AuxInt)
-               s2 := s1.Args[2]
-               if s2.Op != OpStore {
-                       break
-               }
-               _ = s2.Args[2]
-               src := s2.Args[1]
-               s3 := s2.Args[2]
-               if s3.Op != OpStore {
-                       break
-               }
-               t := auxToType(s3.Aux)
-               mem := s3.Args[2]
-               dst := s3.Args[1]
-               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) {
-                       break
-               }
-               v.reset(OpMove)
-               v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(t.Elem())
-               v.AddArg3(dst, src, mem)
-               return true
-       }
-       // match: (StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem))))
-       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)
-       // result: (Move {t.Elem()} [int64(sz)] dst src mem)
-       for {
-               sym := auxToCall(v.Aux)
-               s1 := v_0
-               if s1.Op != OpStore {
-                       break
-               }
-               _ = s1.Args[2]
-               s1_1 := s1.Args[1]
-               if s1_1.Op != OpConst32 {
-                       break
-               }
-               sz := auxIntToInt32(s1_1.AuxInt)
-               s2 := s1.Args[2]
-               if s2.Op != OpStore {
-                       break
-               }
-               _ = s2.Args[2]
-               src := s2.Args[1]
-               s3 := s2.Args[2]
-               if s3.Op != OpStore {
-                       break
-               }
-               t := auxToType(s3.Aux)
-               mem := s3.Args[2]
-               dst := s3.Args[1]
-               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3)) {
-                       break
-               }
-               v.reset(OpMove)
-               v.AuxInt = int64ToAuxInt(int64(sz))
-               v.Aux = typeToAux(t.Elem())
-               v.AddArg3(dst, src, mem)
-               return true
-       }
-       // match: (StaticCall {sym} x)
-       // cond: needRaceCleanup(sym, v)
-       // result: x
-       for {
-               sym := auxToCall(v.Aux)
-               x := v_0
-               if !(needRaceCleanup(sym, v)) {
-                       break
-               }
-               v.copyOf(x)
-               return true
-       }
-       return false
-}
 func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
        b := v.Block
        typ := &b.Func.Config.Types
@@ -21442,7 +21365,6 @@ func rewriteValuegeneric_OpStore(v *Value) bool {
        v_1 := v.Args[1]
        v_0 := v.Args[0]
        b := v.Block
-       config := b.Func.Config
        fe := b.Func.fe
        // match: (Store {t1} p1 (Load <t2> p2 mem) mem)
        // cond: isSamePtr(p1, p2) && t2.Size() == t1.Size()
@@ -21890,58 +21812,6 @@ func rewriteValuegeneric_OpStore(v *Value) bool {
                v.AddArg3(dst, e, mem)
                return true
        }
-       // match: (Store (Load (OffPtr [c] (SP)) mem) x mem)
-       // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize
-       // result: mem
-       for {
-               if v_0.Op != OpLoad {
-                       break
-               }
-               mem := v_0.Args[1]
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpOffPtr {
-                       break
-               }
-               c := auxIntToInt64(v_0_0.AuxInt)
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpSP {
-                       break
-               }
-               x := v_1
-               if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) {
-                       break
-               }
-               v.copyOf(mem)
-               return true
-       }
-       // match: (Store (OffPtr (Load (OffPtr [c] (SP)) mem)) x mem)
-       // cond: isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize
-       // result: mem
-       for {
-               if v_0.Op != OpOffPtr {
-                       break
-               }
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpLoad {
-                       break
-               }
-               mem := v_0_0.Args[1]
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpOffPtr {
-                       break
-               }
-               c := auxIntToInt64(v_0_0_0.AuxInt)
-               v_0_0_0_0 := v_0_0_0.Args[0]
-               if v_0_0_0_0.Op != OpSP {
-                       break
-               }
-               x := v_1
-               if mem != v_2 || !(isConstZero(x) && mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) {
-                       break
-               }
-               v.copyOf(mem)
-               return true
-       }
        // match: (Store (SelectN [0] call:(StaticLECall _ _)) x mem:(SelectN [1] call))
        // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")
        // result: mem
@@ -24660,27 +24530,6 @@ func rewriteValuegeneric_OpZero(v *Value) bool {
        v_1 := v.Args[1]
        v_0 := v.Args[0]
        b := v.Block
-       config := b.Func.Config
-       // match: (Zero (Load (OffPtr [c] (SP)) mem) mem)
-       // cond: mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize() + config.RegSize
-       // result: mem
-       for {
-               if v_0.Op != OpLoad {
-                       break
-               }
-               mem := v_0.Args[1]
-               v_0_0 := v_0.Args[0]
-               if v_0_0.Op != OpOffPtr {
-                       break
-               }
-               c := auxIntToInt64(v_0_0.AuxInt)
-               v_0_0_0 := v_0_0.Args[0]
-               if v_0_0_0.Op != OpSP || mem != v_1 || !(mem.Op == OpStaticCall && isSameCall(mem.Aux, "runtime.newobject") && c == config.ctxt.FixedFrameSize()+config.RegSize) {
-                       break
-               }
-               v.copyOf(mem)
-               return true
-       }
        // match: (Zero (SelectN [0] call:(StaticLECall _ _)) mem:(SelectN [1] call))
        // cond: isSameCall(call.Aux, "runtime.newobject")
        // result: mem
index 8facb91100c5e79de0337d8de96a9608464a996c..6b34310db70e2902566a941db191db2e30ca9e87 100644 (file)
@@ -145,7 +145,7 @@ func schedule(f *Func) {
                                // reduce register pressure. It also helps make sure
                                // VARDEF ops are scheduled before the corresponding LEA.
                                score[v.ID] = ScoreMemory
-                       case v.Op == OpSelect0 || v.Op == OpSelect1:
+                       case v.Op == OpSelect0 || v.Op == OpSelect1 || v.Op == OpSelectN:
                                // Schedule the pseudo-op of reading part of a tuple
                                // immediately after the tuple-generating op, since
                                // this value is already live. This also removes its
@@ -270,6 +270,20 @@ func schedule(f *Func) {
                                        tuples[v.Args[0].ID] = make([]*Value, 2)
                                }
                                tuples[v.Args[0].ID][1] = v
+                       case v.Op == OpSelectN:
+                               if tuples[v.Args[0].ID] == nil {
+                                       tuples[v.Args[0].ID] = make([]*Value, v.Args[0].Type.NumFields())
+                               }
+                               tuples[v.Args[0].ID][v.AuxInt] = v
+                       case v.Type.IsResults() && tuples[v.ID] != nil:
+                               tup := tuples[v.ID]
+                               for i := len(tup) - 1; i >= 0; i-- {
+                                       if tup[i] != nil {
+                                               order = append(order, tup[i])
+                                       }
+                               }
+                               delete(tuples, v.ID)
+                               order = append(order, v)
                        case v.Type.IsTuple() && tuples[v.ID] != nil:
                                if tuples[v.ID][1] != nil {
                                        order = append(order, tuples[v.ID][1])
index 5dfc45364954c12ea1971b138a0e570865aca361..bd08334a5fe0a5a89009d9d463562bfd4136635c 100644 (file)
@@ -18,10 +18,11 @@ func tighten(f *Func) {
                                continue
                        }
                        switch v.Op {
-                       case OpPhi, OpArg, OpSelect0, OpSelect1:
+                       case OpPhi, OpArg, OpSelect0, OpSelect1, OpSelectN:
                                // Phis need to stay in their block.
                                // Arg must stay in the entry block.
                                // Tuple selectors must stay with the tuple generator.
+                               // SelectN is typically, ultimately, a register.
                                continue
                        }
                        if v.MemoryArg() != nil {
index 38deabf83d24b24a5c97dd139759dc99410b509d..289df40431a7a7b4ce2cb3083d7e24332d45af07 100644 (file)
@@ -4,8 +4,8 @@
 
 package ssa
 
-// tightenTupleSelectors ensures that tuple selectors (Select0 and
-// Select1 ops) are in the same block as their tuple generator. The
+// tightenTupleSelectors ensures that tuple selectors (Select0, Select1,
+// and SelectN ops) are in the same block as their tuple generator. The
 // function also ensures that there are no duplicate tuple selectors.
 // These properties are expected by the scheduler but may not have
 // been maintained by the optimization pipeline up to this point.
@@ -13,28 +13,40 @@ package ssa
 // See issues 16741 and 39472.
 func tightenTupleSelectors(f *Func) {
        selectors := make(map[struct {
-               id ID
-               op Op
+               id    ID
+               which int
        }]*Value)
        for _, b := range f.Blocks {
                for _, selector := range b.Values {
-                       if selector.Op != OpSelect0 && selector.Op != OpSelect1 {
+                       // Key fields for de-duplication
+                       var tuple *Value
+                       idx := 0
+                       switch selector.Op {
+                       default:
                                continue
-                       }
-
-                       // Get the tuple generator to use as a key for de-duplication.
-                       tuple := selector.Args[0]
-                       if !tuple.Type.IsTuple() {
-                               f.Fatalf("arg of tuple selector %s is not a tuple: %s", selector.String(), tuple.LongString())
+                       case OpSelect1:
+                               idx = 1
+                               fallthrough
+                       case OpSelect0:
+                               tuple = selector.Args[0]
+                               if !tuple.Type.IsTuple() {
+                                       f.Fatalf("arg of tuple selector %s is not a tuple: %s", selector.String(), tuple.LongString())
+                               }
+                       case OpSelectN:
+                               tuple = selector.Args[0]
+                               idx = int(selector.AuxInt)
+                               if !tuple.Type.IsResults() {
+                                       f.Fatalf("arg of result selector %s is not a results: %s", selector.String(), tuple.LongString())
+                               }
                        }
 
                        // If there is a pre-existing selector in the target block then
                        // use that. Do this even if the selector is already in the
                        // target block to avoid duplicate tuple selectors.
                        key := struct {
-                               id ID
-                               op Op
-                       }{tuple.ID, selector.Op}
+                               id    ID
+                               which int
+                       }{tuple.ID, idx}
                        if t := selectors[key]; t != nil {
                                if selector != t {
                                        selector.copyOf(t)
index 7d375da1283321db82762cd2110372afd00ae154..0af039577f99cdbc64f6fd074297122e2befbc90 100644 (file)
@@ -487,12 +487,14 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va
        off := config.ctxt.FixedFrameSize()
 
        var ACArgs []Param
+       var argTypes []*types.Type
        if typ != nil { // for typedmemmove
                taddr := b.NewValue1A(pos, OpAddr, b.Func.Config.Types.Uintptr, typ, sb)
                off = round(off, taddr.Type.Alignment())
                arg := b.NewValue1I(pos, OpOffPtr, taddr.Type.PtrTo(), off, sp)
                mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, taddr, mem)
                ACArgs = append(ACArgs, Param{Type: b.Func.Config.Types.Uintptr, Offset: int32(off)})
+               argTypes = append(argTypes, b.Func.Config.Types.Uintptr)
                off += taddr.Type.Size()
        }
 
@@ -500,6 +502,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va
        arg := b.NewValue1I(pos, OpOffPtr, ptr.Type.PtrTo(), off, sp)
        mem = b.NewValue3A(pos, OpStore, types.TypeMem, ptr.Type, arg, ptr, mem)
        ACArgs = append(ACArgs, Param{Type: ptr.Type, Offset: int32(off)})
+       argTypes = append(argTypes, ptr.Type)
        off += ptr.Type.Size()
 
        if val != nil {
@@ -507,15 +510,15 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va
                arg = b.NewValue1I(pos, OpOffPtr, val.Type.PtrTo(), off, sp)
                mem = b.NewValue3A(pos, OpStore, types.TypeMem, val.Type, arg, val, mem)
                ACArgs = append(ACArgs, Param{Type: val.Type, Offset: int32(off)})
+               argTypes = append(argTypes, val.Type)
                off += val.Type.Size()
        }
        off = round(off, config.PtrSize)
 
        // issue call
-       // TODO(register args) -- will need more details
-       mem = b.NewValue1A(pos, OpStaticCall, types.TypeMem, StaticAuxCall(fn, ACArgs, nil, nil), mem)
+       mem = b.NewValue1A(pos, OpStaticCall, types.TypeResultMem, StaticAuxCall(fn, ACArgs, nil, b.Func.ABIDefault.ABIAnalyzeTypes(nil, argTypes, nil)), mem)
        mem.AuxInt = off - config.ctxt.FixedFrameSize()
-       return mem
+       return b.NewValue1I(pos, OpSelectN, types.TypeMem, 0, mem)
 }
 
 // round to a multiple of r, r is a power of 2
@@ -563,12 +566,20 @@ func IsReadOnlyGlobalAddr(v *Value) bool {
 
 // IsNewObject reports whether v is a pointer to a freshly allocated & zeroed object at memory state mem.
 func IsNewObject(v *Value, mem *Value) bool {
+       // TODO this will need updating for register args; the OpLoad is wrong.
        if v.Op != OpLoad {
                return false
        }
        if v.MemoryArg() != mem {
                return false
        }
+       if mem.Op != OpSelectN {
+               return false
+       }
+       if mem.Type != types.TypeMem {
+               return false
+       } // assume it is the right selection if true
+       mem = mem.Args[0]
        if mem.Op != OpStaticCall {
                return false
        }
index 20acdbdc660fbc83894ac3035443979b11744b82..ba00b9c7f6e3428a1579889318801ece028091e5 100644 (file)
@@ -4734,7 +4734,8 @@ func (s *state) openDeferExit() {
                        aux := ssa.ClosureAuxCall(ACArgs, ACResults)
                        call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
                } else {
-                       aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), ACArgs, ACResults, nil) // TODO will need types for this.
+                       aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), ACArgs, ACResults,
+                               s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
                        call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
                }
                callArgs = append(callArgs, s.mem())
@@ -4896,7 +4897,8 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
 
                // Call runtime.deferprocStack with pointer to _defer record.
                ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(base.Ctxt.FixedFrameSize())})
-               aux := ssa.StaticAuxCall(ir.Syms.DeferprocStack, ACArgs, ACResults, nil)
+               aux := ssa.StaticAuxCall(ir.Syms.DeferprocStack, ACArgs, ACResults,
+                       s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
                callArgs = append(callArgs, addr, s.mem())
                call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
                call.AddArgs(callArgs...)
@@ -4956,10 +4958,12 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
                // call target
                switch {
                case k == callDefer:
-                       aux := ssa.StaticAuxCall(ir.Syms.Deferproc, ACArgs, ACResults, nil) // TODO paramResultInfo for DeferProc
+                       aux := ssa.StaticAuxCall(ir.Syms.Deferproc, ACArgs, ACResults,
+                               s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults))) // TODO paramResultInfo for DeferProc
                        call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
                case k == callGo:
-                       aux := ssa.StaticAuxCall(ir.Syms.Newproc, ACArgs, ACResults, nil)
+                       aux := ssa.StaticAuxCall(ir.Syms.Newproc, ACArgs, ACResults,
+                               s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults)))
                        call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) // TODO paramResultInfo for NewProc
                case closure != nil:
                        // rawLoad because loading the code pointer from a
@@ -5434,6 +5438,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
        var ACArgs []ssa.Param
        var ACResults []ssa.Param
        var callArgs []*ssa.Value
+       var callArgTypes []*types.Type
 
        for _, arg := range args {
                t := arg.Type
@@ -5441,6 +5446,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
                size := t.Size()
                ACArgs = append(ACArgs, ssa.Param{Type: t, Offset: int32(off)})
                callArgs = append(callArgs, arg)
+               callArgTypes = append(callArgTypes, t)
                off += size
        }
        off = types.Rnd(off, int64(types.RegSize))
@@ -5455,7 +5461,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
 
        // Issue call
        var call *ssa.Value
-       aux := ssa.StaticAuxCall(fn, ACArgs, ACResults, nil) // WILL NEED A TYPE FOR THIS.)
+       aux := ssa.StaticAuxCall(fn, ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, callArgTypes, results))
        callArgs = append(callArgs, s.mem())
        call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
        call.AddArgs(callArgs...)
@@ -6520,7 +6526,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
                                // input args need no code
                        case ssa.OpSP, ssa.OpSB:
                                // nothing to do
-                       case ssa.OpSelect0, ssa.OpSelect1:
+                       case ssa.OpSelect0, ssa.OpSelect1, ssa.OpSelectN:
                                // nothing to do
                        case ssa.OpGetG:
                                // nothing to do when there's a g register,
index b6374e49a51a13dfb169d3caad6f57b8a2aa204b..9fb6d689941fd60679c1b4307d38e75c17ac1a69 100644 (file)
@@ -581,12 +581,19 @@ func NewTuple(t1, t2 *Type) *Type {
        return t
 }
 
-func NewResults(types []*Type) *Type {
+func newResults(types []*Type) *Type {
        t := New(TRESULTS)
        t.Extra.(*Results).Types = types
        return t
 }
 
+func NewResults(types []*Type) *Type {
+       if len(types) == 1 && types[0] == TypeMem {
+               return TypeResultMem
+       }
+       return newResults(types)
+}
+
 func newSSA(name string) *Type {
        t := New(TSSA)
        t.Extra = name
@@ -1407,6 +1414,9 @@ func (t *Type) PtrTo() *Type {
 }
 
 func (t *Type) NumFields() int {
+       if t.kind == TRESULTS {
+               return len(t.Extra.(*Results).Types)
+       }
        return t.Fields().Len()
 }
 func (t *Type) FieldType(i int) *Type {
@@ -1597,11 +1607,12 @@ func FakeRecvType() *Type {
 
 var (
        // TSSA types. HasPointers assumes these are pointer-free.
-       TypeInvalid = newSSA("invalid")
-       TypeMem     = newSSA("mem")
-       TypeFlags   = newSSA("flags")
-       TypeVoid    = newSSA("void")
-       TypeInt128  = newSSA("int128")
+       TypeInvalid   = newSSA("invalid")
+       TypeMem       = newSSA("mem")
+       TypeFlags     = newSSA("flags")
+       TypeVoid      = newSSA("void")
+       TypeInt128    = newSSA("int128")
+       TypeResultMem = newResults([]*Type{TypeMem})
 )
 
 // NewNamed returns a new named type for the given type name.