@@ -6,13 +6,16 @@ const INSTR_COST: usize = 5;
6
6
const CALL_PENALTY : usize = 25 ;
7
7
const LANDINGPAD_PENALTY : usize = 50 ;
8
8
const RESUME_PENALTY : usize = 45 ;
9
+ const LARGE_SWITCH_PENALTY : usize = 20 ;
10
+ const CONST_SWITCH_BONUS : usize = 30 ;
9
11
10
12
/// Verify that the callee body is compatible with the caller.
11
13
#[ derive( Clone ) ]
12
14
pub ( crate ) struct CostChecker < ' b , ' tcx > {
13
15
tcx : TyCtxt < ' tcx > ,
14
16
param_env : ParamEnv < ' tcx > ,
15
- cost : usize ,
17
+ penalty : usize ,
18
+ bonus : usize ,
16
19
callee_body : & ' b Body < ' tcx > ,
17
20
instance : Option < ty:: Instance < ' tcx > > ,
18
21
}
@@ -24,11 +27,11 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
24
27
instance : Option < ty:: Instance < ' tcx > > ,
25
28
callee_body : & ' b Body < ' tcx > ,
26
29
) -> CostChecker < ' b , ' tcx > {
27
- CostChecker { tcx, param_env, callee_body, instance, cost : 0 }
30
+ CostChecker { tcx, param_env, callee_body, instance, penalty : 0 , bonus : 0 }
28
31
}
29
32
30
33
pub fn cost ( & self ) -> usize {
31
- self . cost
34
+ usize :: saturating_sub ( self . penalty , self . bonus )
32
35
}
33
36
34
37
fn instantiate_ty ( & self , v : Ty < ' tcx > ) -> Ty < ' tcx > {
@@ -41,14 +44,31 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
41
44
}
42
45
43
46
impl < ' tcx > Visitor < ' tcx > for CostChecker < ' _ , ' tcx > {
44
- fn visit_statement ( & mut self , statement : & Statement < ' tcx > , _ : Location ) {
45
- // Don't count StorageLive/StorageDead in the inlining cost .
47
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
48
+ // Most costs are in rvalues and terminators, not in statements .
46
49
match statement. kind {
47
- StatementKind :: StorageLive ( _)
48
- | StatementKind :: StorageDead ( _)
49
- | StatementKind :: Deinit ( _)
50
- | StatementKind :: Nop => { }
51
- _ => self . cost += INSTR_COST ,
50
+ StatementKind :: Intrinsic ( ref ndi) => {
51
+ self . penalty += match * * ndi {
52
+ NonDivergingIntrinsic :: Assume ( ..) => INSTR_COST ,
53
+ NonDivergingIntrinsic :: CopyNonOverlapping ( ..) => CALL_PENALTY ,
54
+ } ;
55
+ }
56
+ _ => self . super_statement ( statement, location) ,
57
+ }
58
+ }
59
+
60
+ fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , _location : Location ) {
61
+ match rvalue {
62
+ Rvalue :: NullaryOp ( NullOp :: UbChecks , ..) if !self . tcx . sess . ub_checks ( ) => {
63
+ // If this is in optimized MIR it's because it's used later,
64
+ // so if we don't need UB checks this session, give a bonus
65
+ // here to offset the cost of the call later.
66
+ self . bonus += CALL_PENALTY ;
67
+ }
68
+ // These are essentially constants that didn't end up in an Operand,
69
+ // so treat them as also being free.
70
+ Rvalue :: NullaryOp ( ..) => { }
71
+ _ => self . penalty += INSTR_COST ,
52
72
}
53
73
}
54
74
@@ -59,17 +79,17 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
59
79
// If the place doesn't actually need dropping, treat it like a regular goto.
60
80
let ty = self . instantiate_ty ( place. ty ( self . callee_body , tcx) . ty ) ;
61
81
if ty. needs_drop ( tcx, self . param_env ) {
62
- self . cost += CALL_PENALTY ;
82
+ self . penalty += CALL_PENALTY ;
63
83
if let UnwindAction :: Cleanup ( _) = unwind {
64
- self . cost += LANDINGPAD_PENALTY ;
84
+ self . penalty += LANDINGPAD_PENALTY ;
65
85
}
66
86
} else {
67
- self . cost += INSTR_COST ;
87
+ self . penalty += INSTR_COST ;
68
88
}
69
89
}
70
90
TerminatorKind :: Call { func : Operand :: Constant ( ref f) , unwind, .. } => {
71
91
let fn_ty = self . instantiate_ty ( f. const_ . ty ( ) ) ;
72
- self . cost += if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( )
92
+ self . penalty += if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( )
73
93
&& tcx. intrinsic ( def_id) . is_some ( )
74
94
{
75
95
// Don't give intrinsics the extra penalty for calls
@@ -78,23 +98,40 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
78
98
CALL_PENALTY
79
99
} ;
80
100
if let UnwindAction :: Cleanup ( _) = unwind {
81
- self . cost += LANDINGPAD_PENALTY ;
101
+ self . penalty += LANDINGPAD_PENALTY ;
102
+ }
103
+ }
104
+ TerminatorKind :: SwitchInt { ref discr, ref targets } => {
105
+ if discr. constant ( ) . is_some ( ) {
106
+ // Not only will this become a `Goto`, but likely other
107
+ // things will be removable as unreachable.
108
+ self . bonus += CONST_SWITCH_BONUS ;
109
+ } else if targets. all_targets ( ) . len ( ) > 3 {
110
+ // More than false/true/unreachable gets extra cost.
111
+ self . penalty += LARGE_SWITCH_PENALTY ;
112
+ } else {
113
+ self . penalty += INSTR_COST ;
82
114
}
83
115
}
84
- TerminatorKind :: Assert { unwind, .. } => {
85
- self . cost += CALL_PENALTY ;
116
+ TerminatorKind :: Assert { unwind, ref msg, .. } => {
117
+ self . penalty +=
118
+ if msg. is_optional_overflow_check ( ) && !self . tcx . sess . overflow_checks ( ) {
119
+ INSTR_COST
120
+ } else {
121
+ CALL_PENALTY
122
+ } ;
86
123
if let UnwindAction :: Cleanup ( _) = unwind {
87
- self . cost += LANDINGPAD_PENALTY ;
124
+ self . penalty += LANDINGPAD_PENALTY ;
88
125
}
89
126
}
90
- TerminatorKind :: UnwindResume => self . cost += RESUME_PENALTY ,
127
+ TerminatorKind :: UnwindResume => self . penalty += RESUME_PENALTY ,
91
128
TerminatorKind :: InlineAsm { unwind, .. } => {
92
- self . cost += INSTR_COST ;
129
+ self . penalty += INSTR_COST ;
93
130
if let UnwindAction :: Cleanup ( _) = unwind {
94
- self . cost += LANDINGPAD_PENALTY ;
131
+ self . penalty += LANDINGPAD_PENALTY ;
95
132
}
96
133
}
97
- _ => self . cost += INSTR_COST ,
134
+ _ => self . penalty += INSTR_COST ,
98
135
}
99
136
}
100
137
}
0 commit comments