From 3080b7d0af65858400b13134c1c471e2cb35e647 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 26 Apr 2018 21:20:41 -0400 Subject: [PATCH] runtime: unify fetching of locals and arguments maps Currently we have two nearly identical copies of the code that fetches the locals and arguments liveness maps for a frame, plus a third that's a poor knock-off. Unify these all into a single function. Change-Id: Ibce7926a0b0e3d23182112da4e25df899579a585 Reviewed-on: https://go-review.googlesource.com/109698 Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/runtime/mbitmap.go | 22 ++---- src/runtime/mgcmark.go | 79 +++------------------- src/runtime/stack.go | 149 ++++++++++++++++++++++++----------------- 3 files changed, 101 insertions(+), 149 deletions(-) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 5c7d812403..513e6b9eed 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -1999,30 +1999,16 @@ func getgcmask(ep interface{}) (mask []byte) { _g_ := getg() gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0) if frame.fn.valid() { - f := frame.fn - targetpc := frame.continpc - if targetpc == 0 { + locals, _ := getStackMap(&frame, nil, false) + if locals.n == 0 { return } - pcdata := int32(-1) // Use the entry map at function entry - if targetpc != f.entry { - targetpc-- - pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, nil) - } - if pcdata == -1 { - return - } - stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) - if stkmap == nil || stkmap.n <= 0 { - return - } - bv := stackmapdata(stkmap, pcdata) - size := uintptr(bv.n) * sys.PtrSize + size := uintptr(locals.n) * sys.PtrSize n := (*ptrtype)(unsafe.Pointer(t)).elem.size mask = make([]byte, n/sys.PtrSize) for i := uintptr(0); i < n; i += sys.PtrSize { off := (uintptr(p) + i - frame.varp + size) / sys.PtrSize - mask[i/sys.PtrSize] = bv.ptrbit(off) + mask[i/sys.PtrSize] = locals.ptrbit(off) } } return diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 12a232a005..e8cfdce4fc 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -791,82 +791,21 @@ func scanstack(gp *g, gcw *gcWork) { // Scan a stack frame: local variables and function arguments/results. //go:nowritebarrier func scanframeworker(frame *stkframe, cache *pcvalueCache, gcw *gcWork) { + if _DebugGC > 1 && frame.continpc != 0 { + print("scanframe ", funcname(frame.fn), "\n") + } - f := frame.fn - targetpc := frame.continpc - if targetpc == 0 { - // Frame is dead. - return - } - if _DebugGC > 1 { - print("scanframe ", funcname(f), "\n") - } - pcdata := int32(-1) - if targetpc != f.entry { - // Back up to the CALL. If we're at the function entry - // point, we want to use the entry map (-1), even if - // the first instruction of the function changes the - // stack map. - targetpc-- - pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, cache) - } - if pcdata == -1 { - // We do not have a valid pcdata value but there might be a - // stackmap for this function. It is likely that we are looking - // at the function prologue, assume so and hope for the best. - pcdata = 0 - } + locals, args := getStackMap(frame, cache, false) // Scan local variables if stack frame has been allocated. - size := frame.varp - frame.sp - var minsize uintptr - switch sys.ArchFamily { - case sys.ARM64: - minsize = sys.SpAlign - default: - minsize = sys.MinFrameSize - } - if size > minsize { - stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) - if stkmap == nil || stkmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") - throw("missing stackmap") - } - - // Locals bitmap information, scan just the pointers in locals. - if pcdata < 0 || pcdata >= stkmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stkmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n") - throw("scanframe: bad symbol table") - } - if stkmap.nbit > 0 { - bv := stackmapdata(stkmap, pcdata) - size = uintptr(bv.n) * sys.PtrSize - scanblock(frame.varp-size, size, bv.bytedata, gcw) - } + if locals.n > 0 { + size := uintptr(locals.n) * sys.PtrSize + scanblock(frame.varp-size, size, locals.bytedata, gcw) } // Scan arguments. - if frame.arglen > 0 { - var bv bitvector - if frame.argmap != nil { - bv = *frame.argmap - } else { - stkmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) - if stkmap == nil || stkmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(frame.arglen), "\n") - throw("missing stackmap") - } - if pcdata < 0 || pcdata >= stkmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stkmap.n, " args stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n") - throw("scanframe: bad symbol table") - } - bv = stackmapdata(stkmap, pcdata) - } - if bv.n > 0 { - scanblock(frame.argp, uintptr(bv.n)*sys.PtrSize, bv.bytedata, gcw) - } + if args.n > 0 { + scanblock(frame.argp, uintptr(args.n)*sys.PtrSize, args.bytedata, gcw) } } diff --git a/src/runtime/stack.go b/src/runtime/stack.go index 0813497ca7..cbe49355a9 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -606,8 +606,7 @@ func adjustpointers(scanp unsafe.Pointer, bv *bitvector, adjinfo *adjustinfo, f // Note: the argument/return area is adjusted by the callee. func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { adjinfo := (*adjustinfo)(arg) - targetpc := frame.continpc - if targetpc == 0 { + if frame.continpc == 0 { // Frame is dead. return true } @@ -621,47 +620,13 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { // have full GC info for it (because it is written in asm). return true } - pcdata := int32(-1) // Use the entry map at function entry - if targetpc != f.entry { - targetpc-- - pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, &adjinfo.cache) - } - if pcdata == -1 { - pcdata = 0 // in prologue - } + + locals, args := getStackMap(frame, &adjinfo.cache, true) // Adjust local variables if stack frame has been allocated. - size := frame.varp - frame.sp - var minsize uintptr - switch sys.ArchFamily { - case sys.ARM64: - minsize = sys.SpAlign - default: - minsize = sys.MinFrameSize - } - if size > minsize { - stackmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) - if stackmap == nil || stackmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") - throw("missing stackmap") - } - // If nbit == 0, there's no work to do. - if stackmap.nbit > 0 { - // Locals bitmap information, scan just the pointers in locals. - if pcdata < 0 || pcdata >= stackmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n") - throw("bad symbol table") - } - bv := stackmapdata(stackmap, pcdata) - size = uintptr(bv.n) * sys.PtrSize - if stackDebug >= 3 { - print(" locals ", pcdata, "/", stackmap.n, " ", size/sys.PtrSize, " words ", bv.bytedata, "\n") - } - adjustpointers(unsafe.Pointer(frame.varp-size), &bv, adjinfo, f) - } else if stackDebug >= 3 { - print(" no locals to adjust\n") - } + if locals.n > 0 { + size := uintptr(locals.n) * sys.PtrSize + adjustpointers(unsafe.Pointer(frame.varp-size), &locals, adjinfo, f) } // Adjust saved base pointer if there is one. @@ -688,29 +653,11 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool { } // Adjust arguments. - if frame.arglen > 0 { - var bv bitvector - if frame.argmap != nil { - bv = *frame.argmap - } else { - stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) - if stackmap == nil || stackmap.n <= 0 { - print("runtime: frame ", funcname(f), " untyped args ", frame.argp, "+", frame.arglen, "\n") - throw("missing stackmap") - } - if pcdata < 0 || pcdata >= stackmap.n { - // don't know where we are - print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", targetpc, ")\n") - throw("bad symbol table") - } - bv = stackmapdata(stackmap, pcdata) - } + if args.n > 0 { if stackDebug >= 3 { print(" args\n") } - if bv.n > 0 { - adjustpointers(unsafe.Pointer(frame.argp), &bv, adjinfo, funcInfo{}) - } + adjustpointers(unsafe.Pointer(frame.argp), &args, adjinfo, funcInfo{}) } return true } @@ -1186,6 +1133,86 @@ func freeStackSpans() { unlock(&stackLarge.lock) } +// getStackMap returns the locals and arguments live pointer maps for +// frame. +func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args bitvector) { + targetpc := frame.continpc + if targetpc == 0 { + // Frame is dead. Return empty bitvectors. + return + } + + f := frame.fn + pcdata := int32(-1) + if targetpc != f.entry { + // Back up to the CALL. If we're at the function entry + // point, we want to use the entry map (-1), even if + // the first instruction of the function changes the + // stack map. + targetpc-- + pcdata = pcdatavalue(f, _PCDATA_StackMapIndex, targetpc, cache) + } + if pcdata == -1 { + // We do not have a valid pcdata value but there might be a + // stackmap for this function. It is likely that we are looking + // at the function prologue, assume so and hope for the best. + pcdata = 0 + } + + // Local variables. + size := frame.varp - frame.sp + var minsize uintptr + switch sys.ArchFamily { + case sys.ARM64: + minsize = sys.SpAlign + default: + minsize = sys.MinFrameSize + } + if size > minsize { + stackmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps)) + if stackmap == nil || stackmap.n <= 0 { + print("runtime: frame ", funcname(f), " untyped locals ", hex(frame.varp-size), "+", hex(size), "\n") + throw("missing stackmap") + } + // If nbit == 0, there's no work to do. + if stackmap.nbit > 0 { + if pcdata < 0 || pcdata >= stackmap.n { + // don't know where we are + print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " locals stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") + throw("bad symbol table") + } + locals = stackmapdata(stackmap, pcdata) + if stackDebug >= 3 && debug { + print(" locals ", pcdata, "/", stackmap.n, " ", locals.n, " words ", locals.bytedata, "\n") + } + } else if stackDebug >= 3 && debug { + print(" no locals to adjust\n") + } + } + + // Arguments. + if frame.arglen > 0 { + if frame.argmap != nil { + args = *frame.argmap + } else { + stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps)) + if stackmap == nil || stackmap.n <= 0 { + print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(frame.arglen), "\n") + throw("missing stackmap") + } + if pcdata < 0 || pcdata >= stackmap.n { + // don't know where we are + print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n") + throw("bad symbol table") + } + if stackmap.nbit > 0 { + args = stackmapdata(stackmap, pcdata) + } + } + } + return +} + //go:nosplit func morestackc() { throw("attempt to execute system stack code on user stack")