cmd/compile/internal/inline/inlheur: enhance call result scoring

This patch makes a small enhancement to call result scoring, to make
it more independent of param value heuristics. For this pair of
functions:

  func caller() {
     v := callee(10)         <<-- this callsite
     if v > 101 {
        ...
     }
  }
  func callee(x int) {
     if x < 0 {
       G = 1
     }
     return 9
  }

The score for the specified call site above would be adjusted only
once, for the "pass constant to parameter that feeds 'if' statement"
heuristic, which didn't reflect the fact that doing the inline enables
not one but two specific deadcode opportunities (first for the code
inside the inlined routine body, then for the "if" downstream of the
inlined call).

This patch changes the call result scoring machinery to use a separate
set of mask bits, so that we can more accurately handle the case
above.

Change-Id: I700166d0c990c037215b9f904e9984886986c600
Reviewed-on: https://go-review.googlesource.com/c/go/+/529117
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Than McIntosh 2023-09-15 15:06:06 -04:00
parent f56c293319
commit 9e90a15ba4
4 changed files with 89 additions and 60 deletions

View File

@ -267,10 +267,8 @@ func (rua *resultUseAnalyzer) callTargetCheckResults(call ir.Node) {
rua.fn.Sym().Name, rname)
}
if cs := rua.returnHasProp(rname, ResultIsConcreteTypeConvertedToInterface); cs != nil {
// FIXME: add cond level support here
adj := passConcreteToItfCallAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
adj = callResultRescoreAdj
adj := returnFeedsConcreteToInterfaceCallAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
}
case ir.OCALLFUNC:
@ -285,17 +283,12 @@ func (rua *resultUseAnalyzer) callTargetCheckResults(call ir.Node) {
}
}
if cs := rua.returnHasProp(rname, ResultAlwaysSameInlinableFunc); cs != nil {
// FIXME: add cond level support here
adj := passInlinableFuncToIndCallAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
adj = callResultRescoreAdj
adj := returnFeedsInlinableFuncToIndCallAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
} else if cs := rua.returnHasProp(rname, ResultAlwaysSameFunc); cs != nil {
// FIXME: add cond level support here
adj := passFuncToIndCallAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
adj = callResultRescoreAdj
adj := returnFeedsFuncToIndCallAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
}
}
}
@ -351,10 +344,7 @@ func (rua *resultUseAnalyzer) foldCheckResults(cond ir.Node) {
if !ShouldFoldIfNameConstant(cond, namesUsed) {
return
}
// FIXME: add cond level support here
adj := passConstToIfAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
adj = callResultRescoreAdj
adj := returnFeedsConstToIfAdj
cs.Score, cs.ScoreMask = adjustScore(adj, cs.Score, cs.ScoreMask)
}

View File

@ -20,29 +20,33 @@ func _() {
_ = x[passFuncToNestedIndCallAdj-256]
_ = x[passInlinableFuncToIndCallAdj-512]
_ = x[passInlinableFuncToNestedIndCallAdj-1024]
_ = x[callResultRescoreAdj-2048]
_ = x[lastAdj-2048]
_ = x[returnFeedsConstToIfAdj-2048]
_ = x[returnFeedsFuncToIndCallAdj-4096]
_ = x[returnFeedsInlinableFuncToIndCallAdj-8192]
_ = x[returnFeedsConcreteToInterfaceCallAdj-16384]
}
var _scoreAdjustTyp_value = [...]uint64{
0x1, /* panicPathAdj */
0x2, /* initFuncAdj */
0x4, /* inLoopAdj */
0x8, /* passConstToIfAdj */
0x10, /* passConstToNestedIfAdj */
0x20, /* passConcreteToItfCallAdj */
0x40, /* passConcreteToNestedItfCallAdj */
0x80, /* passFuncToIndCallAdj */
0x100, /* passFuncToNestedIndCallAdj */
0x200, /* passInlinableFuncToIndCallAdj */
0x400, /* passInlinableFuncToNestedIndCallAdj */
0x800, /* callResultRescoreAdj */
0x800, /* lastAdj */
0x1, /* panicPathAdj */
0x2, /* initFuncAdj */
0x4, /* inLoopAdj */
0x8, /* passConstToIfAdj */
0x10, /* passConstToNestedIfAdj */
0x20, /* passConcreteToItfCallAdj */
0x40, /* passConcreteToNestedItfCallAdj */
0x80, /* passFuncToIndCallAdj */
0x100, /* passFuncToNestedIndCallAdj */
0x200, /* passInlinableFuncToIndCallAdj */
0x400, /* passInlinableFuncToNestedIndCallAdj */
0x800, /* returnFeedsConstToIfAdj */
0x1000, /* returnFeedsFuncToIndCallAdj */
0x2000, /* returnFeedsInlinableFuncToIndCallAdj */
0x4000, /* returnFeedsConcreteToInterfaceCallAdj */
}
const _scoreAdjustTyp_name = "panicPathAdjinitFuncAdjinLoopAdjpassConstToIfAdjpassConstToNestedIfAdjpassConcreteToItfCallAdjpassConcreteToNestedItfCallAdjpassFuncToIndCallAdjpassFuncToNestedIndCallAdjpassInlinableFuncToIndCallAdjpassInlinableFuncToNestedIndCallAdjcallResultRescoreAdjlastAdj"
const _scoreAdjustTyp_name = "panicPathAdjinitFuncAdjinLoopAdjpassConstToIfAdjpassConstToNestedIfAdjpassConcreteToItfCallAdjpassConcreteToNestedItfCallAdjpassFuncToIndCallAdjpassFuncToNestedIndCallAdjpassInlinableFuncToIndCallAdjpassInlinableFuncToNestedIndCallAdjreturnFeedsConstToIfAdjreturnFeedsFuncToIndCallAdjreturnFeedsInlinableFuncToIndCallAdjreturnFeedsConcreteToInterfaceCallAdj"
var _scoreAdjustTyp_index = [...]uint16{0, 12, 23, 32, 48, 70, 94, 124, 144, 170, 199, 234, 254, 261}
var _scoreAdjustTyp_index = [...]uint16{0, 12, 23, 32, 48, 70, 94, 124, 144, 170, 199, 234, 257, 284, 320, 357}
func (i scoreAdjustTyp) String() string {
var b bytes.Buffer

View File

@ -19,10 +19,35 @@ import (
// in which we'll adjust the score of a given callsite.
type scoreAdjustTyp uint
// These constants capture the various ways in which the inliner's
// scoring phase can adjust a callsite score based on heuristics. They
// fall broadly into three categories:
//
// 1) adjustments based solely on the callsite context (ex: call
// appears on panic path)
//
// 2) adjustments that take into account specific interesting values
// passed at a call site (ex: passing a constant that could result in
// cprop/deadcode in the caller)
//
// 3) adjustments that take into account values returned from the call
// at a callsite (ex: call always returns the same inlinable function,
// and return value flows unmodified into an indirect call)
//
// For categories 2 and 3 above, each adjustment can have either a
// "must" version and a "may" version (but not both). Here the idea is
// that in the "must" version the value flow is unconditional: if the
// callsite executes, then the condition we're interested in (ex:
// param feeding call) is guaranteed to happen. For the "may" version,
// there may be control flow that could cause the benefit to be
// bypassed.
const (
// Catgegory 1 adjustments (see above)
panicPathAdj scoreAdjustTyp = (1 << iota)
initFuncAdj
inLoopAdj
// Category 2 adjustments (see above).
passConstToIfAdj
passConstToNestedIfAdj
passConcreteToItfCallAdj
@ -31,8 +56,12 @@ const (
passFuncToNestedIndCallAdj
passInlinableFuncToIndCallAdj
passInlinableFuncToNestedIndCallAdj
callResultRescoreAdj
lastAdj scoreAdjustTyp = callResultRescoreAdj
// Category 3 adjustments.
returnFeedsConstToIfAdj
returnFeedsFuncToIndCallAdj
returnFeedsInlinableFuncToIndCallAdj
returnFeedsConcreteToInterfaceCallAdj
)
// This table records the specific values we use to adjust call
@ -42,18 +71,21 @@ const (
// what value for each one produces the best performance.
var adjValues = map[scoreAdjustTyp]int{
panicPathAdj: 40,
initFuncAdj: 20,
inLoopAdj: -5,
passConstToIfAdj: -20,
passConstToNestedIfAdj: -15,
passConcreteToItfCallAdj: -30,
passConcreteToNestedItfCallAdj: -25,
passFuncToIndCallAdj: -25,
passFuncToNestedIndCallAdj: -20,
passInlinableFuncToIndCallAdj: -45,
passInlinableFuncToNestedIndCallAdj: -40,
callResultRescoreAdj: 0,
panicPathAdj: 40,
initFuncAdj: 20,
inLoopAdj: -5,
passConstToIfAdj: -20,
passConstToNestedIfAdj: -15,
passConcreteToItfCallAdj: -30,
passConcreteToNestedItfCallAdj: -25,
passFuncToIndCallAdj: -25,
passFuncToNestedIndCallAdj: -20,
passInlinableFuncToIndCallAdj: -45,
passInlinableFuncToNestedIndCallAdj: -40,
returnFeedsConstToIfAdj: -15,
returnFeedsFuncToIndCallAdj: -25,
returnFeedsInlinableFuncToIndCallAdj: -40,
returnFeedsConcreteToInterfaceCallAdj: -25,
}
func adjValue(x scoreAdjustTyp) int {

View File

@ -12,7 +12,7 @@ package returns2
// returns2.go T_return_feeds_iface_call 18 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[],"ResultFlags":[]}
// callsite: returns2.go:19:13|0 flagstr "" flagval 0 score -4 mask 2080 maskstr "passConcreteToItfCallAdj|callResultRescoreAdj"
// callsite: returns2.go:19:13|0 flagstr "" flagval 0 score 1 mask 16384 maskstr "returnFeedsConcreteToInterfaceCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_return_feeds_iface_call() {
@ -23,7 +23,7 @@ func T_return_feeds_iface_call() {
// returns2.go T_multi_return_feeds_iface_call 29 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[],"ResultFlags":[]}
// callsite: returns2.go:30:20|0 flagstr "" flagval 0 score -2 mask 2080 maskstr "passConcreteToItfCallAdj|callResultRescoreAdj"
// callsite: returns2.go:30:20|0 flagstr "" flagval 0 score 3 mask 16384 maskstr "returnFeedsConcreteToInterfaceCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_multi_return_feeds_iface_call() {
@ -34,12 +34,12 @@ func T_multi_return_feeds_iface_call() {
// returns2.go T_returned_inlinable_func_feeds_indirect_call 41 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[]}
// callsite: returns2.go:42:18|0 flagstr "" flagval 0 score -43 mask 2560 maskstr "passInlinableFuncToIndCallAdj|callResultRescoreAdj"
// callsite: returns2.go:44:20|1 flagstr "" flagval 0 score -28 mask 2560 maskstr "passInlinableFuncToIndCallAdj|callResultRescoreAdj"
// callsite: returns2.go:42:18|0 flagstr "" flagval 0 score -51 mask 8200 maskstr "passConstToIfAdj|returnFeedsInlinableFuncToIndCallAdj"
// callsite: returns2.go:44:20|1 flagstr "" flagval 0 score -23 mask 8192 maskstr "returnFeedsInlinableFuncToIndCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_returned_inlinable_func_feeds_indirect_call(q int) {
f := returnsFunc()
f := returnsFunc(10)
f(q)
f2 := returnsFunc2()
f2(q)
@ -48,7 +48,7 @@ func T_returned_inlinable_func_feeds_indirect_call(q int) {
// returns2.go T_returned_noninlineable_func_feeds_indirect_call 54 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[]}
// callsite: returns2.go:55:30|0 flagstr "" flagval 0 score -23 mask 2176 maskstr "passFuncToIndCallAdj|callResultRescoreAdj"
// callsite: returns2.go:55:30|0 flagstr "" flagval 0 score -23 mask 4096 maskstr "returnFeedsFuncToIndCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_returned_noninlineable_func_feeds_indirect_call(q int) {
@ -59,7 +59,7 @@ func T_returned_noninlineable_func_feeds_indirect_call(q int) {
// returns2.go T_multi_return_feeds_indirect_call 65 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[]}
// callsite: returns2.go:66:29|0 flagstr "" flagval 0 score -26 mask 2560 maskstr "passInlinableFuncToIndCallAdj|callResultRescoreAdj"
// callsite: returns2.go:66:29|0 flagstr "" flagval 0 score -21 mask 8192 maskstr "returnFeedsInlinableFuncToIndCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_multi_return_feeds_indirect_call(q int) {
@ -70,7 +70,7 @@ func T_multi_return_feeds_indirect_call(q int) {
// returns2.go T_return_feeds_ifswitch 76 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[0]}
// callsite: returns2.go:77:14|0 flagstr "" flagval 0 score 5 mask 2056 maskstr "passConstToIfAdj|callResultRescoreAdj"
// callsite: returns2.go:77:14|0 flagstr "" flagval 0 score 10 mask 2048 maskstr "returnFeedsConstToIfAdj"
// <endcallsites>
// <endfuncpreamble>
func T_return_feeds_ifswitch(q int) int {
@ -87,7 +87,7 @@ func T_return_feeds_ifswitch(q int) int {
// returns2.go T_multi_return_feeds_ifswitch 93 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0],"ResultFlags":[0]}
// callsite: returns2.go:94:21|0 flagstr "" flagval 0 score 4 mask 2056 maskstr "passConstToIfAdj|callResultRescoreAdj"
// callsite: returns2.go:94:21|0 flagstr "" flagval 0 score 9 mask 2048 maskstr "returnFeedsConstToIfAdj"
// <endcallsites>
// <endfuncpreamble>
func T_multi_return_feeds_ifswitch(q int) int {
@ -126,19 +126,19 @@ func T_two_calls_feed_ifswitch(q int) int {
// returns2.go T_chained_indirect_call 132 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0,0],"ResultFlags":[]}
// callsite: returns2.go:135:18|0 flagstr "" flagval 0 score -43 mask 2560 maskstr "passInlinableFuncToIndCallAdj|callResultRescoreAdj"
// callsite: returns2.go:135:18|0 flagstr "" flagval 0 score -31 mask 8192 maskstr "returnFeedsInlinableFuncToIndCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_chained_indirect_call(x, y int) {
// Here 'returnsFunc' returns an inlinable func that feeds
// directly into a call (no named intermediate).
G += returnsFunc()(x + y)
G += returnsFunc(x - y)(x + y)
}
// returns2.go T_chained_conc_iface_call 144 0 1
// <endpropsdump>
// {"Flags":0,"ParamFlags":[0,0],"ResultFlags":[]}
// callsite: returns2.go:148:8|0 flagstr "" flagval 0 score -4 mask 2080 maskstr "passConcreteToItfCallAdj|callResultRescoreAdj"
// callsite: returns2.go:148:8|0 flagstr "" flagval 0 score 1 mask 16384 maskstr "returnFeedsConcreteToInterfaceCallAdj"
// <endcallsites>
// <endfuncpreamble>
func T_chained_conc_iface_call(x, y int) {
@ -148,7 +148,10 @@ func T_chained_conc_iface_call(x, y int) {
newBar(10).Plark().Plark()
}
func returnsFunc() func(int) int {
func returnsFunc(x int) func(int) int {
if x < 0 {
G++
}
return adder
}