v.budget -= fn.InlCost
break
}
+ if n.Left.Op == OCLOSURE {
+ if fn := inlinableClosure(n.Left); fn != nil {
+ v.budget -= fn.Func.InlCost
+ break
+ }
+ } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
+ // NB: this case currently cannot trigger since closure definition
+ // prevents inlining
+ // NB: ideally we would also handle captured variables defined as
+ // closures in the outer scope this brings us back to the idea of
+ // function value propagation, which if available would both avoid
+ // the "reassigned" check and neatly handle multiple use cases in a
+ // single code path
+ if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
+ if fn := inlinableClosure(d.Right); fn != nil {
+ v.budget -= fn.Func.InlCost
+ break
+ }
+ }
+ }
if n.Left.isMethodExpression() {
if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 {
return n
}
+// inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with
+// the inlinable body. Returns nil if the function is not inlinable.
func inlinableClosure(n *Node) *Node {
c := n.Func.Closure
caninl(c)
f := c.Func.Nname
- if f != nil && f.Func.Inl.Len() != 0 {
- if n.Func.Cvars.Len() != 0 {
- // FIXME: support closure with captured variables
- // they currently result in invariant violation in the SSA phase
- if Debug['m'] > 1 {
- fmt.Printf("%v: cannot inline closure w/ captured vars %v\n", n.Line(), n.Left)
- }
- return nil
- }
- return f
+ if f == nil || f.Func.Inl.Len() == 0 {
+ return nil
}
- return nil
+ return f
}
// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
ninit := n.Ninit
+ // Make temp names to use instead of the originals.
+ inlvars := make(map[*Node]*Node)
+
// Find declarations corresponding to inlineable body.
var dcl []*Node
if fn.Name.Defn != nil {
dcl = fn.Func.Inldcl.Slice() // local function
+
+ // handle captured variables when inlining closures
+ if c := fn.Name.Defn.Func.Closure; c != nil {
+ for _, v := range c.Func.Cvars.Slice() {
+ if v.Op == OXXX {
+ continue
+ }
+
+ o := v.Name.Param.Outer
+ // make sure the outer param matches the inlining location
+ // NB: if we enabled inlining of functions containing OCLOSURE or refined
+ // the reassigned check via some sort of copy propagation this would most
+ // likely need to be changed to a loop to walk up to the correct Param
+ if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) {
+ Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v)
+ }
+
+ if v.Name.Byval() {
+ iv := typecheck(inlvar(v), Erv)
+ ninit.Append(nod(ODCL, iv, nil))
+ ninit.Append(typecheck(nod(OAS, iv, o), Etop))
+ inlvars[v] = iv
+ } else {
+ addr := newname(lookup("&" + v.Sym.Name))
+ addr.Type = types.NewPtr(v.Type)
+ ia := typecheck(inlvar(addr), Erv)
+ ninit.Append(nod(ODCL, ia, nil))
+ ninit.Append(typecheck(nod(OAS, ia, nod(OADDR, o, nil)), Etop))
+ inlvars[addr] = ia
+
+ // When capturing by reference, all occurrence of the captured var
+ // must be substituted with dereference of the temporary address
+ inlvars[v] = typecheck(nod(OIND, ia, nil), Erv)
+ }
+ }
+ }
} else {
dcl = fn.Func.Dcl // imported function
}
- // Make temp names to use instead of the originals.
- inlvars := make(map[*Node]*Node)
for _, ln := range dcl {
if ln.Op != ONAME {
continue
}
}()
}
+
+ {
+ x := 42
+ if y := func() int { // ERROR "can inline main.func20"
+ return x
+ }(); y != 42 { // ERROR "inlining call to main.func20"
+ panic("y != 42")
+ }
+ if y := func() int { // ERROR "can inline main.func21" "func literal does not escape"
+ return x
+ }; y() != 42 { // ERROR "inlining call to main.func21"
+ panic("y() != 42")
+ }
+ }
+
+ {
+ x := 42
+ if z := func(y int) int { // ERROR "func literal does not escape"
+ return func() int { // ERROR "can inline main.func22.1"
+ return x + y
+ }() // ERROR "inlining call to main.func22.1"
+ }(1); z != 43 {
+ panic("z != 43")
+ }
+ if z := func(y int) int { // ERROR "func literal does not escape"
+ return func() int { // ERROR "can inline main.func23.1"
+ return x + y
+ }() // ERROR "inlining call to main.func23.1"
+ }; z(1) != 43 {
+ panic("z(1) != 43")
+ }
+ }
+
+ {
+ a := 1
+ func() { // ERROR "func literal does not escape"
+ func() { // ERROR "can inline main.func24"
+ a = 2
+ }() // ERROR "inlining call to main.func24" "&a does not escape"
+ }()
+ if a != 2 {
+ panic("a != 2")
+ }
+ }
+
+ {
+ b := 2
+ func(b int) { // ERROR "func literal does not escape"
+ func() { // ERROR "can inline main.func25.1"
+ b = 3
+ }() // ERROR "inlining call to main.func25.1" "&b does not escape"
+ if b != 3 {
+ panic("b != 3")
+ }
+ }(b)
+ if b != 2 {
+ panic("b != 2")
+ }
+ }
+
+ {
+ c := 3
+ func() { // ERROR "func literal does not escape"
+ c = 4
+ func() { // ERROR "func literal does not escape"
+ if c != 4 {
+ panic("c != 4")
+ }
+ }()
+ }()
+ if c != 4 {
+ panic("c != 4")
+ }
+ }
+
+ {
+ a := 2
+ if r := func(x int) int { // ERROR "func literal does not escape"
+ b := 3
+ return func(y int) int { // ERROR "func literal does not escape"
+ c := 5
+ return func(z int) int { // ERROR "can inline main.func27.1.1"
+ return a*x + b*y + c*z
+ }(10) // ERROR "inlining call to main.func27.1.1"
+ }(100)
+ }(1000); r != 2350 {
+ panic("r != 2350")
+ }
+ }
+
+ {
+ a := 2
+ if r := func(x int) int { // ERROR "func literal does not escape"
+ b := 3
+ return func(y int) int { // ERROR "func literal does not escape"
+ c := 5
+ func(z int) { // ERROR "can inline main.func28.1.1"
+ a = a * x
+ b = b * y
+ c = c * z
+ }(10) // ERROR "inlining call to main.func28.1.1" "&a does not escape" "&b does not escape" "&c does not escape"
+ return a + c
+ }(100) + b
+ }(1000); r != 2350 {
+ panic("r != 2350")
+ }
+ if a != 2000 {
+ panic("a != 2000")
+ }
+ }
}
return func() int { return 42 }() // ERROR "can inline p.func1" "inlining call to p.func1"
}
+func q(x int) int {
+ foo := func() int { return x * 2 } // ERROR "can inline q.func1" "q func literal does not escape"
+ return foo() // ERROR "inlining call to q.func1"
+}
+
+func r(z int) int {
+ foo := func(x int) int { // ERROR "can inline r.func1" "r func literal does not escape"
+ return x + z
+ }
+ bar := func(x int) int { // ERROR "r func literal does not escape"
+ return x + func(y int) int { // ERROR "can inline r.func2.1"
+ return 2*y + x*z
+ }(x) // ERROR "inlining call to r.func2.1"
+ }
+ return foo(42) + bar(42) // ERROR "inlining call to r.func1"
+}
+
+func s0(x int) int {
+ foo := func() { // ERROR "can inline s0.func1" "s0 func literal does not escape"
+ x = x + 1
+ }
+ foo() // ERROR "inlining call to s0.func1" "&x does not escape"
+ return x
+}
+
+func s1(x int) int {
+ foo := func() int { // ERROR "can inline s1.func1" "s1 func literal does not escape"
+ return x
+ }
+ x = x + 1
+ return foo() // ERROR "inlining call to s1.func1" "&x does not escape"
+}
+
// can't currently inline functions with a break statement
func switchBreak(x, y int) int {
var n int