1
0
mirror of https://github.com/golang/go synced 2024-11-23 00:10:07 -07:00

[dev.typeparams] cmd/compile: remove variadic defer calls

Now that defer wrapping is used, deferred function is always
argumentless. Remove the code handling arguments.

This CL is mostly removing the fallback code path. There are more
cleanups to be done, in later CLs.

Change-Id: If6c729d3055c7a507cb1f1a000f5bbd3ad7ff235
Reviewed-on: https://go-review.googlesource.com/c/go/+/325914
Trust: Cherry Mui <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Cherry Mui 2021-06-04 16:32:03 -04:00
parent a9de78ac88
commit 5b350505da

View File

@ -359,31 +359,8 @@ func (s *state) emitOpenDeferInfo() {
r := s.openDefers[i] r := s.openDefers[i]
off = dvarint(x, off, r.n.X.Type().ArgWidth()) off = dvarint(x, off, r.n.X.Type().ArgWidth())
off = dvarint(x, off, -r.closureNode.FrameOffset()) off = dvarint(x, off, -r.closureNode.FrameOffset())
numArgs := len(r.argNodes) numArgs := 0
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)) 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)))
}
} }
} }
@ -864,16 +841,6 @@ type openDeferInfo struct {
// function, method, or interface call, to store a closure that panic // function, method, or interface call, to store a closure that panic
// processing can use for this defer. // processing can use for this defer.
closureNode *ir.Name 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 { type state struct {
@ -4686,17 +4653,14 @@ func (s *state) intrinsicArgs(n *ir.CallExpr) []*ssa.Value {
return args 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 // 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 // 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 // (as well as the deferBits variable), and this will enable us to run the proper
// defer calls during panics. // defer calls during panics.
func (s *state) openDeferRecord(n *ir.CallExpr) { func (s *state) openDeferRecord(n *ir.CallExpr) {
var args []*ssa.Value if len(n.Args) != 0 || n.Op() != ir.OCALLFUNC || n.X.Type().NumResults() != 0 {
var argNodes []*ir.Name
if len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0 {
s.Fatalf("defer call with arguments or results: %v", n) s.Fatalf("defer call with arguments or results: %v", n)
} }
@ -4704,48 +4668,20 @@ func (s *state) openDeferRecord(n *ir.CallExpr) {
n: n, n: n,
} }
fn := n.X fn := n.X
if n.Op() == ir.OCALLFUNC { // We must always store the function value in a stack slot for the
// 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
// runtime panic code to use. But in the defer exit code, we will // call the function directly if it is a static function.
// call the function directly if it is a static function. closureVal := s.expr(fn)
closureVal := s.expr(fn) closure := s.openDeferSave(nil, fn.Type(), closureVal)
closure := s.openDeferSave(nil, fn.Type(), closureVal) opendefer.closureNode = closure.Aux.(*ir.Name)
opendefer.closureNode = closure.Aux.(*ir.Name) if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) {
if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) { opendefer.closure = closure
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))
}
opendefer.argVals = args
opendefer.argNodes = argNodes
index := len(s.openDefers) index := len(s.openDefers)
s.openDefers = append(s.openDefers, opendefer) s.openDefers = append(s.openDefers, opendefer)
// Update deferBits only after evaluation and storage to stack of // 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)) 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) newDeferBits := s.newValue2(ssa.OpOr8, types.Types[types.TUINT8], s.variable(deferBitsVar, types.Types[types.TUINT8]), bitvalue)
s.vars[deferBitsVar] = newDeferBits s.vars[deferBitsVar] = newDeferBits
@ -4848,45 +4784,26 @@ func (s *state) openDeferExit() {
s.vars[deferBitsVar] = maskedval s.vars[deferBitsVar] = maskedval
// Generate code to call the function call of the defer, using the // Generate code to call the function call of the defer, using the
// closure/receiver/args that were stored in argtmps at the point // closure that were stored in argtmps at the point of the defer
// of the defer statement. // statement.
fn := r.n.X fn := r.n.X
stksize := fn.Type().ArgWidth() stksize := fn.Type().ArgWidth()
var ACArgs []*types.Type
var ACResults []*types.Type
var callArgs []*ssa.Value 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 var call *ssa.Value
if r.closure != nil { if r.closure != nil {
v := s.load(r.closure.Type.Elem(), r.closure) v := s.load(r.closure.Type.Elem(), r.closure)
s.maybeNilCheckClosure(v, callDefer) s.maybeNilCheckClosure(v, callDefer)
codeptr := s.rawLoad(types.Types[types.TUINTPTR], v) 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) call = s.newValue2A(ssa.OpClosureLECall, aux.LateExpansionResultType(), aux, codeptr, v)
} else { } 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) call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
} }
callArgs = append(callArgs, s.mem()) callArgs = append(callArgs, s.mem())
call.AddArgs(callArgs...) call.AddArgs(callArgs...)
call.AuxInt = stksize 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 // Make sure that the stack slots with pointers are kept live
// through the call (which is a pre-emption point). Also, we will // through the call (which is a pre-emption point). Also, we will
// use the first call of the last defer exit to compute liveness // use the first call of the last defer exit to compute liveness
@ -4894,16 +4811,6 @@ func (s *state) openDeferExit() {
if r.closureNode != nil { if r.closureNode != nil {
s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, r.closureNode, s.mem(), false) 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.endBlock()
s.startBlock(bEnd) s.startBlock(bEnd)
@ -5022,17 +4929,21 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
var call *ssa.Value var call *ssa.Value
if k == callDeferStack { if k == callDeferStack {
// Make a defer struct d on the stack. // 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) d := typecheck.TempAt(n.Pos(), s.curfn, t)
s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem()) s.vars[memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem())
addr := s.addr(d) addr := s.addr(d)
// Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer. // Must match deferstruct() below and src/runtime/runtime2.go:_defer.
// 0: siz // 0: siz
s.store(types.Types[types.TUINT32], s.store(types.Types[types.TUINT32],
s.newValue1I(ssa.OpOffPtr, types.Types[types.TUINT32].PtrTo(), t.FieldOff(0), addr), s.newValue1I(ssa.OpOffPtr, types.Types[types.TUINT32].PtrTo(), t.FieldOff(0), addr),
s.constInt32(types.Types[types.TUINT32], int32(stksize))) s.constInt32(types.Types[types.TUINT32], 0))
// 1: started, set in deferprocStack // 1: started, set in deferprocStack
// 2: heap, set in deferprocStack // 2: heap, set in deferprocStack
// 3: openDefer // 3: openDefer
@ -5048,39 +4959,13 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
// 10: varp // 10: varp
// 11: fd // 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
// 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)
}
// Set receiver (for method calls).
if n.Op() == ir.OCALLMETH {
base.Fatalf("OCALLMETH missed by walkCall")
}
// Set other args.
for _, f := range ft.Params().Fields().Slice() {
s.storeArgWithBase(args[0], f.Type, addr, off+abi.FieldOffsetOf(f))
args = args[1:]
}
// Call runtime.deferprocStack with pointer to _defer record. // Call runtime.deferprocStack with pointer to _defer record.
ACArgs = append(ACArgs, types.Types[types.TUINTPTR]) ACArgs = append(ACArgs, types.Types[types.TUINTPTR])
aux := ssa.StaticAuxCall(ir.Syms.DeferprocStack, s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults)) aux := ssa.StaticAuxCall(ir.Syms.DeferprocStack, s.f.ABIDefault.ABIAnalyzeTypes(nil, ACArgs, ACResults))
callArgs = append(callArgs, addr, s.mem()) callArgs = append(callArgs, addr, s.mem())
call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux) call = s.newValue0A(ssa.OpStaticLECall, aux.LateExpansionResultType(), aux)
call.AddArgs(callArgs...) call.AddArgs(callArgs...)
if stksize < int64(types.PtrSize) { call.AuxInt = int64(types.PtrSize) // deferprocStack takes a *_defer arg
// We need room for both the call to deferprocStack and the call to
// the deferred function.
// TODO(register args) Revisit this if/when we pass args in registers.
stksize = int64(types.PtrSize)
}
call.AuxInt = stksize
} else { } else {
// Store arguments to stack, including defer/go arguments and receiver for method calls. // Store arguments to stack, including defer/go arguments and receiver for method calls.
// These are written in SP-offset order. // These are written in SP-offset order.
@ -7689,9 +7574,8 @@ func max8(a, b int8) int8 {
return b return b
} }
// deferstruct makes a runtime._defer structure, with additional space for // deferstruct makes a runtime._defer structure.
// stksize bytes of args. func deferstruct() *types.Type {
func deferstruct(stksize int64) *types.Type {
makefield := func(name string, typ *types.Type) *types.Field { makefield := func(name string, typ *types.Type) *types.Field {
// Unlike the global makefield function, this one needs to set Pkg // Unlike the global makefield function, this one needs to set Pkg
// because these types might be compared (in SSA CSE sorting). // because these types might be compared (in SSA CSE sorting).
@ -7699,11 +7583,8 @@ func deferstruct(stksize int64) *types.Type {
sym := &types.Sym{Name: name, Pkg: types.LocalPkg} sym := &types.Sym{Name: name, Pkg: types.LocalPkg}
return types.NewField(src.NoXPos, sym, typ) 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 // 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{ fields := []*types.Field{
makefield("siz", types.Types[types.TUINT32]), makefield("siz", types.Types[types.TUINT32]),
makefield("started", types.Types[types.TBOOL]), makefield("started", types.Types[types.TBOOL]),
@ -7720,7 +7601,6 @@ func deferstruct(stksize int64) *types.Type {
makefield("framepc", types.Types[types.TUINTPTR]), makefield("framepc", types.Types[types.TUINTPTR]),
makefield("varp", types.Types[types.TUINTPTR]), makefield("varp", types.Types[types.TUINTPTR]),
makefield("fd", types.Types[types.TUINTPTR]), makefield("fd", types.Types[types.TUINTPTR]),
makefield("args", argtype),
} }
// build struct holding the above fields // build struct holding the above fields