Skip to content

Commit 44079f3

Browse files
committed
cmd/compile: keep closure pointer live for range body closures
For range-over-function, the compiler generates a hidden closure for the range body, and call the iterator function with the hidden closure as the yield parameter. For debuggers, if it stops inside the range body (hidden closure), it needs some way to find the outer function (that contains the range statement), to access the variables that are in scope. To do this, we keep the closure pointer live on stack with a special name ".closureptr", so the debugger can look for this name and find the closure pointer. In the usual case, the closure is a struct defined in the outer frame, so following the pointer it will find the frame. We do this in SSA generation, so if the range func is inlined and there is no actual closure, we don't generate any extra code. In the case that there is an actual closure, it's just a single store to the stack, so the overhead is still small. TODO: add some test Change-Id: I0e8219b895733f8943a13c67b03ca776bdc02bc9 Reviewed-on: https://go-review.googlesource.com/c/go/+/586975 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent 197101a commit 44079f3

File tree

1 file changed

+15
-0
lines changed
  • src/cmd/compile/internal/ssagen

1 file changed

+15
-0
lines changed

src/cmd/compile/internal/ssagen/ssa.go

+15
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,21 @@ func buildssa(fn *ir.Func, worker int, isPgoHot bool) *ssa.Func {
515515
// Populate closure variables.
516516
if fn.Needctxt() {
517517
clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr)
518+
if fn.RangeParent != nil {
519+
// For a range body closure, keep its closure pointer live on the
520+
// stack with a special name, so the debugger can look for it and
521+
// find the parent frame.
522+
sym := &types.Sym{Name: ".closureptr", Pkg: types.LocalPkg}
523+
cloSlot := s.curfn.NewLocal(src.NoXPos, sym, s.f.Config.Types.BytePtr)
524+
cloSlot.SetUsed(true)
525+
cloSlot.SetEsc(ir.EscNever)
526+
cloSlot.SetAddrtaken(true)
527+
s.vars[memVar] = s.newValue1Apos(ssa.OpVarDef, types.TypeMem, cloSlot, s.mem(), false)
528+
addr := s.addr(cloSlot)
529+
s.store(s.f.Config.Types.BytePtr, addr, clo)
530+
// Keep it from being dead-store eliminated.
531+
s.vars[memVar] = s.newValue1Apos(ssa.OpVarLive, types.TypeMem, cloSlot, s.mem(), false)
532+
}
518533
csiter := typecheck.NewClosureStructIter(fn.ClosureVars)
519534
for {
520535
n, typ, offset := csiter.Next()

0 commit comments

Comments
 (0)