diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index f2591c362a..64af26bf29 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -142,20 +142,78 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { hs.SetTypecheck(1) } - // Pointer to current iteration position - hp := typecheck.Temp(types.NewPtr(elem)) - init = append(init, ir.NewAssignStmt(base.Pos, hp, ir.NewUnaryExpr(base.Pos, ir.OSPTR, hs))) + // We use a "pointer" to keep track of where we are in the backing array + // of the slice hs. This pointer starts at hs.ptr and gets incremented + // by the element size each time through the loop. + // + // It's tricky, though, as on the last iteration this pointer gets + // incremented to point past the end of the backing array. We can't + // let the garbage collector see that final out-of-bounds pointer. + // + // To avoid this, we keep the "pointer" alternately in 2 variables, one + // pointer typed and one uintptr typed. Most of the time it lives in the + // regular pointer variable, but when it might be out of bounds (after it + // has been incremented, but before the loop condition has been checked) + // it lives briefly in the uintptr variable. + // + // hp contains the pointer version (of type *T, where T is the element type). + // It is guaranteed to always be in range, keeps the backing store alive, + // and is updated on stack copies. If a GC occurs when this function is + // suspended at any safepoint, this variable ensures correct operation. + // + // hu contains the equivalent uintptr version. It may point past the + // end, but doesn't keep the backing store alive and doesn't get updated + // on a stack copy. If a GC occurs while this function is on the top of + // the stack, then the last frame is scanned conservatively and hu will + // act as a reference to the backing array to ensure it is not collected. + // + // The "pointer" we're moving across the backing array lives in one + // or the other of hp and hu as the loop proceeds. + // + // hp is live during most of the body of the loop. But it isn't live + // at the very top of the loop, when we haven't checked i