diff options
Diffstat (limited to 'src/cmd/compile/internal/ssagen/ssa.go')
-rw-r--r-- | src/cmd/compile/internal/ssagen/ssa.go | 524 |
1 files changed, 199 insertions, 325 deletions
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index dfa76006de..b0f2585e3a 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -87,8 +87,7 @@ func InitConfig() { _ = types.NewPtr(types.Types[types.TINT64]) // *int64 _ = types.NewPtr(types.ErrorType) // *error types.NewPtrCacheEnabled = false - ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0) - ssaConfig.SoftFloat = Arch.SoftFloat + ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat) ssaConfig.Race = base.Flag.Race ssaCaches = make([]ssa.Cache, base.Flag.LowerC) @@ -279,18 +278,6 @@ func regAbiForFuncType(ft *types.Func) bool { return np > 0 && strings.Contains(ft.Params.FieldType(np-1).String(), magicLastTypeName) } -// 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 -// interface call. -func getParam(n *ir.CallExpr, i int) *types.Field { - t := n.X.Type() - if n.Op() == ir.OCALLMETH { - base.Fatalf("OCALLMETH missed by walkCall") - } - return t.Params().Field(i) -} - // dvarint writes a varint v to the funcdata in symbol x and returns the new offset func dvarint(x *obj.LSym, off int, v int64) int { if v < 0 || v > 1e9 { @@ -324,66 +311,21 @@ func dvarint(x *obj.LSym, off int, v int64) int { // for stack variables are specified as the number of bytes below varp (pointer to the // top of the local variables) for their starting address. The format is: // -// - Max total argument size among all the defers // - Offset of the deferBits variable // - Number of defers in the function // - Information about each defer call, in reverse order of appearance in the function: -// - Total argument size of the call // - Offset of the closure value to call -// - Number of arguments (including interface receiver or method receiver as first arg) -// - Information about each argument -// - Offset of the stored defer argument in this function's frame -// - Size of the argument -// - Offset of where argument should be placed in the args frame when making call func (s *state) emitOpenDeferInfo() { x := base.Ctxt.Lookup(s.curfn.LSym.Name + ".opendefer") s.curfn.LSym.Func().OpenCodedDeferInfo = x off := 0 - - // Compute maxargsize (max size of arguments for all defers) - // first, so we can output it first to the funcdata - var maxargsize int64 - for i := len(s.openDefers) - 1; i >= 0; i-- { - r := s.openDefers[i] - argsize := r.n.X.Type().ArgWidth() // TODO register args: but maybe use of abi0 will make this easy - if argsize > maxargsize { - maxargsize = argsize - } - } - off = dvarint(x, off, maxargsize) off = dvarint(x, off, -s.deferBitsTemp.FrameOffset()) off = dvarint(x, off, int64(len(s.openDefers))) // Write in reverse-order, for ease of running in that order at runtime for i := len(s.openDefers) - 1; i >= 0; i-- { r := s.openDefers[i] - off = dvarint(x, off, r.n.X.Type().ArgWidth()) off = dvarint(x, off, -r.closureNode.FrameOffset()) - numArgs := len(r.argNodes) - if r.rcvrNode != nil { - // If there's an interface receiver, treat/place it as the first - // arg. (If there is a method receiver, it's already included as - // first arg in r.argNodes.) - numArgs++ - } - off = dvarint(x, off, int64(numArgs)) - argAdjust := 0 // presence of receiver offsets the parameter count. - if r.rcvrNode != nil { - off = dvarint(x, off, -okOffset(r.rcvrNode.FrameOffset())) - off = dvarint(x, off, s.config.PtrSize) - off = dvarint(x, off, 0) // This is okay because defer records use ABI0 (for now) - argAdjust++ - } - - // 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, -okOffset(arg.FrameOffset())) - off = dvarint(x, off, f.Type.Size()) - off = dvarint(x, off, okOffset(pri.InParam(j+argAdjust).FrameOffset(pri))) - } } } @@ -580,7 +522,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { } // Populate closure variables. - if !fn.ClosureCalled() { + if fn.Needctxt() { clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr) offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field for _, n := range fn.ClosureVars { @@ -650,7 +592,6 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func { // it mimics the behavior of the former ABI (everything stored) and because it's not 100% // clear if naming conventions are respected in autogenerated code. // TODO figure out exactly what's unused, don't spill it. Make liveness fine-grained, also. - // TODO non-amd64 architectures have link registers etc that may require adjustment here. for _, p := range params.InParams() { typs, offs := p.RegisterTypesAndOffsets() for i, t := range typs { @@ -865,16 +806,6 @@ type openDeferInfo struct { // function, method, or interface call, to store a closure that panic // processing can use for this defer. closureNode *ir.Name - // If defer call is interface call, the address of the argtmp where the - // receiver is stored - rcvr *ssa.Value - // The node representing the argtmp where the receiver is stored - rcvrNode *ir.Name - // The addresses of the argtmps where the evaluated arguments of the defer - // function call are stored. - argVals []*ssa.Value - // The nodes representing the argtmps where the args of the defer are stored - argNodes []*ir.Name } type state struct { @@ -1491,7 +1422,12 @@ func (s *state) stmt(n ir.Node) { case ir.OAS2DOTTYPE: n := n.(*ir.AssignListStmt) - res, resok := s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true) + var res, resok *ssa.Value + if n.Rhs[0].Op() == ir.ODOTTYPE2 { + res, resok = s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true) + } else { + res, resok = s.dynamicDottype(n.Rhs[0].(*ir.DynamicTypeAssertExpr), true) + } deref := false if !TypeOK(n.Rhs[0].Type()) { if res.Op != ssa.OpLoad { @@ -2748,6 +2684,11 @@ func (s *state) expr(n ir.Node) *ssa.Value { res, _ := s.dottype(n, false) return res + case ir.ODYNAMICDOTTYPE: + n := n.(*ir.DynamicTypeAssertExpr) + res, _ := s.dynamicDottype(n, false) + return res + // binary ops case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT: n := n.(*ir.BinaryExpr) @@ -3183,7 +3124,7 @@ func (s *state) expr(n ir.Node) *ssa.Value { } fallthrough - case ir.OCALLINTER, ir.OCALLMETH: + case ir.OCALLINTER: n := n.(*ir.CallExpr) return s.callResult(n, callNormal) @@ -3191,6 +3132,14 @@ func (s *state) expr(n ir.Node) *ssa.Value { n := n.(*ir.CallExpr) return s.newValue1(ssa.OpGetG, n.Type(), s.mem()) + case ir.OGETCALLERPC: + n := n.(*ir.CallExpr) + return s.newValue0(ssa.OpGetCallerPC, n.Type()) + + case ir.OGETCALLERSP: + n := n.(*ir.CallExpr) + return s.newValue0(ssa.OpGetCallerSP, n.Type()) + case ir.OAPPEND: return s.append(n.(*ir.CallExpr), false) @@ -3703,6 +3652,16 @@ func softfloatInit() { // TODO: do not emit sfcall if operation can be optimized to constant in later // opt phase func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) { + f2i := func(t *types.Type) *types.Type { + switch t.Kind() { + case types.TFLOAT32: + return types.Types[types.TUINT32] + case types.TFLOAT64: + return types.Types[types.TUINT64] + } + return t + } + if callDef, ok := softFloatOps[op]; ok { switch op { case ssa.OpLess32F, @@ -3715,7 +3674,19 @@ func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) { args[1] = s.newValue1(s.ssaOp(ir.ONEG, types.Types[callDef.rtype]), args[1].Type, args[1]) } - result := s.rtcall(callDef.rtfn, true, []*types.Type{types.Types[callDef.rtype]}, args...)[0] + // runtime functions take uints for floats and returns uints. + // Convert to uints so we use the right calling convention. + for i, a := range args { + if a.Type.IsFloat() { + args[i] = s.newValue1(ssa.OpCopy, f2i(a.Type), a) + } + } + + rt := types.Types[callDef.rtype] + result := s.rtcall(callDef.rtfn, true, []*types.Type{f2i(rt)}, args...)[0] + if rt.IsFloat() { + result = s.newValue1(ssa.OpCopy, rt, result) + } if op == ssa.OpNeq32F || op == ssa.OpNeq64F { result = s.newValue1(ssa.OpNot, result.Type, result) } @@ -4687,17 +4658,14 @@ func (s *state) intrinsicArgs(n *ir.CallExpr) []*ssa.Value { return args } -// openDeferRecord adds code to evaluate and store the args for an open-code defer +// openDeferRecord adds code to evaluate and store the function for an open-code defer // call, and records info about the defer, so we can generate proper code on the // exit paths. n is the sub-node of the defer node that is the actual function -// call. We will also record funcdata information on where the args are stored +// call. We will also record funcdata information on where the function is stored // (as well as the deferBits variable), and this will enable us to run the proper // defer calls during panics. func (s *state) openDeferRecord(n *ir.CallExpr) { - var args []*ssa.Value - var argNodes []*ir.Name - - if buildcfg.Experiment.RegabiDefer && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) { + if len(n.Args) != 0 || n.Op() != ir.OCALLFUNC || n.X.Type().NumResults() != 0 { s.Fatalf("defer call with arguments or results: %v", n) } @@ -4705,48 +4673,20 @@ func (s *state) openDeferRecord(n *ir.CallExpr) { n: n, } fn := n.X - if n.Op() == ir.OCALLFUNC { - // We must always store the function value in a stack slot for the - // runtime panic code to use. But in the defer exit code, we will - // call the function directly if it is a static function. - closureVal := s.expr(fn) - closure := s.openDeferSave(nil, fn.Type(), closureVal) - opendefer.closureNode = closure.Aux.(*ir.Name) - if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) { - opendefer.closure = closure - } - } else if n.Op() == ir.OCALLMETH { - base.Fatalf("OCALLMETH missed by walkCall") - } else { - if fn.Op() != ir.ODOTINTER { - base.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op()) - } - fn := fn.(*ir.SelectorExpr) - closure, rcvr := s.getClosureAndRcvr(fn) - opendefer.closure = s.openDeferSave(nil, closure.Type, closure) - // Important to get the receiver type correct, so it is recognized - // as a pointer for GC purposes. - opendefer.rcvr = s.openDeferSave(nil, fn.Type().Recv().Type, rcvr) - opendefer.closureNode = opendefer.closure.Aux.(*ir.Name) - opendefer.rcvrNode = opendefer.rcvr.Aux.(*ir.Name) - } - for _, argn := range n.Args { - var v *ssa.Value - if TypeOK(argn.Type()) { - v = s.openDeferSave(nil, argn.Type(), s.expr(argn)) - } else { - v = s.openDeferSave(argn, argn.Type(), nil) - } - args = append(args, v) - argNodes = append(argNodes, v.Aux.(*ir.Name)) + // We must always store the function value in a stack slot for the + // runtime panic code to use. But in the defer exit code, we will + // call the function directly if it is a static function. + closureVal := s.expr(fn) + closure := s.openDeferSave(fn.Type(), closureVal) + opendefer.closureNode = closure.Aux.(*ir.Name) + if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) { + opendefer.closure = closure } - opendefer.argVals = args - opendefer.argNodes = argNodes index := len(s.openDefers) s.openDefers = append(s.openDefers, opendefer) // Update deferBits only after evaluation and storage to stack of - // args/receiver/interface is successful. + // the function is successful. bitvalue := s.constInt8(types.Types[types.TUINT8], 1<<uint(index)) newDeferBits := s.newValue2(ssa.OpOr8, types.Types[types.TUINT8], s.variable(deferBitsVar, types.Types[types.TUINT8]), bitvalue) s.vars[deferBitsVar] = newDeferBits @@ -4755,57 +4695,47 @@ func (s *state) openDeferRecord(n *ir.CallExpr) { // openDeferSave generates SSA nodes to store a value (with type t) for an // open-coded defer at an explicit autotmp location on the stack, so it can be -// reloaded and used for the appropriate call on exit. If type t is SSAable, then -// val must be non-nil (and n should be nil) and val is the value to be stored. If -// type t is non-SSAable, then n must be non-nil (and val should be nil) and n is -// evaluated (via s.addr() below) to get the value that is to be stored. The -// function returns an SSA value representing a pointer to the autotmp location. -func (s *state) openDeferSave(n ir.Node, t *types.Type, val *ssa.Value) *ssa.Value { - canSSA := TypeOK(t) - var pos src.XPos - if canSSA { - pos = val.Pos - } else { - pos = n.Pos() +// reloaded and used for the appropriate call on exit. Type t must be a function type +// (therefore SSAable). val is the value to be stored. The function returns an SSA +// value representing a pointer to the autotmp location. +func (s *state) openDeferSave(t *types.Type, val *ssa.Value) *ssa.Value { + if !TypeOK(t) { + s.Fatalf("openDeferSave of non-SSA-able type %v val=%v", t, val) } - argTemp := typecheck.TempAt(pos.WithNotStmt(), s.curfn, t) - argTemp.SetOpenDeferSlot(true) - var addrArgTemp *ssa.Value - // Use OpVarLive to make sure stack slots for the args, etc. are not - // removed by dead-store elimination + if !t.HasPointers() { + s.Fatalf("openDeferSave of pointerless type %v val=%v", t, val) + } + pos := val.Pos + temp := typecheck.TempAt(pos.WithNotStmt(), s.curfn, t) + temp.SetOpenDeferSlot(true) + var addrTemp *ssa.Value + // Use OpVarLive to make sure stack slot for the closure is not removed by + // dead-store elimination if s.curBlock.ID != s.f.Entry.ID { - // Force the argtmp storing this defer function/receiver/arg to be - // declared in the entry block, so that it will be live for the - // defer exit code (which will actually access it only if the - // associated defer call has been activated). - s.defvars[s.f.Entry.ID][memVar] = s.f.Entry.NewValue1A(src.NoXPos, ssa.OpVarDef, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][memVar]) - s.defvars[s.f.Entry.ID][memVar] = s.f.Entry.NewValue1A(src.NoXPos, ssa.OpVarLive, types.TypeMem, argTemp, s.defvars[s.f.Entry.ID][memVar]) - addrArgTemp = s.f.Entry.NewValue2A(src.NoXPos, ssa.OpLocalAddr, types.NewPtr(argTemp.Type()), argTemp, s.sp, s.defvars[s.f.Entry.ID][memVar]) + // Force the tmp storing this defer function to be declared in the entry + // block, so that it will be live for the defer exit code (which will + // actually access it only if the associated defer call has been activated). + s.defvars[s.f.Entry.ID][memVar] = s.f.Entry.NewValue1A(src.NoXPos, ssa.OpVarDef, types.TypeMem, temp, s.defvars[s.f.Entry.ID][memVar]) + s.defvars[s.f.Entry.ID][memVar] = s.f.Entry.NewValue1A(src.NoXPos, ssa.OpVarLive, types.TypeMem, temp, s.defvars[s.f.Entry.ID][memVar]) + addrTemp = s.f.Entry.NewValue2A(src.NoXPos, ssa.OpLocalAddr, types.NewPtr(temp.Type()), temp, s.sp, s.defvars[s.f.Entry.ID][memVar]) } else { // Special case if we're still in the entry block. We can't use // the above code, since s.defvars[s.f.Entry.ID] isn't defined // until we end the entry block with s.endBlock(). - s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, argTemp, s.mem(), false) - s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argTemp, s.mem(), false) - addrArgTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(argTemp.Type()), argTemp, s.sp, s.mem(), false) - } - if t.HasPointers() { - // Since we may use this argTemp during exit depending on the - // deferBits, we must define it unconditionally on entry. - // Therefore, we must make sure it is zeroed out in the entry - // block if it contains pointers, else GC may wrongly follow an - // uninitialized pointer value. - argTemp.SetNeedzero(true) - } - if !canSSA { - a := s.addr(n) - s.move(t, addrArgTemp, a) - return addrArgTemp - } + s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, temp, s.mem(), false) + s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, temp, s.mem(), false) + addrTemp = s.newValue2Apos(ssa.OpLocalAddr, types.NewPtr(temp.Type()), temp, s.sp, s.mem(), false) + } + // Since we may use this temp during exit depending on the + // deferBits, we must define it unconditionally on entry. + // Therefore, we must make sure it is zeroed out in the entry + // block if it contains pointers, else GC may wrongly follow an + // uninitialized pointer value. + temp.SetNeedzero(true) // We are storing to the stack, hence we can avoid the full checks in // storeType() (no write barrier) and do a simple store(). - s.store(t, addrArgTemp, val) - return addrArgTemp + s.store(t, addrTemp, val) + return addrTemp } // openDeferExit generates SSA for processing all the open coded defers at exit. @@ -4849,45 +4779,26 @@ func (s *state) openDeferExit() { s.vars[deferBitsVar] = maskedval // Generate code to call the function call of the defer, using the - // closure/receiver/args that were stored in argtmps at the point - // of the defer statement. + // closure that were stored in argtmps at the point of the defer + // statement. fn := r.n.X stksize := fn.Type().ArgWidth() - var ACArgs []*types.Type - var ACResults []*types.Type var callArgs []*ssa.Value - if r.rcvr != nil { - // rcvr in case of OCALLINTER - v := s.load(r.rcvr.Type.Elem(), r.rcvr) - ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) - callArgs = append(callArgs, v) - } - for j, argAddrVal := range r.argVals { - f := getParam(r.n, j) - ACArgs = append(ACArgs, f.Type) - var a *ssa.Value - if !TypeOK(f.Type) { - a = s.newValue2(ssa.OpDereference, f.Type, argAddrVal, s.mem()) - } else { - a = s.load(f.Type, argAddrVal) - } - callArgs = append(callArgs, a) - } var call *ssa.Value if r.closure != nil { v := s.load(r.closure.Type.Elem(), r.closure) s.maybeNilCheckClosure(v, callDefer) codeptr := s.rawLoad(types.Types[types.TUINTPTR], v) - aux := ssa.ClosureAuxCall(s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults)) + aux := ssa.ClosureAuxCall(s.f.ABIDefault.ABIAnalyzeTypes(nil, nil, nil)) call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v) } else { - aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults)) + aux := ssa.StaticAuxCall(fn.(*ir.Name).Linksym(), s.f.ABIDefault.ABIAnalyzeTypes(nil, nil, nil)) call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) } callArgs = append(callArgs, s.mem()) call.AddArgs(callArgs...) call.AuxInt = stksize - s.vars[memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, int64(len(ACResults)), call) + s.vars[memVar] = s.newValue1I(ssa.OpSelectN, types.TypeMem, 0, call) // Make sure that the stack slots with pointers are kept live // through the call (which is a pre-emption point). Also, we will // use the first call of the last defer exit to compute liveness @@ -4895,16 +4806,6 @@ func (s *state) openDeferExit() { if r.closureNode != nil { s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false) } - if r.rcvrNode != nil { - if r.rcvrNode.Type().HasPointers() { - s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.rcvrNode, s.mem(), false) - } - } - for _, argNode := range r.argNodes { - if argNode.Type().HasPointers() { - s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, argNode, s.mem(), false) - } - } s.endBlock() s.startBlock(bEnd) @@ -4952,7 +4853,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val } } - if buildcfg.Experiment.RegabiDefer && k != callNormal && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) { + if k != callNormal && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) { s.Fatalf("go/defer call with arguments: %v", n) } @@ -4986,8 +4887,6 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val // not the point of defer statement. s.maybeNilCheckClosure(closure, k) } - case ir.OCALLMETH: - base.Fatalf("OCALLMETH missed by walkCall") case ir.OCALLINTER: if fn.Op() != ir.ODOTINTER { s.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op()) @@ -5023,55 +4922,31 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val var call *ssa.Value if k == callDeferStack { // Make a defer struct d on the stack. - t := deferstruct(stksize) + if stksize != 0 { + s.Fatalf("deferprocStack with non-zero stack size %d: %v", stksize, n) + } + + t := deferstruct() d := typecheck.TempAt(n.Pos(), s.curfn, t) s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem()) addr := s.addr(d) - // Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer. - // 0: siz - s.store(types.Types[types.TUINT32], - s.newValue1I(ssa.OpOffPtr, types.Types[types.TUINT32].PtrTo(), t.FieldOff(0), addr), - s.constInt32(types.Types[types.TUINT32], int32(stksize))) - // 1: started, set in deferprocStack - // 2: heap, set in deferprocStack - // 3: openDefer - // 4: sp, set in deferprocStack - // 5: pc, set in deferprocStack - // 6: fn + // Must match deferstruct() below and src/runtime/runtime2.go:_defer. + // 0: started, set in deferprocStack + // 1: heap, set in deferprocStack + // 2: openDefer + // 3: sp, set in deferprocStack + // 4: pc, set in deferprocStack + // 5: fn s.store(closure.Type, - s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(6), addr), + s.newValue1I(ssa.OpOffPtr, closure.Type.PtrTo(), t.FieldOff(5), addr), closure) - // 7: panic, set in deferprocStack - // 8: link, set in deferprocStack - // 9: framepc - // 10: varp - // 11: fd - - // Then, store all the arguments of the defer call. - ft := fn.Type() - off := t.FieldOff(12) // TODO register args: be sure this isn't a hardcoded param stack offset. - args := n.Args - i0 := 0 - - // Set receiver (for interface calls). Always a pointer. - if rcvr != nil { - p := s.newValue1I(ssa.OpOffPtr, ft.Recv().Type.PtrTo(), off, addr) - s.store(types.Types[types.TUINTPTR], p, rcvr) - i0 = 1 - } - // Set receiver (for method calls). - if n.Op() == ir.OCALLMETH { - base.Fatalf("OCALLMETH missed by walkCall") - } - // Set other args. - // This code is only used when RegabiDefer is not enabled, and arguments are always - // passed on stack. - for i, f := range ft.Params().Fields().Slice() { - s.storeArgWithBase(args[0], f.Type, addr, off+params.InParam(i+i0).FrameOffset(params)) - args = args[1:] - } + // 6: panic, set in deferprocStack + // 7: link, set in deferprocStack + // 8: fd + // 9: varp + // 10: framepc // Call runtime.deferprocStack with pointer to _defer record. ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) @@ -5079,26 +4954,18 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val 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. - stksize = int64(types.PtrSize) - } - call.AuxInt = stksize + call.AuxInt = int64(types.PtrSize) // deferprocStack takes a *_defer arg } else { // Store arguments to stack, including defer/go arguments and receiver for method calls. // These are written in SP-offset order. argStart := base.Ctxt.FixedFrameSize() // 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, types.Types[types.TUINT32]) // not argExtra - callArgs = append(callArgs, argsize) - ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) + // Write closure (arg to newproc/deferproc). + ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) // not argExtra callArgs = append(callArgs, closure) - stksize += 2 * int64(types.PtrSize) - argStart += 2 * int64(types.PtrSize) + stksize += int64(types.PtrSize) + argStart += int64(types.PtrSize) } // Set receiver (for interface calls). @@ -5109,9 +4976,6 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val // Write args. t := n.X.Type() args := n.Args - if n.Op() == ir.OCALLMETH { - base.Fatalf("OCALLMETH missed by walkCall") - } for _, p := range params.InParams() { // includes receiver for interface calls ACArgs = append(ACArgs, p.Type) @@ -5314,9 +5178,13 @@ func (s *state) addr(n ir.Node) *ssa.Value { case ir.OCALLFUNC, ir.OCALLINTER: n := n.(*ir.CallExpr) return s.callAddr(n, callNormal) - case ir.ODOTTYPE: - n := n.(*ir.TypeAssertExpr) - v, _ := s.dottype(n, false) + case ir.ODOTTYPE, ir.ODYNAMICDOTTYPE: + var v *ssa.Value + if n.Op() == ir.ODOTTYPE { + v, _ = s.dottype(n.(*ir.TypeAssertExpr), false) + } else { + v, _ = s.dynamicDottype(n.(*ir.DynamicTypeAssertExpr), false) + } if v.Op != ssa.OpLoad { s.Fatalf("dottype of non-load") } @@ -6210,14 +6078,38 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n ir.Node, x *ssa.Value, ft, tt * func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Value) { iface := s.expr(n.X) // input interface target := s.reflectType(n.Type()) // target type - byteptr := s.f.Config.Types.BytePtr + var targetItab *ssa.Value + if n.Itab != nil { + targetItab = s.expr(n.Itab) + } + return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, targetItab, commaok) +} + +func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res, resok *ssa.Value) { + iface := s.expr(n.X) + target := s.expr(n.T) + var itab *ssa.Value + if !n.X.Type().IsEmptyInterface() && !n.Type().IsInterface() { + byteptr := s.f.Config.Types.BytePtr + itab = target + target = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)) // itab.typ + } + return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, itab, commaok) +} - if n.Type().IsInterface() { - if n.Type().IsEmptyInterface() { +// dottype1 implements a x.(T) operation. iface is the argument (x), dst is the type we're asserting to (T) +// and src is the type we're asserting from. +// target is the *runtime._type of dst. +// If src is a nonempty interface and dst is not an interface, targetItab is an itab representing (dst, src). Otherwise it is nil. +// commaok is true if the caller wants a boolean success value. Otherwise, the generated code panics if the conversion fails. +func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) { + byteptr := s.f.Config.Types.BytePtr + if dst.IsInterface() { + if dst.IsEmptyInterface() { // Converting to an empty interface. // Input could be an empty or nonempty interface. if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion inlined") + base.WarnfAt(pos, "type assertion inlined") } // Get itab/type field from input. @@ -6225,7 +6117,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // Conversion succeeds iff that field is not nil. cond := s.newValue2(ssa.OpNeqPtr, types.Types[types.TBOOL], itab, s.constNil(byteptr)) - if n.X.Type().IsEmptyInterface() && commaok { + if src.IsEmptyInterface() && commaok { // Converting empty interface to empty interface with ,ok is just a nil check. return iface, cond } @@ -6247,7 +6139,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // On success, return (perhaps modified) input interface. s.startBlock(bOk) - if n.X.Type().IsEmptyInterface() { + if src.IsEmptyInterface() { res = iface // Use input interface unchanged. return } @@ -6255,7 +6147,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab) typ := s.load(byteptr, off) idata := s.newValue1(ssa.OpIData, byteptr, iface) - res = s.newValue2(ssa.OpIMake, n.Type(), typ, idata) + res = s.newValue2(ssa.OpIMake, dst, typ, idata) return } @@ -6277,62 +6169,62 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val bFail.AddEdgeTo(bEnd) s.startBlock(bEnd) idata := s.newValue1(ssa.OpIData, byteptr, iface) - res = s.newValue2(ssa.OpIMake, n.Type(), s.variable(typVar, byteptr), idata) + res = s.newValue2(ssa.OpIMake, dst, s.variable(typVar, byteptr), idata) resok = cond delete(s.vars, typVar) return } // converting to a nonempty interface needs a runtime call. if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion not inlined") + base.WarnfAt(pos, "type assertion not inlined") } if !commaok { fn := ir.Syms.AssertI2I - if n.X.Type().IsEmptyInterface() { + if src.IsEmptyInterface() { fn = ir.Syms.AssertE2I } 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 + return s.newValue2(ssa.OpIMake, dst, tab, data), nil } fn := ir.Syms.AssertI2I2 - if n.X.Type().IsEmptyInterface() { + if src.IsEmptyInterface() { fn = ir.Syms.AssertE2I2 } - 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())) + res = s.rtcall(fn, true, []*types.Type{dst}, target, iface)[0] + resok = s.newValue2(ssa.OpNeqInter, types.Types[types.TBOOL], res, s.constInterface(dst)) return } if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion inlined") + base.WarnfAt(pos, "type assertion inlined") } // Converting to a concrete type. - direct := types.IsDirectIface(n.Type()) + direct := types.IsDirectIface(dst) itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface if base.Debug.TypeAssert > 0 { - base.WarnfAt(n.Pos(), "type assertion inlined") + base.WarnfAt(pos, "type assertion inlined") } - var targetITab *ssa.Value - if n.X.Type().IsEmptyInterface() { + var wantedFirstWord *ssa.Value + if src.IsEmptyInterface() { // Looking for pointer to target type. - targetITab = target + wantedFirstWord = target } else { // Looking for pointer to itab for target type and source interface. - targetITab = s.expr(n.Itab) + wantedFirstWord = targetItab } var tmp ir.Node // temporary for use with large types var addr *ssa.Value // address of tmp - if commaok && !TypeOK(n.Type()) { + if commaok && !TypeOK(dst) { // unSSAable type, use temporary. // TODO: get rid of some of these temporaries. - tmp, addr = s.temp(n.Pos(), n.Type()) + tmp, addr = s.temp(pos, dst) } - cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, targetITab) + cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, wantedFirstWord) b := s.endBlock() b.Kind = ssa.BlockIf b.SetControl(cond) @@ -6346,8 +6238,8 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val if !commaok { // on failure, panic by calling panicdottype s.startBlock(bFail) - taddr := s.reflectType(n.X.Type()) - if n.X.Type().IsEmptyInterface() { + taddr := s.reflectType(src) + if src.IsEmptyInterface() { s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr) } else { s.rtcall(ir.Syms.PanicdottypeI, false, nil, itab, target, taddr) @@ -6356,10 +6248,10 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // on success, return data from interface s.startBlock(bOk) if direct { - return s.newValue1(ssa.OpIData, n.Type(), iface), nil + return s.newValue1(ssa.OpIData, dst, iface), nil } - p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface) - return s.load(n.Type(), p), nil + p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface) + return s.load(dst, p), nil } // commaok is the more complicated case because we have @@ -6373,14 +6265,14 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val s.startBlock(bOk) if tmp == nil { if direct { - s.vars[valVar] = s.newValue1(ssa.OpIData, n.Type(), iface) + s.vars[valVar] = s.newValue1(ssa.OpIData, dst, iface) } else { - p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface) - s.vars[valVar] = s.load(n.Type(), p) + p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface) + s.vars[valVar] = s.load(dst, p) } } else { - p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface) - s.move(n.Type(), addr, p) + p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface) + s.move(dst, addr, p) } s.vars[okVar] = s.constBool(true) s.endBlock() @@ -6389,9 +6281,9 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // type assertion failed s.startBlock(bFail) if tmp == nil { - s.vars[valVar] = s.zeroVal(n.Type()) + s.vars[valVar] = s.zeroVal(dst) } else { - s.zero(n.Type(), addr) + s.zero(dst, addr) } s.vars[okVar] = s.constBool(false) s.endBlock() @@ -6400,10 +6292,10 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val // merge point s.startBlock(bEnd) if tmp == nil { - res = s.variable(valVar, n.Type()) + res = s.variable(valVar, dst) delete(s.vars, valVar) } else { - res = s.load(n.Type(), addr) + res = s.load(dst, addr) s.vars[memVar] = s.newValue1A(ssa.OpVarKill, types.TypeMem, tmp.(*ir.Name), s.mem()) } resok = s.variable(okVar, types.Types[types.TBOOL]) @@ -6921,8 +6813,12 @@ func genssa(f *ssa.Func, pp *objw.Progs) { // recovers a panic, it will return to caller with right results. // The results are already in memory, because they are not SSA'd // when the function has defers (see canSSAName). - if f.OwnAux.ABIInfo().OutRegistersUsed() != 0 { - Arch.LoadRegResults(&s, f) + for _, o := range f.OwnAux.ABIInfo().OutParams() { + n := o.Name.(*ir.Name) + rts, offs := o.RegisterTypesAndOffsets() + for i := range o.Registers { + Arch.LoadRegResult(&s, f, rts[i], ssa.ObjRegForAbiReg(o.Registers[i], f.Config), n, offs[i]) + } } pp.Prog(obj.ARET) @@ -7460,18 +7356,6 @@ func (s *State) PrepareCall(v *ssa.Value) { call, ok := v.Aux.(*ssa.AuxCall) - if ok && call.Fn == ir.Syms.Deferreturn { - // Deferred calls will appear to be returning to - // the CALL deferreturn(SB) that we are about to emit. - // However, the stack trace code will show the line - // of the instruction byte before the return PC. - // To avoid that being an unrelated instruction, - // insert an actual hardware NOP that will have the right line number. - // This is different from obj.ANOP, which is a virtual no-op - // that doesn't make it into the instruction stream. - Arch.Ginsnopdefer(s.pp) - } - if ok { // Record call graph information for nowritebarrierrec // analysis. @@ -7542,10 +7426,6 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name { return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list } -func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym { - return reflectdata.ITabSym(it, offset) -} - // SplitSlot returns a slot representing the data of parent starting at offset. func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot { node := parent.N @@ -7676,9 +7556,8 @@ func max8(a, b int8) int8 { return b } -// deferstruct makes a runtime._defer structure, with additional space for -// stksize bytes of args. -func deferstruct(stksize int64) *types.Type { +// deferstruct makes a runtime._defer structure. +func deferstruct() *types.Type { makefield := func(name string, typ *types.Type) *types.Field { // Unlike the global makefield function, this one needs to set Pkg // because these types might be compared (in SSA CSE sorting). @@ -7686,13 +7565,9 @@ func deferstruct(stksize int64) *types.Type { sym := &types.Sym{Name: name, Pkg: types.LocalPkg} return types.NewField(src.NoXPos, sym, typ) } - argtype := types.NewArray(types.Types[types.TUINT8], stksize) - argtype.Width = stksize - argtype.Align = 1 // These fields must match the ones in runtime/runtime2.go:_defer and - // cmd/compile/internal/gc/ssa.go:(*state).call. + // (*state).call above. fields := []*types.Field{ - makefield("siz", types.Types[types.TUINT32]), makefield("started", types.Types[types.TBOOL]), makefield("heap", types.Types[types.TBOOL]), makefield("openDefer", types.Types[types.TBOOL]), @@ -7704,10 +7579,9 @@ func deferstruct(stksize int64) *types.Type { makefield("fn", types.Types[types.TUINTPTR]), makefield("_panic", types.Types[types.TUINTPTR]), makefield("link", types.Types[types.TUINTPTR]), - makefield("framepc", types.Types[types.TUINTPTR]), - makefield("varp", types.Types[types.TUINTPTR]), makefield("fd", types.Types[types.TUINTPTR]), - makefield("args", argtype), + makefield("varp", types.Types[types.TUINTPTR]), + makefield("framepc", types.Types[types.TUINTPTR]), } // build struct holding the above fields |