aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/reader.go
diff options
context:
space:
mode:
authoramusman <alexander.musman@gmail.com>2024-04-11 10:57:32 +0300
committerKeith Randall <khr@golang.org>2024-09-09 20:20:10 +0000
commitaf0c40311e2ee33ecd24971257606f42a49cf593 (patch)
tree467a068d58b102868ca6f18ad06644517304949a /src/cmd/compile/internal/noder/reader.go
parentadf220a5d5ecaa407401e5f46a8460e97bbca64d (diff)
downloadgo-af0c40311e2ee33ecd24971257606f42a49cf593.tar.xz
cmd/compile: emit tail call wrappers when possible
Use OTAILCALL in wrapper if the receiver and method are both pointers and it is not going to be inlined, similar to how it is done in reflectdata.methodWrapper. Currently tail call may be used for functions with identical argument types. This change updates wrappers where both wrapper and the wrapped method's receiver are pointers. In this case, we have the same signature for the wrapper and the wrapped method (modulo the receiver's pointed-to types), and do not need any local variables in the generated wrapper (on stack) because the arguments are immediately passed to the wrapped method in place (without need to move some value passed to other register or to change any argument/return passed through stack). Thus, the wrapper does not need its own stack frame. This applies to promoted methods, e.g. when we have some struct type U with an embedded type *T and construct a wrapper like func (recv *U) M(arg int) bool { return recv.T.M(i) } See also test/abi/method_wrapper.go for a running example. Code size difference measured with this change (tried for x86_64): etcd binary: .text section size: 21472251 -> 21432350 (0.2%) total binary size: 32226640 -> 32191136 (0.1%) compile binary: .text section size: 17419073 -> 17413929 (0.03%) total binary size: 26744743 -> 26737567 (0.03%) Change-Id: I9bbe730568f6def21a8e61118a6b6f503d98049c Reviewed-on: https://go-review.googlesource.com/c/go/+/578235 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com>
Diffstat (limited to 'src/cmd/compile/internal/noder/reader.go')
-rw-r--r--src/cmd/compile/internal/noder/reader.go19
1 files changed, 14 insertions, 5 deletions
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 1dae4da167..ce4cc1cc4e 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -3971,14 +3971,23 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
args[i] = param.Nname.(*ir.Name)
}
- // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
- // Not urgent though, because tail calls are currently incompatible with regabi anyway.
-
- fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
-
dot := typecheck.XDotMethod(pos, recv, method.Sym, true)
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) &&
+ !(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)
+ }
+ // Prefer OTAILCALL to reduce code size (except the case when the called method can be inlined).
+ fn.Body.Append(ir.NewTailCallStmt(pos, call))
+ return
+ }
+
+ fn.SetWrapper(true)
+
if method.Type.NumResults() == 0 {
fn.Body.Append(call)
return