runtime: avoid repeated findmoduledatap calls

Currently almost every function that deals with a *_func has to first
look up the *moduledata for the module containing the function's entry
point. This means we almost always do at least two identical module
lookups whenever we deal with a *_func (one to get the *_func and
another to get something from its module data) and sometimes several
more.

Fix this by making findfunc return a new funcInfo type that embeds
*_func, but also includes the *moduledata, and making all of the
functions that currently take a *_func instead take a funcInfo and use
the already-found *moduledata.

This transformation is trivial for the most part, since the *_func
type is usually inferred. The annoying part is that we can no longer
use nil to indicate failure, so this introduces a funcInfo.valid()
method and replaces nil checks with calls to valid.

Change-Id: I9b8075ef1c31185c1943596d96dec45c7ab5100f
Reviewed-on: https://go-review.googlesource.com/37331
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Hudson-Doyle <michael.hudson@canonical.com>
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index ddcb81f..896bfc4 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -174,7 +174,7 @@
 		return
 	}
 	f := findfunc(rpc[1])
-	if f == nil {
+	if !f.valid() {
 		// TODO(rsc): Probably a bug?
 		// The C version said "have retpc at least"
 		// but actually returned pc=0.
@@ -187,7 +187,7 @@
 	// All architectures turn faults into apparent calls to sigpanic.
 	// If we see a call to sigpanic, we do not back up the PC to find
 	// the line number of the call instruction, because there is no call.
-	if xpc > f.entry && (g == nil || g.entry != funcPC(sigpanic)) {
+	if xpc > f.entry && (!g.valid() || g.entry != funcPC(sigpanic)) {
 		xpc--
 	}
 	file, line32 := funcline(f, xpc)
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 32e393d..35f6124 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -565,7 +565,7 @@
 	for i := uintptr(0); i < nstk; i++ {
 		pc := stk[i]
 		f := findfunc(pc)
-		if f == nil {
+		if !f.valid() {
 			var buf [64]byte
 			n := len(buf)
 			n--
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 7bead96..3e782f5 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -1877,7 +1877,7 @@
 		frame.sp = uintptr(p)
 		_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 != nil {
+		if frame.fn.valid() {
 			f := frame.fn
 			targetpc := frame.continpc
 			if targetpc == 0 {
diff --git a/src/runtime/os3_plan9.go b/src/runtime/os3_plan9.go
index 5aa6b67..5d4b5a6 100644
--- a/src/runtime/os3_plan9.go
+++ b/src/runtime/os3_plan9.go
@@ -62,7 +62,7 @@
 		// but we do recognize the top pointer on the stack as code,
 		// then assume this was a call to non-code and treat like
 		// pc == 0, to make unwinding show the context.
-		if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
+		if pc != 0 && !findfunc(pc).valid() && findfunc(*(*uintptr)(unsafe.Pointer(sp))).valid() {
 			pc = 0
 		}
 
diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go
index ea24650..682caac 100644
--- a/src/runtime/plugin.go
+++ b/src/runtime/plugin.go
@@ -95,7 +95,7 @@
 			continue
 		}
 
-		f := (*_func)(unsafe.Pointer(&md.pclntable[md.ftab[i].funcoff]))
+		f := funcInfo{(*_func)(unsafe.Pointer(&md.pclntable[md.ftab[i].funcoff])), md}
 		name := funcname(f)
 
 		// A common bug is f.entry has a relocation to a duplicate
@@ -104,7 +104,7 @@
 		name2 := "none"
 		entry2 := uintptr(0)
 		f2 := findfunc(entry)
-		if f2 != nil {
+		if f2.valid() {
 			name2 = funcname(f2)
 			entry2 = f2.entry
 		}
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 722bdf7..c9c4513 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -3336,7 +3336,7 @@
 // or putting one on the stack at the right offset.
 func setsSP(pc uintptr) bool {
 	f := findfunc(pc)
-	if f == nil {
+	if !f.valid() {
 		// couldn't find the function for this PC,
 		// so assume the worst and stop traceback
 		return true
diff --git a/src/runtime/race.go b/src/runtime/race.go
index 1034e35..49495cc 100644
--- a/src/runtime/race.go
+++ b/src/runtime/race.go
@@ -98,7 +98,7 @@
 	if f != nil {
 		file, line := f.FileLine(ctx.pc)
 		if line != 0 {
-			ctx.fn = cfuncname(f.raw())
+			ctx.fn = cfuncname(f.funcInfo())
 			ctx.line = uintptr(line)
 			ctx.file = &bytes(file)[0] // assume NUL-terminated
 			ctx.off = ctx.pc - f.Entry()
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 7c15919..8b6bddf 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -685,7 +685,7 @@
 
 // stack traces
 type stkframe struct {
-	fn       *_func     // function being run
+	fn       funcInfo   // function being run
 	pc       uintptr    // program counter within fn
 	continpc uintptr    // program counter where execution can continue, or 0 if not
 	lr       uintptr    // program counter at caller aka link register
diff --git a/src/runtime/signal_386.go b/src/runtime/signal_386.go
index 8807552..416c7c2 100644
--- a/src/runtime/signal_386.go
+++ b/src/runtime/signal_386.go
@@ -60,7 +60,7 @@
 	// but we do recognize the top pointer on the stack as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(*(*uintptr)(unsafe.Pointer(sp))).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_amd64x.go b/src/runtime/signal_amd64x.go
index c8a6513..fad5fc0 100644
--- a/src/runtime/signal_amd64x.go
+++ b/src/runtime/signal_amd64x.go
@@ -71,7 +71,7 @@
 	// but we do recognize the top pointer on the stack as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(*(*uintptr)(unsafe.Pointer(sp))).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_arm.go b/src/runtime/signal_arm.go
index 9748544..d00b225 100644
--- a/src/runtime/signal_arm.go
+++ b/src/runtime/signal_arm.go
@@ -57,7 +57,7 @@
 	// but we do recognize the link register as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.lr())) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(uintptr(c.lr())).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_arm64.go b/src/runtime/signal_arm64.go
index 4c6df42..1db0525 100644
--- a/src/runtime/signal_arm64.go
+++ b/src/runtime/signal_arm64.go
@@ -73,7 +73,7 @@
 	// but we do recognize the link register as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.lr())) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(uintptr(c.lr())).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_linux_s390x.go b/src/runtime/signal_linux_s390x.go
index de71ee9..a31f436 100644
--- a/src/runtime/signal_linux_s390x.go
+++ b/src/runtime/signal_linux_s390x.go
@@ -103,7 +103,7 @@
 	// but we do recognize the link register as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(uintptr(c.link())).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_mips64x.go b/src/runtime/signal_mips64x.go
index 973ec2d..9546a5a 100644
--- a/src/runtime/signal_mips64x.go
+++ b/src/runtime/signal_mips64x.go
@@ -77,7 +77,7 @@
 	// but we do recognize the link register as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(uintptr(c.link())).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_mipsx.go b/src/runtime/signal_mipsx.go
index 62df79c..1c545ec 100644
--- a/src/runtime/signal_mipsx.go
+++ b/src/runtime/signal_mipsx.go
@@ -74,7 +74,7 @@
 	// but we do recognize the link register as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(uintptr(c.link())).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/signal_ppc64x.go b/src/runtime/signal_ppc64x.go
index f09f890..03cb996 100644
--- a/src/runtime/signal_ppc64x.go
+++ b/src/runtime/signal_ppc64x.go
@@ -78,7 +78,7 @@
 	// but we do recognize the link register as code,
 	// then assume this was a call to non-code and treat like
 	// pc == 0, to make unwinding show the context.
-	if pc != 0 && findfunc(pc) == nil && findfunc(uintptr(c.link())) != nil {
+	if pc != 0 && !findfunc(pc).valid() && findfunc(uintptr(c.link())).valid() {
 		pc = 0
 	}
 
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index d6a4e4e..92b8a2b 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -569,7 +569,7 @@
 
 // bv describes the memory starting at address scanp.
 // Adjust any pointers contained therein.
-func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f *_func) {
+func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f funcInfo) {
 	bv := gobv(*cbv)
 	minp := adjinfo.old.lo
 	maxp := adjinfo.old.hi
@@ -589,7 +589,7 @@
 			pp := (*uintptr)(add(scanp, i*sys.PtrSize))
 		retry:
 			p := *pp
-			if f != nil && 0 < p && p < minLegalPointer && debug.invalidptr != 0 {
+			if f.valid() && 0 < p && p < minLegalPointer && debug.invalidptr != 0 {
 				// Looks like a junk value in a pointer slot.
 				// Live analysis wrong?
 				getg().m.traceback = 2
@@ -713,7 +713,7 @@
 		if stackDebug >= 3 {
 			print("      args\n")
 		}
-		adjustpointers(unsafe.Pointer(frame.argp), &bv, adjinfo, nil)
+		adjustpointers(unsafe.Pointer(frame.argp), &bv, adjinfo, funcInfo{})
 	}
 	return true
 }
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index b16e544..a31cf55 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -157,7 +157,8 @@
 // NOTE: Func does not expose the actual unexported fields, because we return *Func
 // values to users, and we want to keep them from being able to overwrite the data
 // with (say) *f = Func{}.
-// All code operating on a *Func must call raw to get the *_func instead.
+// All code operating on a *Func must call raw() to get the *_func
+// or funcInfo() to get the funcInfo instead.
 
 // A Func represents a Go function in the running binary.
 type Func struct {
@@ -168,6 +169,11 @@
 	return (*_func)(unsafe.Pointer(f))
 }
 
+func (f *Func) funcInfo() funcInfo {
+	fn := f.raw()
+	return funcInfo{fn, findmoduledatap(fn.entry)}
+}
+
 // PCDATA and FUNCDATA table indexes.
 //
 // See funcdata.h and ../cmd/internal/obj/funcdata.go.
@@ -365,15 +371,15 @@
 	for i := 0; i < nftab; i++ {
 		// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
 		if datap.ftab[i].entry > datap.ftab[i+1].entry {
-			f1 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
-			f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff]))
+			f1 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])), datap}
+			f2 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff])), datap}
 			f2name := "end"
 			if i+1 < nftab {
 				f2name = funcname(f2)
 			}
 			println("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name)
 			for j := 0; j <= i; j++ {
-				print("\t", hex(datap.ftab[j].entry), " ", funcname((*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff]))), "\n")
+				print("\t", hex(datap.ftab[j].entry), " ", funcname(funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff])), datap}), "\n")
 			}
 			throw("invalid runtime symbol table")
 		}
@@ -386,10 +392,10 @@
 			// But don't use the next PC if it corresponds to a foreign object chunk
 			// (no pcln table, f2.pcln == 0). That chunk might have an alignment
 			// more than 16 bytes.
-			f := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff]))
+			f := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])), datap}
 			end := f.entry
 			if i+1 < nftab {
-				f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff]))
+				f2 := funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff])), datap}
 				if f2.pcln != 0 {
 					end = f2.entry - 16
 					if end < f.entry {
@@ -419,12 +425,12 @@
 // FuncForPC returns a *Func describing the function that contains the
 // given program counter address, or else nil.
 func FuncForPC(pc uintptr) *Func {
-	return (*Func)(unsafe.Pointer(findfunc(pc)))
+	return (*Func)(unsafe.Pointer(findfunc(pc)._func))
 }
 
 // Name returns the name of the function.
 func (f *Func) Name() string {
-	return funcname(f.raw())
+	return funcname(f.funcInfo())
 }
 
 // Entry returns the entry address of the function.
@@ -439,7 +445,7 @@
 func (f *Func) FileLine(pc uintptr) (file string, line int) {
 	// Pass strict=false here, because anyone can call this function,
 	// and they might just be wrong about targetpc belonging to f.
-	file, line32 := funcline1(f.raw(), pc, false)
+	file, line32 := funcline1(f.funcInfo(), pc, false)
 	return file, int(line32)
 }
 
@@ -452,10 +458,19 @@
 	return nil
 }
 
-func findfunc(pc uintptr) *_func {
+type funcInfo struct {
+	*_func
+	datap *moduledata
+}
+
+func (f funcInfo) valid() bool {
+	return f._func != nil
+}
+
+func findfunc(pc uintptr) funcInfo {
 	datap := findmoduledatap(pc)
 	if datap == nil {
-		return nil
+		return funcInfo{}
 	}
 	const nsub = uintptr(len(findfuncbucket{}.subbuckets))
 
@@ -491,7 +506,7 @@
 			idx++
 		}
 	}
-	return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff]))
+	return funcInfo{(*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff])), datap}
 }
 
 type pcvalueCache struct {
@@ -506,7 +521,7 @@
 	val int32
 }
 
-func pcvalue(f *_func, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
+func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
 	if off == 0 {
 		return -1
 	}
@@ -530,14 +545,14 @@
 		}
 	}
 
-	datap := findmoduledatap(f.entry) // inefficient
-	if datap == nil {
+	if !f.valid() {
 		if strict && panicking == 0 {
 			print("runtime: no module data for ", hex(f.entry), "\n")
 			throw("no module data")
 		}
 		return -1
 	}
+	datap := f.datap
 	p := datap.pclntable[off:]
 	pc := f.entry
 	val := int32(-1)
@@ -589,41 +604,37 @@
 	return -1
 }
 
-func cfuncname(f *_func) *byte {
-	if f == nil || f.nameoff == 0 {
+func cfuncname(f funcInfo) *byte {
+	if !f.valid() || f.nameoff == 0 {
 		return nil
 	}
-	datap := findmoduledatap(f.entry) // inefficient
-	if datap == nil {
-		return nil
-	}
-	return &datap.pclntable[f.nameoff]
+	return &f.datap.pclntable[f.nameoff]
 }
 
-func funcname(f *_func) string {
+func funcname(f funcInfo) string {
 	return gostringnocopy(cfuncname(f))
 }
 
-func funcnameFromNameoff(f *_func, nameoff int32) string {
-	datap := findmoduledatap(f.entry) // inefficient
-	if datap == nil {
+func funcnameFromNameoff(f funcInfo, nameoff int32) string {
+	datap := f.datap
+	if !f.valid() {
 		return ""
 	}
 	cstr := &datap.pclntable[nameoff]
 	return gostringnocopy(cstr)
 }
 
-func funcfile(f *_func, fileno int32) string {
-	datap := findmoduledatap(f.entry) // inefficient
-	if datap == nil {
+func funcfile(f funcInfo, fileno int32) string {
+	datap := f.datap
+	if !f.valid() {
 		return "?"
 	}
 	return gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
 }
 
-func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) {
-	datap := findmoduledatap(f.entry) // inefficient
-	if datap == nil {
+func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int32) {
+	datap := f.datap
+	if !f.valid() {
 		return "?", 0
 	}
 	fileno := int(pcvalue(f, f.pcfile, targetpc, nil, strict))
@@ -636,11 +647,11 @@
 	return
 }
 
-func funcline(f *_func, targetpc uintptr) (file string, line int32) {
+func funcline(f funcInfo, targetpc uintptr) (file string, line int32) {
 	return funcline1(f, targetpc, true)
 }
 
-func funcspdelta(f *_func, targetpc uintptr, cache *pcvalueCache) int32 {
+func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 {
 	x := pcvalue(f, f.pcsp, targetpc, cache, true)
 	if x&(sys.PtrSize-1) != 0 {
 		print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n")
@@ -648,7 +659,7 @@
 	return x
 }
 
-func pcdatavalue(f *_func, table int32, targetpc uintptr, cache *pcvalueCache) int32 {
+func pcdatavalue(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache) int32 {
 	if table < 0 || table >= f.npcdata {
 		return -1
 	}
@@ -656,14 +667,14 @@
 	return pcvalue(f, off, targetpc, cache, true)
 }
 
-func funcdata(f *_func, i int32) unsafe.Pointer {
+func funcdata(f funcInfo, i int32) unsafe.Pointer {
 	if i < 0 || i >= f.nfuncdata {
 		return nil
 	}
 	p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4)
 	if sys.PtrSize == 8 && uintptr(p)&4 != 0 {
-		if uintptr(unsafe.Pointer(f))&4 != 0 {
-			println("runtime: misaligned func", f)
+		if uintptr(unsafe.Pointer(f._func))&4 != 0 {
+			println("runtime: misaligned func", f._func)
 		}
 		p = add(p, 4)
 	}
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 9f319cd..c29c162 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -816,7 +816,7 @@
 
 	var frame traceFrame
 	f := findfunc(pc)
-	if f == nil {
+	if !f.valid() {
 		frames[pc] = frame
 		return frame, buf
 	}
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 39ef8a2..f72b068 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -92,14 +92,14 @@
 		if fn == nil {
 			// Defer of nil function. Args don't matter.
 			frame.pc = 0
-			frame.fn = nil
+			frame.fn = funcInfo{}
 			frame.argp = 0
 			frame.arglen = 0
 			frame.argmap = nil
 		} else {
 			frame.pc = fn.fn
 			f := findfunc(frame.pc)
-			if f == nil {
+			if !f.valid() {
 				print("runtime: unknown pc in defer ", hex(frame.pc), "\n")
 				throw("unknown pc")
 			}
@@ -186,7 +186,7 @@
 	}
 
 	f := findfunc(frame.pc)
-	if f == nil {
+	if !f.valid() {
 		if callback != nil {
 			print("runtime: unknown pc ", hex(frame.pc), "\n")
 			throw("unknown pc")
@@ -230,10 +230,10 @@
 				frame.fp += sys.RegSize
 			}
 		}
-		var flr *_func
+		var flr funcInfo
 		if topofstack(f) {
 			frame.lr = 0
-			flr = nil
+			flr = funcInfo{}
 		} else if usesLR && f.entry == jmpdeferPC {
 			// jmpdefer modifies SP/LR/PC non-atomically.
 			// If a profiling interrupt arrives during jmpdefer,
@@ -259,7 +259,7 @@
 				}
 			}
 			flr = findfunc(frame.lr)
-			if flr == nil {
+			if !flr.valid() {
 				// This happens if you get a profiling interrupt at just the wrong time.
 				// In that context it is okay to stop early.
 				// But if callback is set, we're doing a garbage collection and must
@@ -403,7 +403,7 @@
 		waspanic = f.entry == sigpanicPC
 
 		// Do not unwind past the bottom of the stack.
-		if flr == nil {
+		if !flr.valid() {
 			break
 		}
 
@@ -426,7 +426,7 @@
 			}
 			f = findfunc(frame.pc)
 			frame.fn = f
-			if f == nil {
+			if !f.valid() {
 				frame.pc = x
 			} else if funcspdelta(f, frame.pc, &cache) == 0 {
 				frame.lr = x
@@ -521,7 +521,7 @@
 // call, ctxt must be nil (getArgInfo will retrieve what it needs from
 // the active stack frame). If this is a deferred call, ctxt must be
 // the function object that was deferred.
-func getArgInfo(frame *stkframe, f *_func, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) {
+func getArgInfo(frame *stkframe, f funcInfo, needArgMap bool, ctxt *funcval) (arglen uintptr, argmap *bitvector) {
 	arglen = uintptr(f.args)
 	if needArgMap && f.args == _ArgsSizeUnknown {
 		// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
@@ -593,7 +593,7 @@
 	// Show what created goroutine, except main goroutine (goid 1).
 	pc := gp.gopc
 	f := findfunc(pc)
-	if f != nil && showframe(f, gp, false) && gp.goid != 1 {
+	if f.valid() && showframe(f, gp, false) && gp.goid != 1 {
 		print("created by ", funcname(f), "\n")
 		tracepc := pc // back up to CALL instruction for funcline.
 		if pc > f.entry {
@@ -673,7 +673,7 @@
 	return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
 }
 
-func showframe(f *_func, gp *g, firstFrame bool) bool {
+func showframe(f funcInfo, gp *g, firstFrame bool) bool {
 	g := getg()
 	if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
 		return true
@@ -690,7 +690,7 @@
 		return true
 	}
 
-	return level > 1 || f != nil && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
+	return level > 1 || f.valid() && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
 }
 
 // isExportedRuntime reports whether name is an exported runtime function.
@@ -781,7 +781,7 @@
 }
 
 // Does f mark the top of a goroutine stack?
-func topofstack(f *_func) bool {
+func topofstack(f funcInfo) bool {
 	pc := f.entry
 	return pc == goexitPC ||
 		pc == mstartPC ||