return funcProps
}
+// RevisitInlinability revisits the question of whether to continue to
+// treat function 'fn' as an inline candidate based on the set of
+// properties we've computed for it. If (for example) it has an
+// initial size score of 150 and no interesting properties to speak
+// of, then there isn't really any point to moving ahead with it as an
+// inline candidate.
+func RevisitInlinability(fn *ir.Func, budgetForFunc func(*ir.Func) int32) {
+ if fn.Inl == nil {
+ return
+ }
+ entry, ok := fpmap[fn]
+ if !ok {
+ //FIXME: issue error?
+ return
+ }
+ mxAdjust := int32(largestScoreAdjustment(fn, entry.props))
+ budget := budgetForFunc(fn)
+ if fn.Inl.Cost+mxAdjust > budget {
+ fn.Inl = nil
+ }
+}
+
// computeFuncProps examines the Go function 'fn' and computes for it
// a function "properties" object, to be used to drive inlining
// heuristics. See comments on the FuncProps type for more info.
// there may be control flow that could cause the benefit to be
// bypassed.
const (
- // Catgegory 1 adjustments (see above)
+ // Category 1 adjustments (see above)
panicPathAdj scoreAdjustTyp = (1 << iota)
initFuncAdj
inLoopAdj
}
}
-var mayMust = [...]struct{ may, must scoreAdjustTyp }{
+var mayMustAdj = [...]struct{ may, must scoreAdjustTyp }{
{may: passConstToNestedIfAdj, must: passConstToIfAdj},
{may: passConcreteToNestedItfCallAdj, must: passConcreteToItfCallAdj},
{may: passFuncToNestedIndCallAdj, must: passFuncToNestedIndCallAdj},
}
func mayToMust(x scoreAdjustTyp) scoreAdjustTyp {
- for _, v := range mayMust {
+ for _, v := range mayMustAdj {
if x == v.may {
return v.must
}
}
func mustToMay(x scoreAdjustTyp) scoreAdjustTyp {
- for _, v := range mayMust {
+ for _, v := range mayMustAdj {
if x == v.must {
return v.may
}
return score, mask
}
+var resultFlagToPositiveAdj map[ResultPropBits]scoreAdjustTyp
+var paramFlagToPositiveAdj map[ParamPropBits]scoreAdjustTyp
+
+func setupFlagToAdjMaps() {
+ resultFlagToPositiveAdj = map[ResultPropBits]scoreAdjustTyp{
+ ResultIsAllocatedMem: returnFeedsConcreteToInterfaceCallAdj,
+ ResultAlwaysSameFunc: returnFeedsFuncToIndCallAdj,
+ ResultAlwaysSameConstant: returnFeedsConstToIfAdj,
+ }
+ paramFlagToPositiveAdj = map[ParamPropBits]scoreAdjustTyp{
+ ParamMayFeedInterfaceMethodCall: passConcreteToNestedItfCallAdj,
+ ParamFeedsInterfaceMethodCall: passConcreteToItfCallAdj,
+ ParamMayFeedIndirectCall: passInlinableFuncToNestedIndCallAdj,
+ ParamFeedsIndirectCall: passInlinableFuncToIndCallAdj,
+ }
+}
+
+// largestScoreAdjustment tries to estimate the largest possible
+// negative score adjustment that could be applied to a call of the
+// function with the specified props. Example:
+//
+// func foo() { func bar(x int, p *int) int {
+// ... if x < 0 { *p = x }
+// } return 99
+// }
+//
+// Function 'foo' above on the left has no interesting properties,
+// thus as a result the most we'll adjust any call to is the value for
+// "call in loop". If the calculated cost of the function is 150, and
+// the in-loop adjustment is 5 (for example), then there is not much
+// point treating it as inlinable. On the other hand "bar" has a param
+// property (parameter "x" feeds unmodified to an "if" statement") and
+// a return property (always returns same constant) meaning that a
+// given call _could_ be rescored down as much as -35 points-- thus if
+// the size of "bar" is 100 (for example) then there is at least a
+// chance that scoring will enable inlining.
+func largestScoreAdjustment(fn *ir.Func, props *FuncProps) int {
+ if resultFlagToPositiveAdj == nil {
+ setupFlagToAdjMaps()
+ }
+ var tmask scoreAdjustTyp
+ score := adjValues[inLoopAdj] // any call can be in a loop
+ for _, pf := range props.ParamFlags {
+ if adj, ok := paramFlagToPositiveAdj[pf]; ok {
+ score, tmask = adjustScore(adj, score, tmask)
+ }
+ }
+ for _, rf := range props.ResultFlags {
+ if adj, ok := resultFlagToPositiveAdj[rf]; ok {
+ score, tmask = adjustScore(adj, score, tmask)
+ }
+ }
+
+ if debugTrace&debugTraceScoring != 0 {
+ fmt.Fprintf(os.Stderr, "=-= largestScore(%v) is %d\n",
+ fn, score)
+ }
+
+ return score
+}
+
// DumpInlCallSiteScores is invoked by the inliner if the debug flag
// "-d=dumpinlcallsitescores" is set; it dumps out a human-readable
// summary of all (potentially) inlinable callsites in the package,