]> Cypherpunks repositories - gostls13.git/commitdiff
Revert "Revert "cmd/compile,runtime: allocate defer records on the stack""
authorKeith Randall <khr@golang.org>
Sat, 8 Jun 2019 17:20:57 +0000 (17:20 +0000)
committerKeith Randall <khr@golang.org>
Mon, 10 Jun 2019 16:19:39 +0000 (16:19 +0000)
This reverts CL 180761

Reason for revert: Reinstate the stack-allocated defer CL.

There was nothing wrong with the CL proper, but stack allocation of defers exposed two other issues.

Issue #32477: Fix has been submitted as CL 181258.
Issue #32498: Possible fix is CL 181377 (not submitted yet).

Change-Id: I32b3365d5026600069291b068bbba6cb15295eb3
Reviewed-on: https://go-review.googlesource.com/c/go/+/181378
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
15 files changed:
src/cmd/compile/internal/gc/esc.go
src/cmd/compile/internal/gc/escape.go
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/reflect.go
src/cmd/compile/internal/gc/ssa.go
src/runtime/mgcmark.go
src/runtime/panic.go
src/runtime/runtime2.go
src/runtime/stack.go
src/runtime/stack_test.go
src/runtime/stubs.go
src/runtime/syscall_windows.go
src/runtime/traceback.go
test/codegen/stack.go
test/live.go

index ded9439a14cc4bc3ad467ef2c688a73a8166139c..c42f25e104651e33daba18415d69108de8eb0c16 100644 (file)
@@ -802,6 +802,7 @@ opSwitch:
 
        case ODEFER:
                if e.loopdepth == 1 { // top level
+                       n.Esc = EscNever // force stack allocation of defer record (see ssa.go)
                        break
                }
                // arguments leak out of scope
index 88dc9ef8a87d8b362c54d2c8cbdc5dbdba320cd6..47ce853858e978eb0e25de97c46d0b3bb681dc9a 100644 (file)
@@ -882,6 +882,7 @@ func (e *Escape) augmentParamHole(k EscHole, where *Node) EscHole {
        // non-transient location to avoid arguments from being
        // transiently allocated.
        if where.Op == ODEFER && e.loopDepth == 1 {
+               where.Esc = EscNever // force stack allocation of defer record (see ssa.go)
                // TODO(mdempsky): Eliminate redundant EscLocation allocs.
                return e.teeHole(k, e.newLoc(nil, false).asHole())
        }
index a776af9f66aa6c5540c07633d7d970c6bea382ec..f36e2716d667392afd79360a3c2c27fc74a4da36 100644 (file)
@@ -291,6 +291,7 @@ var (
        assertI2I,
        assertI2I2,
        deferproc,
+       deferprocStack,
        Deferreturn,
        Duffcopy,
        Duffzero,
index 04707b9ad20c3d9ab9f22afb6d843da0047e16b0..085481771332d0fbea6568c429a2261ca13652ec 100644 (file)
@@ -317,6 +317,48 @@ func hiter(t *types.Type) *types.Type {
        return hiter
 }
 
+// deferstruct makes a runtime._defer structure, with additional space for
+// stksize bytes of args.
+func deferstruct(stksize int64) *types.Type {
+       makefield := func(name string, typ *types.Type) *types.Field {
+               f := types.NewField()
+               f.Type = typ
+               // Unlike the global makefield function, this one needs to set Pkg
+               // because these types might be compared (in SSA CSE sorting).
+               // TODO: unify this makefield and the global one above.
+               f.Sym = &types.Sym{Name: name, Pkg: localpkg}
+               return f
+       }
+       argtype := types.NewArray(types.Types[TUINT8], stksize)
+       argtype.SetNoalg(true)
+       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.
+       fields := []*types.Field{
+               makefield("siz", types.Types[TUINT32]),
+               makefield("started", types.Types[TBOOL]),
+               makefield("heap", types.Types[TBOOL]),
+               makefield("sp", types.Types[TUINTPTR]),
+               makefield("pc", types.Types[TUINTPTR]),
+               // Note: the types here don't really matter. Defer structures
+               // are always scanned explicitly during stack copying and GC,
+               // so we make them uintptr type even though they are real pointers.
+               makefield("fn", types.Types[TUINTPTR]),
+               makefield("_panic", types.Types[TUINTPTR]),
+               makefield("link", types.Types[TUINTPTR]),
+               makefield("args", argtype),
+       }
+
+       // build struct holding the above fields
+       s := types.New(TSTRUCT)
+       s.SetNoalg(true)
+       s.SetFields(fields)
+       s.Width = widstruct(s, s, 0, 1)
+       s.Align = uint8(Widthptr)
+       return s
+}
+
 // f is method type, with receiver.
 // return function type, receiver as first argument (or not).
 func methodfunc(f *types.Type, receiver *types.Type) *types.Type {
index 4c9bcfe2a5c74653351e8fdc66aed8ca40d551b1..5509e3d18275a8289ff95b54d68d1a65199231bc 100644 (file)
@@ -68,6 +68,7 @@ func initssaconfig() {
        assertI2I = sysfunc("assertI2I")
        assertI2I2 = sysfunc("assertI2I2")
        deferproc = sysfunc("deferproc")
+       deferprocStack = sysfunc("deferprocStack")
        Deferreturn = sysfunc("deferreturn")
        Duffcopy = sysvar("duffcopy")             // asm func with special ABI
        Duffzero = sysvar("duffzero")             // asm func with special ABI
@@ -864,7 +865,11 @@ func (s *state) stmt(n *Node) {
                        }
                }
        case ODEFER:
-               s.call(n.Left, callDefer)
+               d := callDefer
+               if n.Esc == EscNever {
+                       d = callDeferStack
+               }
+               s.call(n.Left, d)
        case OGO:
                s.call(n.Left, callGo)
 
@@ -2859,6 +2864,7 @@ type callKind int8
 const (
        callNormal callKind = iota
        callDefer
+       callDeferStack
        callGo
 )
 
@@ -3799,74 +3805,132 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
                rcvr = s.newValue1(ssa.OpIData, types.Types[TUINTPTR], i)
        }
        dowidth(fn.Type)
-       stksize := fn.Type.ArgWidth() // includes receiver
+       stksize := fn.Type.ArgWidth() // includes receiver, args, and results
 
        // Run all assignments of temps.
        // The temps are introduced to avoid overwriting argument
        // slots when arguments themselves require function calls.
        s.stmtList(n.List)
 
-       // Store arguments to stack, including defer/go arguments and receiver for method calls.
-       // These are written in SP-offset order.
-       argStart := Ctxt.FixedFrameSize()
-       // Defer/go args.
-       if k != callNormal {
-               // Write argsize and closure (args to newproc/deferproc).
-               argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
-               addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
-               s.store(types.Types[TUINT32], addr, argsize)
-               addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
-               s.store(types.Types[TUINTPTR], addr, closure)
-               stksize += 2 * int64(Widthptr)
-               argStart += 2 * int64(Widthptr)
-       }
-
-       // Set receiver (for interface calls).
-       if rcvr != nil {
-               addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
-               s.store(types.Types[TUINTPTR], addr, rcvr)
-       }
-
-       // Write args.
-       t := n.Left.Type
-       args := n.Rlist.Slice()
-       if n.Op == OCALLMETH {
-               f := t.Recv()
-               s.storeArg(args[0], f.Type, argStart+f.Offset)
-               args = args[1:]
-       }
-       for i, n := range args {
-               f := t.Params().Field(i)
-               s.storeArg(n, f.Type, argStart+f.Offset)
-       }
-
-       // call target
        var call *ssa.Value
-       switch {
-       case k == callDefer:
-               call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem())
-       case k == callGo:
-               call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem())
-       case closure != nil:
-               // rawLoad because loading the code pointer from a
-               // closure is always safe, but IsSanitizerSafeAddr
-               // can't always figure that out currently, and it's
-               // critical that we not clobber any arguments already
-               // stored onto the stack.
-               codeptr = s.rawLoad(types.Types[TUINTPTR], closure)
-               call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem())
-       case codeptr != nil:
-               call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem())
-       case sym != nil:
-               call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem())
-       default:
-               Fatalf("bad call type %v %v", n.Op, n)
+       if k == callDeferStack {
+               // Make a defer struct d on the stack.
+               t := deferstruct(stksize)
+               d := tempAt(n.Pos, s.curfn, t)
+
+               s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, types.TypeMem, d, s.mem())
+               addr := s.addr(d, false)
+
+               // Must match reflect.go:deferstruct and src/runtime/runtime2.go:_defer.
+               // 0: siz
+               s.store(types.Types[TUINT32],
+                       s.newValue1I(ssa.OpOffPtr, types.Types[TUINT32].PtrTo(), t.FieldOff(0), addr),
+                       s.constInt32(types.Types[TUINT32], int32(stksize)))
+               // 1: started, set in deferprocStack
+               // 2: heap, set in deferprocStack
+               // 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(5), addr),
+                       closure)
+               // 6: panic, set in deferprocStack
+               // 7: link, set in deferprocStack
+
+               // Then, store all the arguments of the defer call.
+               ft := fn.Type
+               off := t.FieldOff(8)
+               args := n.Rlist.Slice()
+
+               // 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[TUINTPTR], p, rcvr)
+               }
+               // Set receiver (for method calls).
+               if n.Op == OCALLMETH {
+                       f := ft.Recv()
+                       s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
+                       args = args[1:]
+               }
+               // Set other args.
+               for _, f := range ft.Params().Fields().Slice() {
+                       s.storeArgWithBase(args[0], f.Type, addr, off+f.Offset)
+                       args = args[1:]
+               }
+
+               // Call runtime.deferprocStack with pointer to _defer record.
+               arg0 := s.constOffPtrSP(types.Types[TUINTPTR], Ctxt.FixedFrameSize())
+               s.store(types.Types[TUINTPTR], arg0, addr)
+               call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferprocStack, s.mem())
+               if stksize < int64(Widthptr) {
+                       // We need room for both the call to deferprocStack and the call to
+                       // the deferred function.
+                       stksize = int64(Widthptr)
+               }
+               call.AuxInt = stksize
+       } else {
+               // Store arguments to stack, including defer/go arguments and receiver for method calls.
+               // These are written in SP-offset order.
+               argStart := Ctxt.FixedFrameSize()
+               // Defer/go args.
+               if k != callNormal {
+                       // Write argsize and closure (args to newproc/deferproc).
+                       argsize := s.constInt32(types.Types[TUINT32], int32(stksize))
+                       addr := s.constOffPtrSP(s.f.Config.Types.UInt32Ptr, argStart)
+                       s.store(types.Types[TUINT32], addr, argsize)
+                       addr = s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart+int64(Widthptr))
+                       s.store(types.Types[TUINTPTR], addr, closure)
+                       stksize += 2 * int64(Widthptr)
+                       argStart += 2 * int64(Widthptr)
+               }
+
+               // Set receiver (for interface calls).
+               if rcvr != nil {
+                       addr := s.constOffPtrSP(s.f.Config.Types.UintptrPtr, argStart)
+                       s.store(types.Types[TUINTPTR], addr, rcvr)
+               }
+
+               // Write args.
+               t := n.Left.Type
+               args := n.Rlist.Slice()
+               if n.Op == OCALLMETH {
+                       f := t.Recv()
+                       s.storeArg(args[0], f.Type, argStart+f.Offset)
+                       args = args[1:]
+               }
+               for i, n := range args {
+                       f := t.Params().Field(i)
+                       s.storeArg(n, f.Type, argStart+f.Offset)
+               }
+
+               // call target
+               switch {
+               case k == callDefer:
+                       call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, deferproc, s.mem())
+               case k == callGo:
+                       call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem())
+               case closure != nil:
+                       // rawLoad because loading the code pointer from a
+                       // closure is always safe, but IsSanitizerSafeAddr
+                       // can't always figure that out currently, and it's
+                       // critical that we not clobber any arguments already
+                       // stored onto the stack.
+                       codeptr = s.rawLoad(types.Types[TUINTPTR], closure)
+                       call = s.newValue3(ssa.OpClosureCall, types.TypeMem, codeptr, closure, s.mem())
+               case codeptr != nil:
+                       call = s.newValue2(ssa.OpInterCall, types.TypeMem, codeptr, s.mem())
+               case sym != nil:
+                       call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, sym.Linksym(), s.mem())
+               default:
+                       Fatalf("bad call type %v %v", n.Op, n)
+               }
+               call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
        }
-       call.AuxInt = stksize // Call operations carry the argsize of the callee along with them
        s.vars[&memVar] = call
 
        // Finish block for defers
-       if k == callDefer {
+       if k == callDefer || k == callDeferStack {
                b := s.endBlock()
                b.Kind = ssa.BlockDefer
                b.SetControl(call)
@@ -4361,17 +4425,27 @@ func (s *state) storeTypePtrs(t *types.Type, left, right *ssa.Value) {
 }
 
 func (s *state) storeArg(n *Node, t *types.Type, off int64) {
+       s.storeArgWithBase(n, t, s.sp, off)
+}
+
+func (s *state) storeArgWithBase(n *Node, t *types.Type, base *ssa.Value, off int64) {
        pt := types.NewPtr(t)
-       sp := s.constOffPtrSP(pt, off)
+       var addr *ssa.Value
+       if base == s.sp {
+               // Use special routine that avoids allocation on duplicate offsets.
+               addr = s.constOffPtrSP(pt, off)
+       } else {
+               addr = s.newValue1I(ssa.OpOffPtr, pt, off, base)
+       }
 
        if !canSSAType(t) {
                a := s.addr(n, false)
-               s.move(t, sp, a)
+               s.move(t, addr, a)
                return
        }
 
        a := s.expr(n)
-       s.storeType(t, sp, a, 0, false)
+       s.storeType(t, addr, a, 0, false)
 }
 
 // slice computes the slice v[i:j:k] and returns ptr, len, and cap of result.
index efa007aa974c4e0f2320252a43a234d9115dca85..2c6372447277546bdbe5d74a26b17ab51eedd470 100644 (file)
@@ -712,15 +712,31 @@ func scanstack(gp *g, gcw *gcWork) {
 
        // Find additional pointers that point into the stack from the heap.
        // Currently this includes defers and panics. See also function copystack.
+
+       // Find and trace all defer arguments.
        tracebackdefers(gp, scanframe, nil)
+
+       // Find and trace other pointers in defer records.
        for d := gp._defer; d != nil; d = d.link {
-               // tracebackdefers above does not scan the func value, which could
-               // be a stack allocated closure. See issue 30453.
                if d.fn != nil {
+                       // tracebackdefers above does not scan the func value, which could
+                       // be a stack allocated closure. See issue 30453.
                        scanblock(uintptr(unsafe.Pointer(&d.fn)), sys.PtrSize, &oneptrmask[0], gcw, &state)
                }
+               if d.link != nil {
+                       // The link field of a stack-allocated defer record might point
+                       // to a heap-allocated defer record. Keep that heap record live.
+                       scanblock(uintptr(unsafe.Pointer(&d.link)), sys.PtrSize, &oneptrmask[0], gcw, &state)
+               }
+               // Retain defers records themselves.
+               // Defer records might not be reachable from the G through regular heap
+               // tracing because the defer linked list might weave between the stack and the heap.
+               if d.heap {
+                       scanblock(uintptr(unsafe.Pointer(&d)), sys.PtrSize, &oneptrmask[0], gcw, &state)
+               }
        }
        if gp._panic != nil {
+               // Panics are always stack allocated.
                state.putPtr(uintptr(unsafe.Pointer(gp._panic)))
        }
 
index f39a4bc0a25ca37500e72e91b212d44e8ac58a0f..ce26eb540d4a8acb556f2654b1fabd3a48197aeb 100644 (file)
@@ -228,6 +228,46 @@ func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
        // been set and must not be clobbered.
 }
 
+// deferprocStack queues a new deferred function with a defer record on the stack.
+// The defer record must have its siz and fn fields initialized.
+// All other fields can contain junk.
+// The defer record must be immediately followed in memory by
+// the arguments of the defer.
+// Nosplit because the arguments on the stack won't be scanned
+// until the defer record is spliced into the gp._defer list.
+//go:nosplit
+func deferprocStack(d *_defer) {
+       gp := getg()
+       if gp.m.curg != gp {
+               // go code on the system stack can't defer
+               throw("defer on system stack")
+       }
+       // siz and fn are already set.
+       // The other fields are junk on entry to deferprocStack and
+       // are initialized here.
+       d.started = false
+       d.heap = false
+       d.sp = getcallersp()
+       d.pc = getcallerpc()
+       // The lines below implement:
+       //   d.panic = nil
+       //   d.link = gp._defer
+       //   gp._defer = d
+       // But without write barriers. The first two are writes to
+       // the stack so they don't need a write barrier, and furthermore
+       // are to uninitialized memory, so they must not use a write barrier.
+       // The third write does not require a write barrier because we
+       // explicitly mark all the defer structures, so we don't need to
+       // keep track of pointers to them with a write barrier.
+       *(*uintptr)(unsafe.Pointer(&d._panic)) = 0
+       *(*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer))
+       *(*uintptr)(unsafe.Pointer(&gp._defer)) = uintptr(unsafe.Pointer(d))
+
+       return0()
+       // No code can go here - the C return register has
+       // been set and must not be clobbered.
+}
+
 // Small malloc size classes >= 16 are the multiples of 16: 16, 32, 48, 64, 80, 96, 112, 128, 144, ...
 // Each P holds a pool for defers with small arg sizes.
 // Assign defer allocations to pools by rounding to 16, to match malloc size classes.
@@ -349,6 +389,7 @@ func newdefer(siz int32) *_defer {
                }
        }
        d.siz = siz
+       d.heap = true
        d.link = gp._defer
        gp._defer = d
        return d
@@ -368,6 +409,9 @@ func freedefer(d *_defer) {
        if d.fn != nil {
                freedeferfn()
        }
+       if !d.heap {
+               return
+       }
        sc := deferclass(uintptr(d.siz))
        if sc >= uintptr(len(p{}.deferpool)) {
                return
index bc5b48222b11048ce8b959a1ec4db080693f2c97..16c02cd1edda2faed3eaf82e35f21282dfa69bf6 100644 (file)
@@ -775,9 +775,16 @@ func extendRandom(r []byte, n int) {
 
 // A _defer holds an entry on the list of deferred calls.
 // If you add a field here, add code to clear it in freedefer.
+// This struct must match the code in cmd/compile/internal/gc/reflect.go:deferstruct
+// and cmd/compile/internal/gc/ssa.go:(*state).call.
+// Some defers will be allocated on the stack and some on the heap.
+// All defers are logically part of the stack, so write barriers to
+// initialize them are not required. All defers must be manually scanned,
+// and for heap defers, marked.
 type _defer struct {
-       siz     int32
+       siz     int32 // includes both arguments and results
        started bool
+       heap    bool
        sp      uintptr // sp at time of defer
        pc      uintptr
        fn      *funcval
index 22a0053fdbe336dfeb21bc3ee99a9434837059f9..7ae3eeef83b9fa66adb02a1ff13af2c3b0c88ec0 100644 (file)
@@ -719,16 +719,21 @@ func adjustctxt(gp *g, adjinfo *adjustinfo) {
 }
 
 func adjustdefers(gp *g, adjinfo *adjustinfo) {
-       // Adjust defer argument blocks the same way we adjust active stack frames.
-       tracebackdefers(gp, adjustframe, noescape(unsafe.Pointer(adjinfo)))
-
        // Adjust pointers in the Defer structs.
-       // Defer structs themselves are never on the stack.
+       // We need to do this first because we need to adjust the
+       // defer.link fields so we always work on the new stack.
+       adjustpointer(adjinfo, unsafe.Pointer(&gp._defer))
        for d := gp._defer; d != nil; d = d.link {
                adjustpointer(adjinfo, unsafe.Pointer(&d.fn))
                adjustpointer(adjinfo, unsafe.Pointer(&d.sp))
                adjustpointer(adjinfo, unsafe.Pointer(&d._panic))
+               adjustpointer(adjinfo, unsafe.Pointer(&d.link))
        }
+
+       // Adjust defer argument blocks the same way we adjust active stack frames.
+       // Note: this code is after the loop above, so that if a defer record is
+       // stack allocated, we work on the copy in the new stack.
+       tracebackdefers(gp, adjustframe, noescape(unsafe.Pointer(adjinfo)))
 }
 
 func adjustpanics(gp *g, adjinfo *adjustinfo) {
index df73b3a1d5f99c794e84306d64f63fcc22c45a5a..143d3a99a0eba63d8b27ff5d7083bc13e5c65e1c 100644 (file)
@@ -799,3 +799,58 @@ func TestDeferLiveness(t *testing.T) {
                t.Errorf("output:\n%s\n\nwant no output", output)
        }
 }
+
+func TestDeferHeapAndStack(t *testing.T) {
+       P := 4     // processors
+       N := 10000 //iterations
+       D := 200   // stack depth
+
+       if testing.Short() {
+               P /= 2
+               N /= 10
+               D /= 10
+       }
+       c := make(chan bool)
+       for p := 0; p < P; p++ {
+               go func() {
+                       for i := 0; i < N; i++ {
+                               if deferHeapAndStack(D) != 2*D {
+                                       panic("bad result")
+                               }
+                       }
+                       c <- true
+               }()
+       }
+       for p := 0; p < P; p++ {
+               <-c
+       }
+}
+
+// deferHeapAndStack(n) computes 2*n
+func deferHeapAndStack(n int) (r int) {
+       if n == 0 {
+               return 0
+       }
+       if n%2 == 0 {
+               // heap-allocated defers
+               for i := 0; i < 2; i++ {
+                       defer func() {
+                               r++
+                       }()
+               }
+       } else {
+               // stack-allocated defers
+               defer func() {
+                       r++
+               }()
+               defer func() {
+                       r++
+               }()
+       }
+       r = deferHeapAndStack(n - 1)
+       escapeMe(new([1024]byte)) // force some GCs
+       return
+}
+
+// Pass a value to escapeMe to force it to escape.
+var escapeMe = func(x interface{}) {}
index 18e64dd5f7b7e623fd4457b8da2c192266aae345..26aaf2224d519feb1b4d4343635a5adb0bdf7259 100644 (file)
@@ -248,9 +248,6 @@ func getclosureptr() uintptr
 //go:noescape
 func asmcgocall(fn, arg unsafe.Pointer) int32
 
-// argp used in Defer structs when there is no argp.
-const _NoArgs = ^uintptr(0)
-
 func morestack()
 func morestack_noctxt()
 func rt0_go()
index 36ad7511af86b5ed8739770b35de95cd4bd049fc..722a73d108e7f3d647c77cad7d5b5a5983d8f9d0 100644 (file)
@@ -112,7 +112,6 @@ const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
 //go:nosplit
 func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {
        lockOSThread()
-       defer unlockOSThread()
        c := &getg().m.syscall
 
        if useLoadLibraryEx {
@@ -135,6 +134,7 @@ func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (hand
        if handle == 0 {
                err = c.err
        }
+       unlockOSThread() // not defer'd after the lockOSThread above to save stack frame size.
        return
 }
 
index d8170185014784512764f44f38d933bab7131f5b..ef48c9fa1f9b888acee43185599a3c2e30ed7b44 100644 (file)
@@ -148,11 +148,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
        waspanic := false
        cgoCtxt := gp.cgoCtxt
        printing := pcbuf == nil && callback == nil
-       _defer := gp._defer
-
-       for _defer != nil && _defer.sp == _NoArgs {
-               _defer = _defer.link
-       }
 
        // If the PC is zero, it's likely a nil function call.
        // Start in the caller's frame.
@@ -319,15 +314,14 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                // In the latter case, use a deferreturn call site as the continuation pc.
                frame.continpc = frame.pc
                if waspanic {
-                       // We match up defers with frames using the SP.
-                       // However, if the function has an empty stack
-                       // frame, then it's possible (on LR machines)
-                       // for multiple call frames to have the same
-                       // SP. But, since a function with no frame
-                       // can't push a defer, the defer can't belong
-                       // to that frame.
-                       if _defer != nil && _defer.sp == frame.sp && frame.sp != frame.fp {
+                       if frame.fn.deferreturn != 0 {
                                frame.continpc = frame.fn.entry + uintptr(frame.fn.deferreturn) + 1
+                               // Note: this may perhaps keep return variables alive longer than
+                               // strictly necessary, as we are using "function has a defer statement"
+                               // as a proxy for "function actually deferred something". It seems
+                               // to be a minor drawback. (We used to actually look through the
+                               // gp._defer for a defer corresponding to this function, but that
+                               // is hard to do with defer records on the stack during a stack copy.)
                                // Note: the +1 is to offset the -1 that
                                // stack.go:getStackMap does to back up a return
                                // address make sure the pc is in the CALL instruction.
@@ -336,11 +330,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                        }
                }
 
-               // Unwind our local defer stack past this frame.
-               for _defer != nil && ((_defer.sp == frame.sp && frame.sp != frame.fp) || _defer.sp == _NoArgs) {
-                       _defer = _defer.link
-               }
-
                if callback != nil {
                        if !callback((*stkframe)(noescape(unsafe.Pointer(&frame))), v) {
                                return n
@@ -510,13 +499,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                n = nprint
        }
 
-       // If callback != nil, we're being called to gather stack information during
-       // garbage collection or stack growth. In that context, require that we used
-       // up the entire defer stack. If not, then there is a bug somewhere and the
-       // garbage collection or stack growth may not have seen the correct picture
-       // of the stack. Crash now instead of silently executing the garbage collection
-       // or stack copy incorrectly and setting up for a mysterious crash later.
-       //
        // Note that panic != nil is okay here: there can be leftover panics,
        // because the defers on the panic stack do not nest in frame order as
        // they do on the defer stack. If you have:
@@ -557,16 +539,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
        // At other times, such as when gathering a stack for a profiling signal
        // or when printing a traceback during a crash, everything may not be
        // stopped nicely, and the stack walk may not be able to complete.
-       // It's okay in those situations not to use up the entire defer stack:
-       // incomplete information then is still better than nothing.
-       if callback != nil && n < max && _defer != nil {
-               print("runtime: g", gp.goid, ": leftover defer sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
-               for _defer = gp._defer; _defer != nil; _defer = _defer.link {
-                       print("\tdefer ", _defer, " sp=", hex(_defer.sp), " pc=", hex(_defer.pc), "\n")
-               }
-               throw("traceback has leftover defers")
-       }
-
        if callback != nil && n < max && frame.sp != gp.stktopsp {
                print("runtime: g", gp.goid, ": frame.sp=", hex(frame.sp), " top=", hex(gp.stktopsp), "\n")
                print("\tstack=[", hex(gp.stack.lo), "-", hex(gp.stack.hi), "] n=", n, " max=", max, "\n")
index ca3762228697f78e0ee0930f05119c1392b98ef7..37d378aa7877373e350d7512272166a17feddc48 100644 (file)
@@ -109,3 +109,8 @@ func MightPanic(a []int, i, j, k, s int) {
        _ = i << s   // panicShift
        _ = i / j    // panicDivide
 }
+
+func Defer() {
+       // amd64:`CALL\truntime\.deferprocStack`
+       defer func() {}()
+}
index e7134eca0cede8dfafb050ce434197569eb21d7e..ec51193725d100679b4b0324db62310d339e33eb 100644 (file)
@@ -687,7 +687,7 @@ type R struct{ *T } // ERRORAUTO "live at entry to \(\*R\)\.Foo: \.this ptr" "li
 // In particular, at printint r must be live.
 func f41(p, q *int) (r *int) { // ERROR "live at entry to f41: p q$"
        r = p
-       defer func() { // ERROR "live at call to deferproc: q r$" "live at call to deferreturn: r$"
+       defer func() { // ERROR "live at call to deferprocStack: q r$" "live at call to deferreturn: r$"
                recover()
        }()
        printint(0) // ERROR "live at call to printint: q r$"