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>
if call.Op() != ir.OCALLFUNC {
return false, false
}
- if call.GoDefer {
+ if call.GoDefer || call.NoInline {
return false, false
}
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 {
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)
}
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
}
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 {
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)
}
}
// 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 {
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
}
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.
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
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)
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