1+ use rustc_middle:: bug;
12use rustc_middle:: mir:: visit:: * ;
23use rustc_middle:: mir:: * ;
34use rustc_middle:: ty:: { self , ParamEnv , Ty , TyCtxt } ;
@@ -6,13 +7,16 @@ const INSTR_COST: usize = 5;
67const CALL_PENALTY : usize = 25 ;
78const LANDINGPAD_PENALTY : usize = 50 ;
89const RESUME_PENALTY : usize = 45 ;
10+ const LARGE_SWITCH_PENALTY : usize = 20 ;
11+ const CONST_SWITCH_BONUS : usize = 10 ;
912
1013/// Verify that the callee body is compatible with the caller.
1114#[ derive( Clone ) ]
1215pub ( crate ) struct CostChecker < ' b , ' tcx > {
1316 tcx : TyCtxt < ' tcx > ,
1417 param_env : ParamEnv < ' tcx > ,
15- cost : usize ,
18+ penalty : usize ,
19+ bonus : usize ,
1620 callee_body : & ' b Body < ' tcx > ,
1721 instance : Option < ty:: Instance < ' tcx > > ,
1822}
@@ -24,11 +28,11 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
2428 instance : Option < ty:: Instance < ' tcx > > ,
2529 callee_body : & ' b Body < ' tcx > ,
2630 ) -> 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 }
2832 }
2933
3034 pub fn cost ( & self ) -> usize {
31- self . cost
35+ usize :: saturating_sub ( self . penalty , self . bonus )
3236 }
3337
3438 fn instantiate_ty ( & self , v : Ty < ' tcx > ) -> Ty < ' tcx > {
@@ -41,60 +45,100 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
4145}
4246
4347impl < ' 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 .
4650 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 ,
5273 }
5374 }
5475
5576 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, .. } => {
5979 // 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 ;
6383 if let UnwindAction :: Cleanup ( _) = unwind {
64- self . cost += LANDINGPAD_PENALTY ;
84+ self . penalty += LANDINGPAD_PENALTY ;
6585 }
66- } else {
67- self . cost += INSTR_COST ;
6886 }
6987 }
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 ( )
7491 {
7592 // Don't give intrinsics the extra penalty for calls
7693 INSTR_COST
7794 } else {
7895 CALL_PENALTY
7996 } ;
8097 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 ;
82111 }
83112 }
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+ } ;
86120 if let UnwindAction :: Cleanup ( _) = unwind {
87- self . cost += LANDINGPAD_PENALTY ;
121+ self . penalty += LANDINGPAD_PENALTY ;
88122 }
89123 }
90- TerminatorKind :: UnwindResume => self . cost += RESUME_PENALTY ,
124+ TerminatorKind :: UnwindResume => self . penalty += RESUME_PENALTY ,
91125 TerminatorKind :: InlineAsm { unwind, .. } => {
92- self . cost += INSTR_COST ;
126+ self . penalty += INSTR_COST ;
93127 if let UnwindAction :: Cleanup ( _) = unwind {
94- self . cost += LANDINGPAD_PENALTY ;
128+ self . penalty += LANDINGPAD_PENALTY ;
95129 }
96130 }
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+ }
98142 }
99143 }
100144}
0 commit comments