]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: improve ir.StaticValue and extract ir.StaticCalleeName
authorMatthew Dempsky <mdempsky@google.com>
Sat, 12 Aug 2023 01:21:22 +0000 (18:21 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 15 Aug 2023 20:43:50 +0000 (20:43 +0000)
This CL extends ir.StaticValue to also work on closure variables.

Also, it extracts the code from escape analysis that's responsible for
determining the static callee of a function. This will be useful when
go/defer statement normalization is moved to typecheck.

Change-Id: I69e1f7fb185658dc9fbfdc69d0f511c84df1d3ac
Reviewed-on: https://go-review.googlesource.com/c/go/+/518959
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/compile/internal/escape/call.go
src/cmd/compile/internal/inline/inl.go
src/cmd/compile/internal/ir/expr.go
src/cmd/compile/internal/noder/reader.go

index 704b2e9dd10c8d7b8c5ca916bf9fb457f2703b1f..bfba651cef25b874901c9d26fd7ae0cb51ec9f4d 100644 (file)
@@ -75,16 +75,8 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
                                call.X.(*ir.ClosureExpr).Func.SetClosureCalled(true)
                        }
 
-                       switch v := ir.StaticValue(call.X); v.Op() {
-                       case ir.ONAME:
-                               if v := v.(*ir.Name); v.Class == ir.PFUNC {
-                                       fn = v
-                               }
-                       case ir.OCLOSURE:
-                               fn = v.(*ir.ClosureExpr).Func.Nname
-                       case ir.OMETHEXPR:
-                               fn = ir.MethodExprName(v)
-                       }
+                       v := ir.StaticValue(call.X)
+                       fn = ir.StaticCalleeName(v)
                case ir.OCALLMETH:
                        base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
                }
index 9003cbab7023f25e9d77b6bebf58aaf16ebd8904..b51498a56ce507a66eead4967459c8dac1012070 100644 (file)
@@ -583,7 +583,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
 
                // Determine if the callee edge is for an inlinable hot callee or not.
                if v.profile != nil && v.curFunc != nil {
-                       if fn := inlCallee(n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
+                       if fn := inlCallee(v.curFunc, n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
                                lineOffset := pgo.NodeLineOffset(n, fn)
                                csi := pgo.CallSiteInfo{LineOffset: lineOffset, Caller: v.curFunc}
                                if _, o := candHotEdgeMap[csi]; o {
@@ -599,7 +599,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
                        break
                }
 
-               if fn := inlCallee(n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
+               if fn := inlCallee(v.curFunc, n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
                        v.budget -= fn.Inl.Cost
                        break
                }
@@ -940,7 +940,7 @@ func inlnode(n ir.Node, bigCaller bool, inlCalls *[]*ir.InlinedCallExpr, edit fu
                if ir.IsIntrinsicCall(call) {
                        break
                }
-               if fn := inlCallee(call.X, profile); fn != nil && typecheck.HaveInlineBody(fn) {
+               if fn := inlCallee(ir.CurFunc, call.X, profile); fn != nil && typecheck.HaveInlineBody(fn) {
                        n = mkinlcall(call, fn, bigCaller, inlCalls)
                }
        }
@@ -952,7 +952,7 @@ func inlnode(n ir.Node, bigCaller bool, inlCalls *[]*ir.InlinedCallExpr, edit fu
 
 // inlCallee takes a function-typed expression and returns the underlying function ONAME
 // that it refers to if statically known. Otherwise, it returns nil.
-func inlCallee(fn ir.Node, profile *pgo.Profile) *ir.Func {
+func inlCallee(caller *ir.Func, fn ir.Node, profile *pgo.Profile) (res *ir.Func) {
        fn = ir.StaticValue(fn)
        switch fn.Op() {
        case ir.OMETHEXPR:
@@ -973,6 +973,9 @@ func inlCallee(fn ir.Node, profile *pgo.Profile) *ir.Func {
        case ir.OCLOSURE:
                fn := fn.(*ir.ClosureExpr)
                c := fn.Func
+               if len(c.ClosureVars) != 0 && c.ClosureVars[0].Outer.Curfn != caller {
+                       return nil // inliner doesn't support inlining across closure frames
+               }
                CanInline(c, profile)
                return c
        }
index 02b1733f0402940a930b10a41305d540a2da0ab5..0f44bd8e2133599ba1944fa777befaec0091c56b 100644 (file)
@@ -847,6 +847,20 @@ func IsAddressable(n Node) bool {
        return false
 }
 
+// StaticValue analyzes n to find the earliest expression that always
+// evaluates to the same value as n, which might be from an enclosing
+// function.
+//
+// For example, given:
+//
+//     var x int = g()
+//     func() {
+//             y := x
+//             *p = int(y)
+//     }
+//
+// calling StaticValue on the "int(y)" expression returns the outer
+// "g()" expression.
 func StaticValue(n Node) Node {
        for {
                if n.Op() == OCONVNOP {
@@ -867,14 +881,11 @@ func StaticValue(n Node) Node {
        }
 }
 
-// staticValue1 implements a simple SSA-like optimization. If n is a local variable
-// that is initialized and never reassigned, staticValue1 returns the initializer
-// expression. Otherwise, it returns nil.
 func staticValue1(nn Node) Node {
        if nn.Op() != ONAME {
                return nil
        }
-       n := nn.(*Name)
+       n := nn.(*Name).Canonical()
        if n.Class != PAUTO {
                return nil
        }
@@ -928,6 +939,10 @@ func Reassigned(name *Name) bool {
                return true
        }
 
+       if name.Addrtaken() {
+               return true // conservatively assume it's reassigned indirectly
+       }
+
        // TODO(mdempsky): This is inefficient and becoming increasingly
        // unwieldy. Figure out a way to generalize escape analysis's
        // reassignment detection for use by inlining and devirtualization.
@@ -964,7 +979,7 @@ func Reassigned(name *Name) bool {
                case OADDR:
                        n := n.(*AddrExpr)
                        if isName(n.X) {
-                               return true
+                               base.FatalfAt(n.Pos(), "%v not marked addrtaken", name)
                        }
                case ORANGE:
                        n := n.(*RangeStmt)
@@ -982,6 +997,23 @@ func Reassigned(name *Name) bool {
        return Any(name.Curfn, do)
 }
 
+// StaticCalleeName returns the ONAME/PFUNC for n, if known.
+func StaticCalleeName(n Node) *Name {
+       switch n.Op() {
+       case OMETHEXPR:
+               n := n.(*SelectorExpr)
+               return MethodExprName(n)
+       case ONAME:
+               n := n.(*Name)
+               if n.Class == PFUNC {
+                       return n
+               }
+       case OCLOSURE:
+               return n.(*ClosureExpr).Func.Nname
+       }
+       return nil
+}
+
 // IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation.
 var IsIntrinsicCall = func(*CallExpr) bool { return false }
 
index 9448f234b7abb464e1124d7f03adbba80ea671b3..42794da042a6a460cc66cf6f9b68f5ec6aaebe37 100644 (file)
@@ -3483,6 +3483,11 @@ func unifiedInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.Inlined
 
        r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
        for i, cv := range r.inlFunc.ClosureVars {
+               // TODO(mdempsky): It should be possible to support this case, but
+               // for now we rely on the inliner avoiding it.
+               if cv.Outer.Curfn != callerfn {
+                       base.FatalfAt(call.Pos(), "inlining closure call across frames")
+               }
                r.closureVars[i] = cv.Outer
        }
        if len(r.closureVars) != 0 && r.hasTypeParams() {