Skip to content

Commit 1bcf6be

Browse files
committed
cmd/compile: use staticValue for inlining logic
This CL replaces the ad hoc and duplicated logic for detecting inlinable calls with a single "inlCallee" function, which uses the "staticValue" helper function introduced in an earlier commit. Updates #41474. Change-Id: I103d4091b10366fce1344ef2501222b7df68f21d Reviewed-on: https://go-review.googlesource.com/c/go/+/256460 Reviewed-by: David Chase <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Trust: Matthew Dempsky <[email protected]>
1 parent 64fb6ae commit 1bcf6be

File tree

3 files changed

+34
-68
lines changed

3 files changed

+34
-68
lines changed

src/cmd/compile/internal/gc/inl.go

Lines changed: 25 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -325,18 +325,10 @@ func (v *hairyVisitor) visit(n *Node) bool {
325325
break
326326
}
327327

328-
if fn := n.Left.Func; fn != nil && fn.Inl != nil {
329-
v.budget -= fn.Inl.Cost
328+
if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil {
329+
v.budget -= fn.Func.Inl.Cost
330330
break
331331
}
332-
if n.Left.isMethodExpression() {
333-
if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl != nil {
334-
v.budget -= d.Func.Inl.Cost
335-
break
336-
}
337-
}
338-
// TODO(mdempsky): Budget for OCLOSURE calls if we
339-
// ever allow that. See #15561 and #23093.
340332

341333
// Call cost for non-leaf inlining.
342334
v.budget -= v.extraCallCost
@@ -679,53 +671,11 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
679671
if Debug['m'] > 3 {
680672
fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
681673
}
682-
if n.Left.Func != nil && n.Left.Func.Inl != nil && !isIntrinsicCall(n) { // normal case
683-
n = mkinlcall(n, n.Left, maxCost, inlMap)
684-
} else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil {
685-
n = mkinlcall(n, asNode(n.Left.Sym.Def), maxCost, inlMap)
686-
} else if n.Left.Op == OCLOSURE {
687-
if f := inlinableClosure(n.Left); f != nil {
688-
n = mkinlcall(n, f, maxCost, inlMap)
689-
}
690-
} else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
691-
if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
692-
if f := inlinableClosure(d.Right); f != nil {
693-
// NB: this check is necessary to prevent indirect re-assignment of the variable
694-
// having the address taken after the invocation or only used for reads is actually fine
695-
// but we have no easy way to distinguish the safe cases
696-
if d.Left.Name.Addrtaken() {
697-
if Debug['m'] > 1 {
698-
fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left)
699-
}
700-
if logopt.Enabled() {
701-
logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
702-
fmt.Sprintf("%v cannot be inlined (escaping closure variable)", n.Left))
703-
}
704-
break
705-
}
706-
707-
// ensure the variable is never re-assigned
708-
if unsafe, a := reassigned(n.Left); unsafe {
709-
if Debug['m'] > 1 {
710-
if a != nil {
711-
fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a)
712-
if logopt.Enabled() {
713-
logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
714-
fmt.Sprintf("%v cannot be inlined (re-assigned closure variable)", a))
715-
}
716-
} else {
717-
fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left)
718-
if logopt.Enabled() {
719-
logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
720-
fmt.Sprintf("%v cannot be inlined (global closure variable)", n.Left))
721-
}
722-
}
723-
}
724-
break
725-
}
726-
n = mkinlcall(n, f, maxCost, inlMap)
727-
}
728-
}
674+
if isIntrinsicCall(n) {
675+
break
676+
}
677+
if fn := inlCallee(n.Left); fn != nil && fn.Func.Inl != nil {
678+
n = mkinlcall(n, fn, maxCost, inlMap)
729679
}
730680

731681
case OCALLMETH:
@@ -749,16 +699,22 @@ func inlnode(n *Node, maxCost int32, inlMap map[*Node]bool) *Node {
749699
return n
750700
}
751701

752-
// inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with
753-
// the inlinable body. Returns nil if the function is not inlinable.
754-
func inlinableClosure(n *Node) *Node {
755-
c := n.Func.Closure
756-
caninl(c)
757-
f := c.Func.Nname
758-
if f == nil || f.Func.Inl == nil {
759-
return nil
702+
// inlCallee takes a function-typed expression and returns the underlying function ONAME
703+
// that it refers to if statically known. Otherwise, it returns nil.
704+
func inlCallee(fn *Node) *Node {
705+
fn = staticValue(fn)
706+
switch {
707+
case fn.Op == ONAME && fn.Class() == PFUNC:
708+
if fn.isMethodExpression() {
709+
return asNode(fn.Sym.Def)
710+
}
711+
return fn
712+
case fn.Op == OCLOSURE:
713+
c := fn.Func.Closure
714+
caninl(c)
715+
return c.Func.Nname
760716
}
761-
return f
717+
return nil
762718
}
763719

764720
func staticValue(n *Node) *Node {
@@ -771,7 +727,9 @@ func staticValue(n *Node) *Node {
771727
}
772728
}
773729

774-
// staticValue1 implements a simple SSA-like optimization.
730+
// staticValue1 implements a simple SSA-like optimization. If n is a local variable
731+
// that is initialized and never reassigned, staticValue1 returns the initializer
732+
// expression. Otherwise, it returns nil.
775733
func staticValue1(n *Node) *Node {
776734
if n.Op != ONAME || n.Class() != PAUTO || n.Name.Addrtaken() {
777735
return nil

src/cmd/compile/internal/logopt/logopt_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ func s15a8(x *[15]int64) [15]int64 {
208208
`"relatedInformation":[{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"}]}`)
209209
want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`)
210210
want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`)
211-
want(t, slogged, `{"range":{"start":{"line":21,"character":21},"end":{"line":21,"character":21}},"severity":3,"code":"cannotInlineCall","source":"go compiler","message":"foo cannot be inlined (escaping closure variable)"}`)
212211
// escape analysis explanation
213212
want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+
214213
`"relatedInformation":[`+

test/inline.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ func j(x int) int { // ERROR "can inline j"
4949
}
5050
}
5151

52+
func _() int { // ERROR "can inline _"
53+
tmp1 := h
54+
tmp2 := tmp1
55+
return tmp2(0) // ERROR "inlining call to h"
56+
}
57+
5258
var somethingWrong error
5359

5460
// local closures can be inlined
@@ -58,6 +64,9 @@ func l(x, y int) (int, int, error) {
5864
}
5965
if x == y {
6066
e(somethingWrong) // ERROR "inlining call to l.func1"
67+
} else {
68+
f := e
69+
f(nil) // ERROR "inlining call to l.func1"
6170
}
6271
return y, x, nil
6372
}

0 commit comments

Comments
 (0)