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