1+ use rustc_middle:: bug;
12use rustc_middle:: mir:: visit:: * ;
23use rustc_middle:: mir:: * ;
34use rustc_middle:: ty:: { self , ParamEnv , Ty , TyCtxt } ;
@@ -6,6 +7,8 @@ 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 ) ]
@@ -42,36 +45,49 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
4245}
4346
4447impl < ' tcx > Visitor < ' tcx > for CostChecker < ' _ , ' tcx > {
45- fn visit_statement ( & mut self , statement : & Statement < ' tcx > , _ : Location ) {
46- // 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 .
4750 match statement. kind {
48- StatementKind :: StorageLive ( _)
49- | StatementKind :: StorageDead ( _)
50- | StatementKind :: Deinit ( _)
51- | StatementKind :: Nop => { }
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 ( ..) => { }
5272 _ => self . penalty += INSTR_COST ,
5373 }
5474 }
5575
5676 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , _: Location ) {
57- let tcx = self . tcx ;
58- match terminator. kind {
59- TerminatorKind :: Drop { ref place, unwind, .. } => {
77+ match & terminator. kind {
78+ TerminatorKind :: Drop { place, unwind, .. } => {
6079 // If the place doesn't actually need dropping, treat it like a regular goto.
61- let ty = self . instantiate_ty ( place. ty ( self . callee_body , tcx) . ty ) ;
62- if ty. needs_drop ( tcx, self . param_env ) {
80+ let ty = self . instantiate_ty ( place. ty ( self . callee_body , self . tcx ) . ty ) ;
81+ if ty. needs_drop ( self . tcx , self . param_env ) {
6382 self . penalty += CALL_PENALTY ;
6483 if let UnwindAction :: Cleanup ( _) = unwind {
6584 self . penalty += LANDINGPAD_PENALTY ;
6685 }
67- } else {
68- self . penalty += INSTR_COST ;
6986 }
7087 }
71- TerminatorKind :: Call { func : Operand :: Constant ( ref f) , unwind, .. } => {
72- let fn_ty = self . instantiate_ty ( f. const_ . ty ( ) ) ;
73- self . penalty += if let ty:: FnDef ( def_id, _) = * fn_ty. kind ( )
74- && 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 ( )
7591 {
7692 // Don't give intrinsics the extra penalty for calls
7793 INSTR_COST
@@ -82,8 +98,25 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
8298 self . penalty += LANDINGPAD_PENALTY ;
8399 }
84100 }
85- TerminatorKind :: Assert { unwind, .. } => {
86- self . penalty += CALL_PENALTY ;
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 ;
111+ }
112+ }
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+ } ;
87120 if let UnwindAction :: Cleanup ( _) = unwind {
88121 self . penalty += LANDINGPAD_PENALTY ;
89122 }
@@ -95,7 +128,17 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
95128 self . penalty += LANDINGPAD_PENALTY ;
96129 }
97130 }
98- _ => self . penalty += 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+ }
99142 }
100143 }
101144}
0 commit comments