]> Cypherpunks repositories - gostls13.git/commitdiff
Revert "cmd/compile: Enable inlining of tail calls"
authorCherry Mui <cherryyz@google.com>
Tue, 3 Jun 2025 19:44:32 +0000 (15:44 -0400)
committerCherry Mui <cherryyz@google.com>
Wed, 4 Jun 2025 15:27:44 +0000 (08:27 -0700)
This reverts CL 650455 and CL 655816.

Reason for revert: it causes #73747. Properly fixing it gets into
trickiness with defer/recover, wrapper, and inlining. We're late
in the Go 1.25 release cycle.

Fixes #73747.

Change-Id: Ifb343d522b18fec3fec73a7c886678032ac8e4df
Reviewed-on: https://go-review.googlesource.com/c/go/+/678575
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
src/cmd/compile/internal/inline/inl.go
src/cmd/compile/internal/inline/interleaved/interleaved.go
src/cmd/compile/internal/ir/expr.go
src/cmd/compile/internal/ir/node_gen.go
src/cmd/compile/internal/ir/stmt.go
src/cmd/compile/internal/noder/reader.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/compile/internal/typecheck/stmt.go
src/cmd/compile/internal/walk/stmt.go
test/tailcall.go

index 8bba604214757eba44451e369fc7b715cb615e2a..459c2498fc40da55b62f7f50829c444812552103 100644 (file)
@@ -785,7 +785,7 @@ func inlineCallCheck(callerfn *ir.Func, call *ir.CallExpr) (bool, bool) {
        if call.Op() != ir.OCALLFUNC {
                return false, false
        }
-       if call.GoDefer {
+       if call.GoDefer || call.NoInline {
                return false, false
        }
 
index a884c1bc7375d428ce1a86cda6e15db4017e8bd3..954cc306fc81d3596f09533e19ac3c51e01463c6 100644 (file)
@@ -279,7 +279,12 @@ func (s *inlClosureState) mark(n ir.Node) ir.Node {
 
        ok := match(n)
 
-       ir.EditChildren(n, s.mark)
+       // can't wrap TailCall's child into ParenExpr
+       if t, ok := n.(*ir.TailCallStmt); ok {
+               ir.EditChildren(t.Call, s.mark)
+       } else {
+               ir.EditChildren(n, s.mark)
+       }
 
        if ok {
                if p == nil {
@@ -317,23 +322,6 @@ func (s *inlClosureState) unparenthesize() {
                        n = paren.X
                }
                ir.EditChildren(n, unparen)
-               // special case for tail calls: if the tail call was inlined, transform
-               // the tail call to a return stmt if the inlined function was not void,
-               // otherwise replace it with the inlined expression followed by a return.
-               if tail, ok := n.(*ir.TailCallStmt); ok {
-                       if inl, done := tail.Call.(*ir.InlinedCallExpr); done {
-                               if len(inl.ReturnVars) != 0 {
-                                       ret := ir.NewReturnStmt(tail.Pos(), []ir.Node{inl})
-                                       if len(inl.ReturnVars) > 1 {
-                                               typecheck.RewriteMultiValueCall(ret, inl)
-                                       }
-                                       n = ret
-                               } else {
-                                       ret := ir.NewReturnStmt(tail.Pos(), nil)
-                                       n = ir.NewBlockStmt(tail.Pos(), []ir.Node{inl, ret})
-                               }
-                       }
-               }
                return n
        }
        ir.EditChildren(s.fn, unparen)
@@ -370,9 +358,11 @@ func (s *inlClosureState) fixpoint() bool {
 }
 
 func match(n ir.Node) bool {
-       switch n.(type) {
+       switch n := n.(type) {
        case *ir.CallExpr:
                return true
+       case *ir.TailCallStmt:
+               n.Call.NoInline = true // can't inline yet
        }
        return false
 }
index 8f7df4b458570e24edcb63b30e0c65a261da6b12..702adfdd84ef5ee6bd595accd3f456cd949e56da 100644 (file)
@@ -191,6 +191,7 @@ type CallExpr struct {
        KeepAlive []*Name // vars to be kept alive until call returns
        IsDDD     bool
        GoDefer   bool // whether this call is part of a go or defer statement
+       NoInline  bool // whether this call must not be inlined
 }
 
 func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {
index e67b5ba0bc16f0ea83cf15265157c66e750f40e7..026acbf9dd61f5ecf2bd5bce00665c18622d056c 100644 (file)
@@ -2202,13 +2202,13 @@ func (n *TailCallStmt) doChildrenWithHidden(do func(Node) bool) bool {
 func (n *TailCallStmt) editChildren(edit func(Node) Node) {
        editNodes(n.init, edit)
        if n.Call != nil {
-               n.Call = edit(n.Call)
+               n.Call = edit(n.Call).(*CallExpr)
        }
 }
 func (n *TailCallStmt) editChildrenWithHidden(edit func(Node) Node) {
        editNodes(n.init, edit)
        if n.Call != nil {
-               n.Call = edit(n.Call)
+               n.Call = edit(n.Call).(*CallExpr)
        }
 }
 
index ae7fb2080b6f3e970f3e046a36faae735c75be79..0801ecdd9e8722f825fd7de0c55b5616114a9a58 100644 (file)
@@ -479,7 +479,7 @@ func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
 // code generation to jump directly to another function entirely.
 type TailCallStmt struct {
        miniStmt
-       Call Node // the underlying call
+       Call *CallExpr // the underlying call
 }
 
 func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt {
index c8546198972ad9ee4776f0a3531ca8624971b3f9..2c3f7161a830faf5ae72ec57bf4f8512ee90777e 100644 (file)
@@ -3996,11 +3996,12 @@ func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
 
        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 (the called method can be inlined).
+               // Prefer OTAILCALL to reduce code size (except the case when the called method can be inlined).
                fn.Body.Append(ir.NewTailCallStmt(pos, call))
                return
        }
index 542ad823ab80872eb87b57e8342d3cf0d8045220..e241e9b9bc0f4f86c64d98ac759dc69390f1efdf 100644 (file)
@@ -1921,7 +1921,7 @@ func (s *state) stmt(n ir.Node) {
 
        case ir.OTAILCALL:
                n := n.(*ir.TailCallStmt)
-               s.callResult(n.Call.(*ir.CallExpr), callTail)
+               s.callResult(n.Call, callTail)
                call := s.mem()
                b := s.endBlock()
                b.Kind = ssa.BlockRetJmp // could use BlockExit. BlockRetJmp is mostly for clarity.
index bb3a29dd13c0b4d539d650e0ddd0f03c0673a4ff..8d792485d863c401d98b279449a763415b46e96c 100644 (file)
@@ -137,7 +137,7 @@ assignOK:
        if cr > len(rhs) {
                stmt := stmt.(*ir.AssignListStmt)
                stmt.SetOp(ir.OAS2FUNC)
-               r := rhs[0]
+               r := rhs[0].(*ir.CallExpr)
                rtyp := r.Type()
 
                mismatched := false
index 2e5ca3180f1ac907fe4fe56b0ef742168d5bfaf8..b2a226e0789fbb27e88aea93af69440398034e3c 100644 (file)
@@ -139,8 +139,7 @@ func walkStmt(n ir.Node) ir.Node {
                n := n.(*ir.TailCallStmt)
 
                var init ir.Nodes
-               call := n.Call.(*ir.CallExpr)
-               call.Fun = walkExpr(call.Fun, &init)
+               n.Call.Fun = walkExpr(n.Call.Fun, &init)
 
                if len(init) > 0 {
                        init.Append(n)
index c1c35c5e483b8e0ec09887bf95e1782d2f7dfefe..6b14a2f1b77b6ce7d42bc878564cbbba09de7336 100644 (file)
@@ -7,14 +7,16 @@
 package p
 
 // Test that when generating wrappers for methods, we generate a tail call to the pointer version of
-// the method.
+// the method, if that method is not inlineable. We use go:noinline here to force the non-inlineability
+// condition.
 
-func (f *Foo) Get2Vals() [2]int          { return [2]int{f.Val, f.Val + 1} }
-func (f *Foo) Get3Vals() (int, int, int) { return f.Val, f.Val + 1, f.Val + 2 }
+//go:noinline
+func (f *Foo) Get2Vals() [2]int { return [2]int{f.Val, f.Val + 1} }
+func (f *Foo) Get3Vals() [3]int { return [3]int{f.Val, f.Val + 1, f.Val + 2} }
 
 type Foo struct{ Val int }
 
-type Bar struct { // ERROR "tail call emitted for the method \(\*Foo\).Get2Vals wrapper" "tail call emitted for the method \(\*Foo\).Get3Vals wrapper"
+type Bar struct { // ERROR "tail call emitted for the method \(\*Foo\).Get2Vals wrapper"
        int64
        *Foo // needs a method wrapper
        string