@@ -6,13 +6,16 @@ const INSTR_COST: usize = 5;
66const CALL_PENALTY : usize = 25 ;
77const LANDINGPAD_PENALTY : usize = 50 ;
88const RESUME_PENALTY : usize = 45 ;
9+ const LARGE_SWITCH_PENALTY : usize = 20 ;
10+ const CONST_SWITCH_BONUS : usize = 30 ;
911
1012/// Verify that the callee body is compatible with the caller.
1113#[ derive( Clone ) ]
1214pub ( crate ) struct CostChecker < ' b , ' tcx > {
1315 tcx : TyCtxt < ' tcx > ,
1416 param_env : ParamEnv < ' tcx > ,
15- cost : usize ,
17+ penalty : usize ,
18+ bonus : usize ,
1619 callee_body : & ' b Body < ' tcx > ,
1720 instance : Option < ty:: Instance < ' tcx > > ,
1821}
@@ -24,11 +27,11 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
2427 instance : Option < ty:: Instance < ' tcx > > ,
2528 callee_body : & ' b Body < ' tcx > ,
2629 ) -> 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 }
2831 }
2932
3033 pub fn cost ( & self ) -> usize {
31- self . cost
34+ usize :: saturating_sub ( self . penalty , self . bonus )
3235 }
3336
3437 fn instantiate_ty ( & self , v : Ty < ' tcx > ) -> Ty < ' tcx > {
@@ -41,14 +44,31 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
4144}
4245
4346impl < ' 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 .
4649 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 ,
5272 }
5373 }
5474
@@ -59,17 +79,17 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
5979 // If the place doesn't actually need dropping, treat it like a regular goto.
6080 let ty = self . instantiate_ty ( place. ty ( self . callee_body , tcx) . ty ) ;
6181 if ty. needs_drop ( tcx, self . param_env ) {
62- self . cost += CALL_PENALTY ;
82+ self . penalty += CALL_PENALTY ;
6383 if let UnwindAction :: Cleanup ( _) = unwind {
64- self . cost += LANDINGPAD_PENALTY ;
84+ self . penalty += LANDINGPAD_PENALTY ;
6585 }
6686 } else {
67- self . cost += INSTR_COST ;
87+ self . penalty += INSTR_COST ;
6888 }
6989 }
7090 TerminatorKind :: Call { func : Operand :: Constant ( ref f) , unwind, .. } => {
7191 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 ( )
7393 && tcx. intrinsic ( def_id) . is_some ( )
7494 {
7595 // Don't give intrinsics the extra penalty for calls
@@ -78,23 +98,40 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
7898 CALL_PENALTY
7999 } ;
80100 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 ;
82114 }
83115 }
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+ } ;
86123 if let UnwindAction :: Cleanup ( _) = unwind {
87- self . cost += LANDINGPAD_PENALTY ;
124+ self . penalty += LANDINGPAD_PENALTY ;
88125 }
89126 }
90- TerminatorKind :: UnwindResume => self . cost += RESUME_PENALTY ,
127+ TerminatorKind :: UnwindResume => self . penalty += RESUME_PENALTY ,
91128 TerminatorKind :: InlineAsm { unwind, .. } => {
92- self . cost += INSTR_COST ;
129+ self . penalty += INSTR_COST ;
93130 if let UnwindAction :: Cleanup ( _) = unwind {
94- self . cost += LANDINGPAD_PENALTY ;
131+ self . penalty += LANDINGPAD_PENALTY ;
95132 }
96133 }
97- _ => self . cost += INSTR_COST ,
134+ _ => self . penalty += INSTR_COST ,
98135 }
99136 }
100137}
0 commit comments