diff options
| author | Keith Randall <khr@golang.org> | 2026-03-04 16:07:30 -0800 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-03-06 10:28:08 -0800 |
| commit | e7a09d1ffb8745350cb9b5ba9c495b5e066e09ab (patch) | |
| tree | 194cbd50f4983d928b0893a007c3107df29b14c9 /src/cmd/compile/internal/noder | |
| parent | 50d988e4e037d9d41ac223a62706dfea47a100e4 (diff) | |
| download | go-e7a09d1ffb8745350cb9b5ba9c495b5e066e09ab.tar.xz | |
cmd/compile: use tail calls for wrappers for embedded interfaces
type I interface {
foo()
}
type S struct {
I
}
Because I is embedded in S, S needs a foo method. We generate a
wrapper function to implement (*S).foo. It just loads the embedded
field I out of S and calls foo on it.
When the thing in S.I itself needs a wrapper, then we have a wrapper
calling another wrapper. This can continue, leaving a potentially long
sequence of wrappers on the stack. When we then call runtime.Callers
or friends, we have to walk an unbounded number of frames to find a
bounded number of non-wrapper frames.
This really happens, for instance with I = context.Context, S =
context.ValueCtx, and runtime.Callers = pprof sample (for any of
context.Context's methods).
To fix, make the interface call in the wrapper a tail call.
That way, the number of wrapper frames on the stack does not
increase when there are lots of wrappers happening.
Fixes #75764
Fixes #77781
Change-Id: I03b1731159d9218c7f14f72ecbbac822d6a6bb87
Reviewed-on: https://go-review.googlesource.com/c/go/+/751465
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Diffstat (limited to 'src/cmd/compile/internal/noder')
| -rw-r--r-- | src/cmd/compile/internal/noder/reader.go | 7 |
1 files changed, 5 insertions, 2 deletions
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 7f82cfc6ba..a9ed77409b 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -3996,8 +3996,11 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) { call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr) if recv.Type() != nil && recv.Type().IsPtr() && method.Type.Recv().Type.IsPtr() && - method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && - !unifiedHaveInlineBody(ir.MethodExprName(dot).Func) && + method.Embedded != 0 && + (types.IsInterfaceMethod(method.Type) && base.Ctxt.Arch.Name != "wasm" || + !types.IsInterfaceMethod(method.Type) && !unifiedHaveInlineBody(ir.MethodExprName(dot).Func)) && + // TODO: implement wasm indirect tail calls + // TODO: do we need the ppc64le/dynlink restriction for interface tail calls? !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) { if base.Debug.TailCall != 0 { base.WarnfAt(fn.Nname.Type().Recv().Type.Elem().Pos(), "tail call emitted for the method %v wrapper", method.Nname) |
