Skip to content

Commit 3be2176

Browse files
mdempskygopherbot
authored andcommitted
cmd/compile: improve ir.StaticValue and extract ir.StaticCalleeName
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 <[email protected]> Run-TryBot: Matthew Dempsky <[email protected]> Auto-Submit: Matthew Dempsky <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent e95ca91 commit 3be2176

File tree

4 files changed

+51
-19
lines changed

4 files changed

+51
-19
lines changed

src/cmd/compile/internal/escape/call.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,8 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
7575
call.X.(*ir.ClosureExpr).Func.SetClosureCalled(true)
7676
}
7777

78-
switch v := ir.StaticValue(call.X); v.Op() {
79-
case ir.ONAME:
80-
if v := v.(*ir.Name); v.Class == ir.PFUNC {
81-
fn = v
82-
}
83-
case ir.OCLOSURE:
84-
fn = v.(*ir.ClosureExpr).Func.Nname
85-
case ir.OMETHEXPR:
86-
fn = ir.MethodExprName(v)
87-
}
78+
v := ir.StaticValue(call.X)
79+
fn = ir.StaticCalleeName(v)
8880
case ir.OCALLMETH:
8981
base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
9082
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
583583

584584
// Determine if the callee edge is for an inlinable hot callee or not.
585585
if v.profile != nil && v.curFunc != nil {
586-
if fn := inlCallee(n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
586+
if fn := inlCallee(v.curFunc, n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
587587
lineOffset := pgo.NodeLineOffset(n, fn)
588588
csi := pgo.CallSiteInfo{LineOffset: lineOffset, Caller: v.curFunc}
589589
if _, o := candHotEdgeMap[csi]; o {
@@ -599,7 +599,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
599599
break
600600
}
601601

602-
if fn := inlCallee(n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
602+
if fn := inlCallee(v.curFunc, n.X, v.profile); fn != nil && typecheck.HaveInlineBody(fn) {
603603
v.budget -= fn.Inl.Cost
604604
break
605605
}
@@ -940,7 +940,7 @@ func inlnode(n ir.Node, bigCaller bool, inlCalls *[]*ir.InlinedCallExpr, edit fu
940940
if ir.IsIntrinsicCall(call) {
941941
break
942942
}
943-
if fn := inlCallee(call.X, profile); fn != nil && typecheck.HaveInlineBody(fn) {
943+
if fn := inlCallee(ir.CurFunc, call.X, profile); fn != nil && typecheck.HaveInlineBody(fn) {
944944
n = mkinlcall(call, fn, bigCaller, inlCalls)
945945
}
946946
}
@@ -952,7 +952,7 @@ func inlnode(n ir.Node, bigCaller bool, inlCalls *[]*ir.InlinedCallExpr, edit fu
952952

953953
// inlCallee takes a function-typed expression and returns the underlying function ONAME
954954
// that it refers to if statically known. Otherwise, it returns nil.
955-
func inlCallee(fn ir.Node, profile *pgo.Profile) *ir.Func {
955+
func inlCallee(caller *ir.Func, fn ir.Node, profile *pgo.Profile) (res *ir.Func) {
956956
fn = ir.StaticValue(fn)
957957
switch fn.Op() {
958958
case ir.OMETHEXPR:
@@ -973,6 +973,9 @@ func inlCallee(fn ir.Node, profile *pgo.Profile) *ir.Func {
973973
case ir.OCLOSURE:
974974
fn := fn.(*ir.ClosureExpr)
975975
c := fn.Func
976+
if len(c.ClosureVars) != 0 && c.ClosureVars[0].Outer.Curfn != caller {
977+
return nil // inliner doesn't support inlining across closure frames
978+
}
976979
CanInline(c, profile)
977980
return c
978981
}

src/cmd/compile/internal/ir/expr.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,20 @@ func IsAddressable(n Node) bool {
847847
return false
848848
}
849849

850+
// StaticValue analyzes n to find the earliest expression that always
851+
// evaluates to the same value as n, which might be from an enclosing
852+
// function.
853+
//
854+
// For example, given:
855+
//
856+
// var x int = g()
857+
// func() {
858+
// y := x
859+
// *p = int(y)
860+
// }
861+
//
862+
// calling StaticValue on the "int(y)" expression returns the outer
863+
// "g()" expression.
850864
func StaticValue(n Node) Node {
851865
for {
852866
if n.Op() == OCONVNOP {
@@ -867,14 +881,11 @@ func StaticValue(n Node) Node {
867881
}
868882
}
869883

870-
// staticValue1 implements a simple SSA-like optimization. If n is a local variable
871-
// that is initialized and never reassigned, staticValue1 returns the initializer
872-
// expression. Otherwise, it returns nil.
873884
func staticValue1(nn Node) Node {
874885
if nn.Op() != ONAME {
875886
return nil
876887
}
877-
n := nn.(*Name)
888+
n := nn.(*Name).Canonical()
878889
if n.Class != PAUTO {
879890
return nil
880891
}
@@ -928,6 +939,10 @@ func Reassigned(name *Name) bool {
928939
return true
929940
}
930941

942+
if name.Addrtaken() {
943+
return true // conservatively assume it's reassigned indirectly
944+
}
945+
931946
// TODO(mdempsky): This is inefficient and becoming increasingly
932947
// unwieldy. Figure out a way to generalize escape analysis's
933948
// reassignment detection for use by inlining and devirtualization.
@@ -964,7 +979,7 @@ func Reassigned(name *Name) bool {
964979
case OADDR:
965980
n := n.(*AddrExpr)
966981
if isName(n.X) {
967-
return true
982+
base.FatalfAt(n.Pos(), "%v not marked addrtaken", name)
968983
}
969984
case ORANGE:
970985
n := n.(*RangeStmt)
@@ -982,6 +997,23 @@ func Reassigned(name *Name) bool {
982997
return Any(name.Curfn, do)
983998
}
984999

1000+
// StaticCalleeName returns the ONAME/PFUNC for n, if known.
1001+
func StaticCalleeName(n Node) *Name {
1002+
switch n.Op() {
1003+
case OMETHEXPR:
1004+
n := n.(*SelectorExpr)
1005+
return MethodExprName(n)
1006+
case ONAME:
1007+
n := n.(*Name)
1008+
if n.Class == PFUNC {
1009+
return n
1010+
}
1011+
case OCLOSURE:
1012+
return n.(*ClosureExpr).Func.Nname
1013+
}
1014+
return nil
1015+
}
1016+
9851017
// IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation.
9861018
var IsIntrinsicCall = func(*CallExpr) bool { return false }
9871019

src/cmd/compile/internal/noder/reader.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,6 +3483,11 @@ func unifiedInlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.Inlined
34833483

34843484
r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
34853485
for i, cv := range r.inlFunc.ClosureVars {
3486+
// TODO(mdempsky): It should be possible to support this case, but
3487+
// for now we rely on the inliner avoiding it.
3488+
if cv.Outer.Curfn != callerfn {
3489+
base.FatalfAt(call.Pos(), "inlining closure call across frames")
3490+
}
34863491
r.closureVars[i] = cv.Outer
34873492
}
34883493
if len(r.closureVars) != 0 && r.hasTypeParams() {

0 commit comments

Comments
 (0)