Skip to content

Commit ca5ba14

Browse files
committed
cmd/compile/internal: add a PGO devirt post-lookup cleanup hook
The PGO-based devirtualization helper pgoir.addIndirectEdges makes a series of calls into the unified IR reader to import functions that would not normally be imported but may be the target of a hot indirect call from the current package. This importing primarily targets at non-generic functions and methods, but as part of the process we can encounter types that have methods (including generic methods) whose bodies need to be read in. When the reader encounters an inlinable func of this sort, it may (depending on the context) decide not to read the body right away, but instead adds the func to a list ("todoBodies") to be read in later on in a more convenient context. In the bug in question, a hot method lookup takes place in pgoir.addIndirectEdges, and as part of the import process we wind up with a type T with method M that is in this partially created state, and in addition T gets added to the unified IR's list of types that may need method wrappers. During wrapper generation we create a new wrapper "(*T).M" whose body has a call to "T.M", then farther on down the pike during escape analysis we try to analyze the two functions; this causes a crash due to "T.M" being in partially constructed state. As a fix, add a new "PostLookupCleanup" hook (in the unified IR reader) that pgoir.addIndirectEdges can invoke that takes care of reading in the bodies of any functions that have been added to the "todoBodies" list. [Note: creating a test case for this problem is proving to be very tricky; a new test will be added in a subsequent patch]. Fixes #67746. Change-Id: Ibc47ee79e08a55421728d35341df80a865231cff Reviewed-on: https://go-review.googlesource.com/c/go/+/591075 Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Reviewed-by: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 97bc577 commit ca5ba14

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

src/cmd/compile/internal/devirtualize/pgo.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -364,11 +364,15 @@ func constructCallStat(p *pgoir.Profile, fn *ir.Func, name string, call *ir.Call
364364
return e.Dst.Name() < stat.Hottest
365365
}
366366

367+
callerNode := p.WeightedCG.IRNodes[name]
368+
if callerNode == nil {
369+
return nil
370+
}
371+
367372
// Sum of all edges from this callsite, regardless of callee.
368373
// For direct calls, this should be the same as the single edge
369374
// weight (except for multiple calls on one line, which we
370375
// can't distinguish).
371-
callerNode := p.WeightedCG.IRNodes[name]
372376
for _, edge := range callerNode.OutEdges {
373377
if edge.CallSiteOffset != offset {
374378
continue
@@ -656,6 +660,10 @@ func findHotConcreteCallee(p *pgoir.Profile, caller *ir.Func, call *ir.CallExpr,
656660
callerNode := p.WeightedCG.IRNodes[callerName]
657661
callOffset := pgoir.NodeLineOffset(call, caller)
658662

663+
if callerNode == nil {
664+
return nil, 0
665+
}
666+
659667
var hottest *pgoir.IREdge
660668

661669
// Returns true if e is hotter than hottest.

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

+9
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ func LookupFunc(fullName string) (*ir.Func, error) {
6969
return nil, fmt.Errorf("%s is not a function (%v) or method (%v)", fullName, err, mErr)
7070
}
7171

72+
// PostLookupCleanup performs cleanup operations needed
73+
// after a series of calls to LookupFunc, specifically invoking
74+
// readBodies to post-process any funcs on the "todoBodies" list
75+
// that were added as a result of the lookup operations.
76+
func PostLookupCleanup() {
77+
readBodies(typecheck.Target, false)
78+
}
79+
7280
func lookupFunction(pkg *types.Pkg, symName string) (*ir.Func, error) {
7381
sym := pkg.Lookup(symName)
7482

@@ -179,6 +187,7 @@ func unified(m posMap, noders []*noder) {
179187
inline.InlineCall = unifiedInlineCall
180188
typecheck.HaveInlineBody = unifiedHaveInlineBody
181189
pgoir.LookupFunc = LookupFunc
190+
pgoir.PostLookupCleanup = PostLookupCleanup
182191

183192
data := writePkgStub(m, noders)
184193

src/cmd/compile/internal/pgoir/irgraph.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,16 @@ func addIREdge(callerNode *IRNode, callerName string, call ir.Node, callee *ir.F
267267
// LookupFunc looks up a function or method in export data. It is expected to
268268
// be overridden by package noder, to break a dependency cycle.
269269
var LookupFunc = func(fullName string) (*ir.Func, error) {
270-
base.Fatalf("pgo.LookupMethodFunc not overridden")
270+
base.Fatalf("pgoir.LookupMethodFunc not overridden")
271+
panic("unreachable")
272+
}
273+
274+
// PostLookupCleanup performs any remaining cleanup operations needed
275+
// after a series of calls to LookupFunc, specifically reading in the
276+
// bodies of functions that may have been delayed due being encountered
277+
// in a stage where the reader's curfn state was not set up.
278+
var PostLookupCleanup = func() {
279+
base.Fatalf("pgoir.PostLookupCleanup not overridden")
271280
panic("unreachable")
272281
}
273282

@@ -386,6 +395,8 @@ func addIndirectEdges(g *IRGraph, namedEdgeMap pgo.NamedEdgeMap) {
386395
}
387396
callerNode.OutEdges[key] = edge
388397
}
398+
399+
PostLookupCleanup()
389400
}
390401

391402
// PrintWeightedCallGraphDOT prints IRGraph in DOT format.

0 commit comments

Comments
 (0)