@@ -9,6 +9,9 @@ const LANDINGPAD_PENALTY: usize = 50;
9
9
const RESUME_PENALTY : usize = 45 ;
10
10
const LARGE_SWITCH_PENALTY : usize = 20 ;
11
11
const CONST_SWITCH_BONUS : usize = 10 ;
12
+ const MULTIPLE_MUT_PENALTY : usize = 50 ;
13
+ const REF_COPY_BONUS : usize = 5 ;
14
+ const MANY_PARAMETERS_BONUS : usize = 10 ;
12
15
13
16
/// Verify that the callee body is compatible with the caller.
14
17
#[ derive( Clone ) ]
@@ -31,6 +34,75 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
31
34
CostChecker { tcx, param_env, callee_body, instance, penalty : 0 , bonus : 0 }
32
35
}
33
36
37
+ /// Add function-level costs not well-represented by the block-level costs.
38
+ ///
39
+ /// Needed because the `CostChecker` is used sometimes for just blocks,
40
+ /// and even the full `Inline` doesn't call `visit_body`, so there's nowhere
41
+ /// to put this logic in the visitor.
42
+ pub fn add_function_level_costs ( & mut self ) {
43
+ // There's usually extra stack costs to passing lots of parameters,
44
+ // so we'd rather inline that if the method isn't actually complex,
45
+ // especially for trait impls that ignore some parameters.
46
+ if self . callee_body . arg_count > 2 {
47
+ self . bonus += MANY_PARAMETERS_BONUS ;
48
+ }
49
+
50
+ fn is_call_like ( bbd : & BasicBlockData < ' _ > ) -> bool {
51
+ use TerminatorKind :: * ;
52
+ match bbd. terminator ( ) . kind {
53
+ Call { .. }
54
+ | Drop { .. }
55
+ | Assert { .. }
56
+ | Yield { .. }
57
+ | CoroutineDrop
58
+ | InlineAsm { .. } => true ,
59
+
60
+ Goto { .. }
61
+ | SwitchInt { .. }
62
+ | UnwindResume
63
+ | UnwindTerminate ( _)
64
+ | Return
65
+ | Unreachable => false ,
66
+
67
+ FalseEdge { .. } | FalseUnwind { .. } => unreachable ! ( ) ,
68
+ }
69
+ }
70
+
71
+ // If the only has one Call (or similar), inlining isn't increasing the total
72
+ // number of calls, so give extra encouragement to inlining that.
73
+ if self . callee_body . basic_blocks . iter ( ) . filter ( |bbd| is_call_like ( bbd) ) . count ( ) == 1 {
74
+ self . bonus += CALL_PENALTY ;
75
+ }
76
+
77
+ let callee_param_env = std:: cell:: LazyCell :: new ( || {
78
+ let def_id = self . callee_body . source . def_id ( ) ;
79
+ self . tcx . param_env ( def_id)
80
+ } ) ;
81
+
82
+ let mut mut_ref_count = 0 ;
83
+ for local in self . callee_body . args_iter ( ) {
84
+ let ty = self . callee_body . local_decls [ local] . ty ;
85
+ if let ty:: Ref ( _, referee, mtbl) = ty. kind ( ) {
86
+ match mtbl {
87
+ Mutability :: Mut => mut_ref_count += 1 ,
88
+ Mutability :: Not => {
89
+ // Encourage inlining `&impl Copy` parameters,
90
+ // as that often lets us remove the indirection.
91
+ if referee. is_copy_modulo_regions ( self . tcx , * callee_param_env) {
92
+ self . bonus += REF_COPY_BONUS ;
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ // Discourage inlining something with multiple `&mut` parameters,
100
+ // as the MIR inliner doesn't know how to preserve `noalias`.
101
+ if mut_ref_count > 1 {
102
+ self . penalty += MULTIPLE_MUT_PENALTY ;
103
+ }
104
+ }
105
+
34
106
pub fn cost ( & self ) -> usize {
35
107
usize:: saturating_sub ( self . penalty , self . bonus )
36
108
}
0 commit comments