Skip to content

Commit dbf442b

Browse files
aclementsgopherbot
authored andcommitted
runtime: replace stkframe.arglen/argmap with methods
Currently, stkframe.arglen and stkframe.argmap are populated by gentraceback under a particular set of circumstances. But because they can be constructed from other fields in stkframe, they don't need to be computed eagerly at all. They're also rather misleading, as they're only part of computing the actual argument map and most callers should be using getStackMap, which does the rest of the work. This CL drops these fields from stkframe. It shifts the functions that used to compute them, getArgInfoFast and getArgInfo, into corresponding methods stkframe.argBytes and stkframe.argMapInternal. argBytes is expected to be used by callers that need to know only the argument frame size, while argMapInternal is used only by argBytes and getStackMap. We also move some of the logic from getStackMap into argMapInternal because the previous split of responsibilities didn't make much sense. This lets us return just a bitvector from argMapInternal, rather than both a bitvector, which carries a size, and an "actually use this size". The getArgInfoFast function was inlined before (and inl_test checked this). We drop that requirement from stkframe.argBytes because the uses of this have shifted and now it's only called from heap dumping (which never happens) and conservative stack frame scanning (which very, very rarely happens). There will be a few follow-up clean-up CLs. For #54466. This is a nice clean-up on its own, but it also serves to remove pointers from the traceback state that would eventually become troublesome write barriers once we stack-rip gentraceback. Change-Id: I107f98ed8e7b00185c081de425bbf24af02a4163 Reviewed-on: https://go-review.googlesource.com/c/go/+/424514 Run-TryBot: Austin Clements <[email protected]> Auto-Submit: Austin Clements <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Reviewed-by: Cherry Mui <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 511cd9b commit dbf442b

File tree

6 files changed

+65
-71
lines changed

6 files changed

+65
-71
lines changed

src/cmd/compile/internal/test/inl_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ func TestIntendedInlining(t *testing.T) {
4747
"fastrand",
4848
"float64bits",
4949
"funcspdelta",
50-
"getArgInfoFast",
5150
"getm",
5251
"getMCache",
5352
"isDirectIface",

src/runtime/heapdump.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ func dumpframe(s *stkframe, arg unsafe.Pointer) bool {
327327

328328
// Record arg info for parent.
329329
child.argoff = s.argp - s.fp
330-
child.arglen = s.arglen
330+
child.arglen = s.argBytes()
331331
child.sp = (*uint8)(unsafe.Pointer(s.sp))
332332
child.depth++
333333
stkmap = (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))

src/runtime/mgcmark.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -945,10 +945,10 @@ func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) {
945945
}
946946

947947
// Scan arguments to this frame.
948-
if frame.arglen != 0 {
948+
if n := frame.argBytes(); n != 0 {
949949
// TODO: We could pass the entry argument map
950950
// to narrow this down further.
951-
scanConservative(frame.argp, frame.arglen, nil, gcw, state)
951+
scanConservative(frame.argp, n, nil, gcw, state)
952952
}
953953

954954
if isAsyncPreempt || isDebugCall {

src/runtime/runtime2.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,13 +1027,11 @@ type stkframe struct {
10271027
// This is the PC to use to look up GC liveness for this frame.
10281028
continpc uintptr
10291029

1030-
lr uintptr // program counter at caller aka link register
1031-
sp uintptr // stack pointer at pc
1032-
fp uintptr // stack pointer at caller aka frame pointer
1033-
varp uintptr // top of local variables
1034-
argp uintptr // pointer to function arguments
1035-
arglen uintptr // number of bytes at argp
1036-
argmap *bitvector // force use of this argmap
1030+
lr uintptr // program counter at caller aka link register
1031+
sp uintptr // stack pointer at pc
1032+
fp uintptr // stack pointer at caller aka frame pointer
1033+
varp uintptr // top of local variables
1034+
argp uintptr // pointer to function arguments
10371035
}
10381036

10391037
// ancestorInfo records details of where a goroutine was started.

src/runtime/stack.go

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,40 +1305,35 @@ func getStackMap(frame *stkframe, cache *pcvalueCache, debug bool) (locals, args
13051305
}
13061306
}
13071307

1308-
// Arguments.
1309-
if frame.arglen > 0 {
1310-
if frame.argmap != nil {
1311-
// argmap is set when the function is reflect.makeFuncStub or reflect.methodValueCall.
1312-
// In this case, arglen specifies how much of the args section is actually live.
1313-
// (It could be either all the args + results, or just the args.)
1314-
args = *frame.argmap
1315-
n := int32(frame.arglen / goarch.PtrSize)
1316-
if n < args.n {
1317-
args.n = n // Don't use more of the arguments than arglen.
1318-
}
1308+
// Arguments. First fetch frame size and special-case argument maps.
1309+
var isReflect bool
1310+
args, isReflect = frame.argMapInternal()
1311+
if args.n > 0 && args.bytedata == nil {
1312+
// Non-empty argument frame, but not a special map.
1313+
// Fetch the argument map at pcdata.
1314+
stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
1315+
if stackmap == nil || stackmap.n <= 0 {
1316+
print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(args.n*goarch.PtrSize), "\n")
1317+
throw("missing stackmap")
1318+
}
1319+
if pcdata < 0 || pcdata >= stackmap.n {
1320+
// don't know where we are
1321+
print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
1322+
throw("bad symbol table")
1323+
}
1324+
if stackmap.nbit == 0 {
1325+
args.n = 0
13191326
} else {
1320-
stackmap := (*stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
1321-
if stackmap == nil || stackmap.n <= 0 {
1322-
print("runtime: frame ", funcname(f), " untyped args ", hex(frame.argp), "+", hex(frame.arglen), "\n")
1323-
throw("missing stackmap")
1324-
}
1325-
if pcdata < 0 || pcdata >= stackmap.n {
1326-
// don't know where we are
1327-
print("runtime: pcdata is ", pcdata, " and ", stackmap.n, " args stack map entries for ", funcname(f), " (targetpc=", hex(targetpc), ")\n")
1328-
throw("bad symbol table")
1329-
}
1330-
if stackmap.nbit > 0 {
1331-
args = stackmapdata(stackmap, pcdata)
1332-
}
1327+
args = stackmapdata(stackmap, pcdata)
13331328
}
13341329
}
13351330

13361331
// stack objects.
13371332
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") &&
1338-
unsafe.Sizeof(abi.RegArgs{}) > 0 && frame.argmap != nil {
1339-
// argmap is set when the function is reflect.makeFuncStub or reflect.methodValueCall.
1340-
// We don't actually use argmap in this case, but we need to fake the stack object
1341-
// record for these frames which contain an internal/abi.RegArgs at a hard-coded offset.
1333+
unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect {
1334+
// For reflect.makeFuncStub and reflect.methodValueCall,
1335+
// we need to fake the stack object record.
1336+
// These frames contain an internal/abi.RegArgs at a hard-coded offset.
13421337
// This offset matches the assembly code on amd64 and arm64.
13431338
objs = methodValueCallFrameObjs[:]
13441339
} else {

src/runtime/traceback.go

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -285,20 +285,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
285285
frame.varp -= goarch.PtrSize
286286
}
287287

288-
// Derive size of arguments.
289-
// Most functions have a fixed-size argument block,
290-
// so we can use metadata about the function f.
291-
// Not all, though: there are some variadic functions
292-
// in package runtime and reflect, and for those we use call-specific
293-
// metadata recorded by f's caller.
294-
if callback != nil || printing {
295-
frame.argp = frame.fp + sys.MinFrameSize
296-
var ok bool
297-
frame.arglen, frame.argmap, ok = getArgInfoFast(f, callback != nil)
298-
if !ok {
299-
frame.arglen, frame.argmap = getArgInfo(&frame, callback != nil)
300-
}
301-
}
288+
frame.argp = frame.fp + sys.MinFrameSize
302289

303290
// Determine frame's 'continuation PC', where it can continue.
304291
// Normally this is the return address on the stack, but if sigpanic
@@ -491,7 +478,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
491478
frame.lr = 0
492479
frame.sp = frame.fp
493480
frame.fp = 0
494-
frame.argmap = nil
495481

496482
// On link register architectures, sighandler saves the LR on stack
497483
// before faking a call.
@@ -670,21 +656,33 @@ type reflectMethodValue struct {
670656
argLen uintptr // just args
671657
}
672658

673-
// getArgInfoFast returns the argument frame information for a call to f.
674-
// It is short and inlineable. However, it does not handle all functions.
675-
// If ok reports false, you must call getArgInfo instead.
676-
// TODO(josharian): once we do mid-stack inlining,
677-
// call getArgInfo directly from getArgInfoFast and stop returning an ok bool.
678-
func getArgInfoFast(f funcInfo, needArgMap bool) (arglen uintptr, argmap *bitvector, ok bool) {
679-
return uintptr(f.args), nil, !(needArgMap && f.args == _ArgsSizeUnknown)
659+
// argBytes returns the argument frame size for a call to frame.fn.
660+
func (frame *stkframe) argBytes() uintptr {
661+
if frame.fn.args != _ArgsSizeUnknown {
662+
return uintptr(frame.fn.args)
663+
}
664+
// This is an uncommon and complicated case. Fall back to fully
665+
// fetching the argument map to compute its size.
666+
argMap, _ := frame.argMapInternal()
667+
return uintptr(argMap.n) * goarch.PtrSize
680668
}
681669

682-
// getArgInfo returns the argument frame information for a call to f
683-
// with call frame frame.
684-
func getArgInfo(frame *stkframe, needArgMap bool) (arglen uintptr, argmap *bitvector) {
670+
// argMapInternal is used internally by stkframe to fetch special
671+
// argument maps.
672+
//
673+
// argMap.n is always populated with the size of the argument map.
674+
//
675+
// argMap.bytedata is only populated for dynamic argument maps (used
676+
// by reflect). If the caller requires the argument map, it should use
677+
// this if non-nil, and otherwise fetch the argument map using the
678+
// current PC.
679+
//
680+
// hasReflectStackObj indicates that this frame also has a reflect
681+
// function stack object, which the caller must synthesize.
682+
func (frame *stkframe) argMapInternal() (argMap bitvector, hasReflectStackObj bool) {
685683
f := frame.fn
686-
arglen = uintptr(f.args)
687-
if needArgMap && f.args == _ArgsSizeUnknown {
684+
argMap.n = f.args / goarch.PtrSize
685+
if f.args == _ArgsSizeUnknown {
688686
// Extract argument bitmaps for reflect stubs from the calls they made to reflect.
689687
switch funcname(f) {
690688
case "reflect.makeFuncStub", "reflect.methodValueCall":
@@ -715,8 +713,9 @@ func getArgInfo(frame *stkframe, needArgMap bool) (arglen uintptr, argmap *bitve
715713
print("runtime: confused by ", funcname(f), ": no frame (sp=", hex(frame.sp), " fp=", hex(frame.fp), ") at entry+", hex(frame.pc-f.entry()), "\n")
716714
throw("reflect mismatch")
717715
}
718-
return 0, nil
716+
return bitvector{}, false // No locals, so also no stack objects
719717
}
718+
hasReflectStackObj = true
720719
mv := *(**reflectMethodValue)(unsafe.Pointer(arg0))
721720
// Figure out whether the return values are valid.
722721
// Reflect will update this value after it copies
@@ -726,12 +725,15 @@ func getArgInfo(frame *stkframe, needArgMap bool) (arglen uintptr, argmap *bitve
726725
print("runtime: confused by ", funcname(f), "\n")
727726
throw("reflect mismatch")
728727
}
729-
bv := mv.stack
730-
arglen = uintptr(bv.n * goarch.PtrSize)
728+
argMap = *mv.stack
731729
if !retValid {
732-
arglen = uintptr(mv.argLen) &^ (goarch.PtrSize - 1)
730+
// argMap.n includes the results, but
731+
// those aren't valid, so drop them.
732+
n := int32((uintptr(mv.argLen) &^ (goarch.PtrSize - 1)) / goarch.PtrSize)
733+
if n < argMap.n {
734+
argMap.n = n
735+
}
733736
}
734-
argmap = bv
735737
}
736738
}
737739
return

0 commit comments

Comments
 (0)