diff options
Diffstat (limited to 'src/cmd/compile/internal/ssagen/ssa.go')
-rw-r--r-- | src/cmd/compile/internal/ssagen/ssa.go | 323 |
1 files changed, 218 insertions, 105 deletions
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index b042c132d5..05dd0c62a9 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -7,6 +7,7 @@ package ssagen import ( "bufio" "bytes" + "cmd/compile/internal/abi" "encoding/binary" "fmt" "go/constant" @@ -208,6 +209,32 @@ func InitConfig() { ir.Syms.SigPanic = typecheck.LookupRuntimeFunc("sigpanic") } +// AbiForFunc returns the ABI for a function, used to figure out arg/result mapping for rtcall and bodyless functions. +// This follows policy for GOEXPERIMENT=regabi, //go:registerparams, and currently defined ABIInternal. +// Policy is subject to change.... +// This always returns a freshly copied ABI. +func AbiForFunc(fn *ir.Func) *abi.ABIConfig { + return abiForFunc(fn, ssaConfig.ABI0, ssaConfig.ABI1).Copy() // No idea what races will result, be safe +} + +// abiForFunc implements ABI policy for a function, but does not return a copy of the ABI. +// Passing a nil function returns ABIInternal. +func abiForFunc(fn *ir.Func, abi0, abi1 *abi.ABIConfig) *abi.ABIConfig { + a := abi1 + if true || objabi.Regabi_enabled == 0 { + a = abi0 + } + if fn != nil && fn.Pragma&ir.RegisterParams != 0 { // TODO(register args) remove after register abi is working + name := ir.FuncName(fn) + if strings.Contains(name, ".") { + base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name) + } + a = abi1 + base.WarnfAt(fn.Pos(), "declared function %v has register params", fn) + } + return a +} + // getParam returns the Field of ith param of node n (which is a // function/method/interface call), where the receiver of a method call is // considered as the 0th parameter. This does not include the receiver of an @@ -274,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 } @@ -297,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 { @@ -357,12 +395,10 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { if fn.Pragma&ir.Nosplit != 0 { s.f.NoSplit = true } - if fn.Pragma&ir.RegisterParams != 0 { // TODO remove after register abi is working - if strings.Contains(name, ".") { - base.ErrorfAt(fn.Pos(), "Calls to //go:registerparams method %s won't work, remove the pragma from the declaration.", name) - } - s.f.Warnl(fn.Pos(), "declared function %v has register params", fn) - } + s.f.ABI0 = ssaConfig.ABI0.Copy() // Make a copy to avoid racy map operations in type-register-width cache. + s.f.ABI1 = ssaConfig.ABI1.Copy() + s.f.ABIDefault = abiForFunc(nil, s.f.ABI0, s.f.ABI1) + s.f.ABISelf = abiForFunc(fn, s.f.ABI0, s.f.ABI1) s.panics = map[funcLine]*ssa.Block{} s.softFloat = s.config.SoftFloat @@ -449,18 +485,20 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, deferBitsTemp, s.mem(), false) } + var params *abi.ABIParamResultInfo + params = s.f.ABISelf.ABIAnalyze(fn.Type()) + // Generate addresses of local declarations s.decladdrs = map[*ir.Name]*ssa.Value{} - var args []ssa.Param var results []ssa.Param for _, n := range fn.Dcl { switch n.Class { case ir.PPARAM: + // Be aware that blank and unnamed input parameters will not appear here, but do appear in the type s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type()), n, s.sp, s.startmem) - args = append(args, ssa.Param{Type: n.Type(), Offset: int32(n.FrameOffset())}) case ir.PPARAMOUT: s.decladdrs[n] = s.entryNewValue2A(ssa.OpLocalAddr, types.NewPtr(n.Type()), n, s.sp, s.startmem) - results = append(results, ssa.Param{Type: n.Type(), Offset: int32(n.FrameOffset()), Name: n}) + results = append(results, ssa.Param{Name: n}) case ir.PAUTO: // processed at each use, to prevent Addr coming // before the decl. @@ -468,14 +506,65 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { s.Fatalf("local variable with class %v unimplemented", n.Class) } } - s.f.OwnAux = ssa.OwnAuxCall(args, results) + + // TODO: figure out why base.Ctxt.FixedFrameSize() is not added to these offsets here (compare to calls). + // The input half is ignored unless a register ABI is used. + var args []ssa.Param + for _, p := range params.InParams() { + r := p.Registers + var o int32 + if len(r) == 0 { + o = p.Offset() + } else { + o = p.SpillOffset() + int32(params.SpillAreaOffset()) + } + args = append(args, ssa.Param{Type: p.Type, Offset: o, Reg: r}) + } + + // For now, need the ir.Name attached to these, so update those already created. + for i, p := range params.OutParams() { + r := p.Registers + var o int32 + if len(r) == 0 { + o = p.Offset() + } else { + o = types.BADWIDTH + } + results[i].Type = p.Type + results[i].Offset = o + results[i].Reg = r + } + + s.f.OwnAux = ssa.OwnAuxCall(fn.LSym, args, results, params) // Populate SSAable arguments. for _, n := range fn.Dcl { - if n.Class == ir.PPARAM && s.canSSA(n) { - 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. + if n.Class == ir.PPARAM { + if s.canSSA(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. + } else if !s.canSSAName(n) { // I.e., the address was taken. The type may or may not be okay. + // If the value will arrive in registers, + // AND if it can be SSA'd (if it cannot, panic for now), + // THEN + // (1) receive it as an OpArg (but do not store its name in the var table) + // (2) store it to its spill location, which is its address as well. + paramAssignment := ssa.ParamAssignmentForArgName(s.f, n) + if len(paramAssignment.Registers) > 0 { + if !TypeOK(n.Type()) { // TODO register args -- if v is not an SSA-able type, must decompose, here. + panic(fmt.Errorf("Arg in registers is too big to be SSA'd, need to implement decomposition, type=%v, n=%v", n.Type(), n)) + } + v := s.newValue0A(ssa.OpArg, n.Type(), n) + s.store(n.Type(), s.decladdrs[n], v) + } + } } } @@ -1151,7 +1240,7 @@ func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrument if f.Sym.IsBlank() { continue } - offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), f.Offset, addr) + offptr := s.newValue1I(ssa.OpOffPtr, types.NewPtr(f.Type), abi.FieldOffsetOf(f), addr) s.instrumentFields(f.Type, offptr, kind) } } @@ -1846,7 +1935,7 @@ func (s *state) exit() *ssa.Block { } // Run exit code. Today, this is just racefuncexit, in -race mode. - // TODO this seems risky here with a register-ABI, but not clear it is right to do it earlier either. + // TODO(register args) this seems risky here with a register-ABI, but not clear it is right to do it earlier either. // Spills in register allocation might just fix it. s.stmtList(s.curfn.Exit) @@ -2862,15 +2951,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).ResultForOffset(n.Offset) + 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) @@ -4674,7 +4759,7 @@ func (s *state) openDeferExit() { } for j, argAddrVal := range r.argVals { f := getParam(r.n, j) - ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + f.Offset)}) + ACArgs = append(ACArgs, ssa.Param{Type: f.Type, Offset: int32(argStart + abi.FieldOffsetOf(f))}) var a *ssa.Value if !TypeOK(f.Type) { a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem()) @@ -4688,10 +4773,11 @@ func (s *state) openDeferExit() { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[types.TUINTPTR], v) - aux := ssa.ClosureAuxCall(ACArgs, ACResults) + aux := ssa.ClosureAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults))) call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v) } else { - aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), ACArgs, ACResults) + 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()) @@ -4738,18 +4824,9 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val var codeptr *ssa.Value // ptr to target code (if dynamic) var rcvr *ssa.Value // receiver to set fn := n.X - var ACArgs []ssa.Param - var ACResults []ssa.Param - var callArgs []*ssa.Value - res := n.X.Type().Results() - if k == callNormal { - nf := res.NumFields() - for i := 0; i < nf; i++ { - fp := res.Field(i) - ACResults = append(ACResults, ssa.Param{Type: fp.Type, Offset: int32(fp.Offset + base.Ctxt.FixedFrameSize())}) - } - } - + var ACArgs []ssa.Param // AuxCall args + var ACResults []ssa.Param // AuxCall results + var callArgs []*ssa.Value // For late-expansion, the args themselves (not stored, args to the call instead). inRegisters := false switch n.Op() { @@ -4757,7 +4834,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val if k == callNormal && fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC { fn := fn.(*ir.Name) callee = fn - // TODO remove after register abi is working + // TODO(register args) remove after register abi is working inRegistersImported := fn.Pragma()&ir.RegisterParams != 0 inRegistersSamePackage := fn.Func != nil && fn.Func.Pragma&ir.RegisterParams != 0 inRegisters = inRegistersImported || inRegistersSamePackage @@ -4790,6 +4867,27 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val types.CalcSize(fn.Type()) stksize := fn.Type().ArgWidth() // includes receiver, args, and results + callABI := s.f.ABI1 + if !inRegisters { + callABI = s.f.ABI0 + } + + params := callABI.ABIAnalyze(n.X.Type()) + + res := n.X.Type().Results() + if k == callNormal { + for _, p := range params.OutParams() { + r := p.Registers + var o int32 + if len(r) == 0 { + o = p.Offset() + } else { + o = p.SpillOffset() + int32(params.SpillAreaOffset()) + } + ACResults = append(ACResults, ssa.Param{Type: p.Type, Offset: o, Reg: r}) + } + } + var call *ssa.Value if k == callDeferStack { // Make a defer struct d on the stack. @@ -4821,7 +4919,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. @@ -4835,20 +4933,21 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val } // Set other args. for _, f := range ft.Params().Fields().Slice() { - s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset) + s.storeArgWithBase(args[0], f.Type, addr, off+abi.FieldOffsetOf(f)) args = args[1:] } // 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) + 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...) if stksize < int64(types.PtrSize) { // We need room for both the call to deferprocStack and the call to // the deferred function. - // TODO Revisit this if/when we pass args in registers. + // TODO(register args) Revisit this if/when we pass args in registers. stksize = int64(types.PtrSize) } call.AuxInt = stksize @@ -4856,21 +4955,23 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val // Store arguments to stack, including defer/go arguments and receiver for method calls. // These are written in SP-offset order. argStart := base.Ctxt.FixedFrameSize() + // argExtra is for combining with ABI-derived offsets; argStart is for old ABI0 code (defer, go). + argExtra := int32(0) // TODO(register args) untangle this mess when fully transition to abiutils, defer/go sanitized. // Defer/go args. if k != callNormal { // Write argsize and closure (args to newproc/deferproc). argsize := s.constInt32(types.Types[types.TUINT32], int32(stksize)) - ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINT32], Offset: int32(argStart)}) + ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINT32], Offset: int32(argStart)}) // not argExtra callArgs = append(callArgs, argsize) ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(argStart) + int32(types.PtrSize)}) callArgs = append(callArgs, closure) stksize += 2 * int64(types.PtrSize) argStart += 2 * int64(types.PtrSize) + argExtra = 2 * int32(types.PtrSize) } // Set receiver (for interface calls). if rcvr != nil { - ACArgs = append(ACArgs, ssa.Param{Type: types.Types[types.TUINTPTR], Offset: int32(argStart)}) callArgs = append(callArgs, rcvr) } @@ -4880,11 +4981,20 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val if n.Op() == ir.OCALLMETH { base.Fatalf("OCALLMETH missed by walkCall") } - for i, n := range args { - f := t.Params().Field(i) - ACArg, arg := s.putArg(n, f.Type, argStart+f.Offset) + + for _, p := range params.InParams() { // includes receiver for interface calls + r := p.Registers + var o int32 + if len(r) == 0 { + o = p.Offset() + } else { + o = p.SpillOffset() + int32(params.SpillAreaOffset()) + } + ACArg := ssa.Param{Type: p.Type, Offset: argExtra + o, Reg: r} // o from ABI includes any architecture-dependent offsets. ACArgs = append(ACArgs, ACArg) - callArgs = append(callArgs, arg) + } + for i, n := range args { + callArgs = append(callArgs, s.putArg(n, t.Params().Field(i).Type)) } callArgs = append(callArgs, s.mem()) @@ -4892,11 +5002,13 @@ 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) + 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) - call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) + 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 // closure is always safe, but IsSanitizerSafeAddr @@ -4904,13 +5016,14 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val // critical that we not clobber any arguments already // stored onto the stack. codeptr = s.rawLoad(types.Types[types.TUINTPTR], closure) - aux := ssa.ClosureAuxCall(ACArgs, ACResults) + aux := ssa.ClosureAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults))) call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, closure) case codeptr != nil: - aux := ssa.InterfaceAuxCall(ACArgs, ACResults) + // Note that the "receiver" parameter is nil because the actual receiver is the first input parameter. + aux := ssa.InterfaceAuxCall(ACArgs, ACResults, s.f.ABIDefault.ABIAnalyzeTypes(nil, ssa.ACParamsToTypes(ACArgs), ssa.ACParamsToTypes(ACResults))) call = s.newValue1A(ssa.OpInterLECall, aux.LateExpansionResultType(), aux, codeptr) case callee != nil: - aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), ACArgs, ACResults) + aux := ssa.StaticAuxCall(callTargetLSym(callee, s.curfn.LSym), ACArgs, ACResults, params) call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) default: s.Fatalf("bad call type %v %v", n.Op(), n) @@ -5048,15 +5161,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).ResultForOffset(n.Offset) - 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: @@ -5370,6 +5475,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 @@ -5377,6 +5483,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)) @@ -5391,7 +5498,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) + 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...) @@ -5539,15 +5646,15 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) { } } -// putArg evaluates n for the purpose of passing it as an argument to a function and returns the corresponding Param and value for the call. -func (s *state) putArg(n ir.Node, t *types.Type, off int64) (ssa.Param, *ssa.Value) { +// putArg evaluates n for the purpose of passing it as an argument to a function and returns the value for the call. +func (s *state) putArg(n ir.Node, t *types.Type) *ssa.Value { var a *ssa.Value if !TypeOK(t) { a = s.newValue2(ssa.OpDereference, t, s.addr(n), s.mem()) } else { a = s.expr(n) } - return ssa.Param{Type: t, Offset: int32(off)}, a + return a } func (s *state) storeArgWithBase(n ir.Node, t *types.Type, base *ssa.Value, off int64) { @@ -6075,18 +6182,23 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val if base.Debug.TypeAssert > 0 { base.WarnfAt(n.Pos(), "type assertion not inlined") } - if n.X.Type().IsEmptyInterface() { - if commaok { - call := s.rtcall(ir.Syms.AssertE2I2, true, []*types.Type{n.Type(), types.Types[types.TBOOL]}, target, iface) - return call[0], call[1] + if !commaok { + fn := ir.Syms.AssertI2I + if n.X.Type().IsEmptyInterface() { + fn = ir.Syms.AssertE2I } - return s.rtcall(ir.Syms.AssertE2I, true, []*types.Type{n.Type()}, target, iface)[0], nil + data := s.newValue1(ssa.OpIData, types.Types[types.TUNSAFEPTR], iface) + tab := s.newValue1(ssa.OpITab, byteptr, iface) + tab = s.rtcall(fn, true, []*types.Type{byteptr}, target, tab)[0] + return s.newValue2(ssa.OpIMake, n.Type(), tab, data), nil } - if commaok { - call := s.rtcall(ir.Syms.AssertI2I2, true, []*types.Type{n.Type(), types.Types[types.TBOOL]}, target, iface) - return call[0], call[1] + fn := ir.Syms.AssertI2I2 + if n.X.Type().IsEmptyInterface() { + fn = ir.Syms.AssertE2I2 } - return s.rtcall(ir.Syms.AssertI2I, true, []*types.Type{n.Type()}, target, iface)[0], nil + res = s.rtcall(fn, true, []*types.Type{n.Type()}, target, iface)[0] + resok = s.newValue2(ssa.OpNeqInter, types.Types[types.TBOOL], res, s.constInterface(n.Type())) + return } if base.Debug.TypeAssert > 0 { @@ -6266,6 +6378,8 @@ type Branch struct { // State contains state needed during Prog generation. type State struct { + ABI obj.ABI + pp *objw.Progs // Branches remembers all the branch instructions we've seen @@ -6275,9 +6389,6 @@ type State struct { // bstart remembers where each block starts (indexed by block ID) bstart []*obj.Prog - // Some architectures require a 64-bit temporary for FP-related register shuffling. Examples include PPC and Sparc V8. - ScratchFpMem *ir.Name - maxarg int64 // largest frame size for arguments to calls made by the function // Map from GC safe points to liveness index, generated by @@ -6361,6 +6472,7 @@ func (s *State) DebugFriendlySetPosFrom(v *ssa.Value) { // genssa appends entries to pp for each instruction in f. func genssa(f *ssa.Func, pp *objw.Progs) { var s State + s.ABI = f.OwnAux.Fn.ABI() e := f.Frontend().(*ssafn) @@ -6390,8 +6502,6 @@ func genssa(f *ssa.Func, pp *objw.Progs) { progToBlock[s.pp.Next] = f.Blocks[0] } - s.ScratchFpMem = e.scratchFpMem - if base.Ctxt.Flag_locationlists { if cap(f.Cache.ValueToProgAfter) < f.NumValues() { f.Cache.ValueToProgAfter = make([]*obj.Prog, f.NumValues()) @@ -6442,14 +6552,20 @@ func genssa(f *ssa.Func, pp *objw.Progs) { x := s.pp.Next s.DebugFriendlySetPosFrom(v) + if v.Op.ResultInArg0() && v.ResultReg() != v.Args[0].Reg() { + v.Fatalf("input[0] and output not in same register %s", v.LongString()) + } + switch v.Op { case ssa.OpInitMem: // memory arg needs no code case ssa.OpArg: // input args need no code + case ssa.OpArgIntReg, ssa.OpArgFloatReg: + CheckArgReg(v) 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, @@ -6922,11 +7038,20 @@ func CheckLoweredPhi(v *ssa.Value) { // That register contains the closure pointer on closure entry. func CheckLoweredGetClosurePtr(v *ssa.Value) { entry := v.Block.Func.Entry + // TODO register args: not all the register-producing ops can come first. if entry != v.Block || entry.Values[0] != v { base.Fatalf("in %s, badly placed LoweredGetClosurePtr: %v %v", v.Block.Func.Name, v.Block, v) } } +// CheckArgReg ensures that v is in the function's entry block. +func CheckArgReg(v *ssa.Value) { + entry := v.Block.Func.Entry + if entry != v.Block { + base.Fatalf("in %s, badly placed ArgIReg or ArgFReg: %v %v", v.Block.Func.Name, v.Block, v) + } +} + func AddrAuto(a *obj.Addr, v *ssa.Value) { n, off := ssa.AutoVar(v) a.Type = obj.TYPE_MEM @@ -6940,17 +7065,6 @@ func AddrAuto(a *obj.Addr, v *ssa.Value) { } } -func (s *State) AddrScratch(a *obj.Addr) { - if s.ScratchFpMem == nil { - panic("no scratch memory available; forgot to declare usesScratch for Op?") - } - a.Type = obj.TYPE_MEM - a.Name = obj.NAME_AUTO - a.Sym = s.ScratchFpMem.Linksym() - a.Reg = int16(Arch.REGSP) - a.Offset = s.ScratchFpMem.Offset_ -} - // Call returns a new CALL instruction for the SSA value v. // It uses PrepareCall to prepare the call. func (s *State) Call(v *ssa.Value) *obj.Prog { @@ -7053,12 +7167,11 @@ func fieldIdx(n *ir.SelectorExpr) int { // ssafn holds frontend information about a function that the backend is processing. // It also exports a bunch of compiler services for the ssa backend. type ssafn struct { - curfn *ir.Func - strings map[string]*obj.LSym // map from constant string to data symbols - scratchFpMem *ir.Name // temp for floating point register / memory moves on some architectures - stksize int64 // stack size for current frame - stkptrsize int64 // prefix of stack containing pointers - log bool // print ssa debug to the stdout + curfn *ir.Func + strings map[string]*obj.LSym // map from constant string to data symbols + stksize int64 // stack size for current frame + stkptrsize int64 // prefix of stack containing pointers + log bool // print ssa debug to the stdout } // StringData returns a symbol which |