@@ -392,7 +392,9 @@ impl<'tcx> Inliner<'tcx> {
392
392
callee_body : & Body < ' tcx > ,
393
393
callee_attrs : & CodegenFnAttrs ,
394
394
) -> Result < ( ) , & ' static str > {
395
- let tcx = self . tcx ;
395
+ let cost_info = body_cost ( self . tcx , self . param_env , callee_body, |ty| {
396
+ callsite. callee . subst_mir ( self . tcx , & ty)
397
+ } ) ;
396
398
397
399
let mut threshold = if callee_attrs. requests_inline ( ) {
398
400
self . tcx . sess . opts . unstable_opts . inline_mir_hint_threshold . unwrap_or ( 100 )
@@ -403,123 +405,16 @@ impl<'tcx> Inliner<'tcx> {
403
405
// Give a bonus functions with a small number of blocks,
404
406
// We normally have two or three blocks for even
405
407
// very small functions.
406
- if callee_body . basic_blocks ( ) . len ( ) <= 3 {
408
+ if cost_info . bbcount <= 3 {
407
409
threshold += threshold / 4 ;
408
410
}
409
411
debug ! ( " final inline threshold = {}" , threshold) ;
410
412
411
- // FIXME: Give a bonus to functions with only a single caller
412
- let mut first_block = true ;
413
- let mut cost = 0 ;
414
-
415
- // Traverse the MIR manually so we can account for the effects of
416
- // inlining on the CFG.
417
- let mut work_list = vec ! [ START_BLOCK ] ;
418
- let mut visited = BitSet :: new_empty ( callee_body. basic_blocks ( ) . len ( ) ) ;
419
- while let Some ( bb) = work_list. pop ( ) {
420
- if !visited. insert ( bb. index ( ) ) {
421
- continue ;
422
- }
423
- let blk = & callee_body. basic_blocks ( ) [ bb] ;
424
-
425
- for stmt in & blk. statements {
426
- // Don't count StorageLive/StorageDead in the inlining cost.
427
- match stmt. kind {
428
- StatementKind :: StorageLive ( _)
429
- | StatementKind :: StorageDead ( _)
430
- | StatementKind :: Deinit ( _)
431
- | StatementKind :: Nop => { }
432
- _ => cost += INSTR_COST ,
433
- }
434
- }
435
- let term = blk. terminator ( ) ;
436
- let mut is_drop = false ;
437
- match term. kind {
438
- TerminatorKind :: Drop { ref place, target, unwind }
439
- | TerminatorKind :: DropAndReplace { ref place, target, unwind, .. } => {
440
- is_drop = true ;
441
- work_list. push ( target) ;
442
- // If the place doesn't actually need dropping, treat it like
443
- // a regular goto.
444
- let ty = callsite. callee . subst_mir ( self . tcx , & place. ty ( callee_body, tcx) . ty ) ;
445
- if ty. needs_drop ( tcx, self . param_env ) {
446
- cost += CALL_PENALTY ;
447
- if let Some ( unwind) = unwind {
448
- cost += LANDINGPAD_PENALTY ;
449
- work_list. push ( unwind) ;
450
- }
451
- } else {
452
- cost += INSTR_COST ;
453
- }
454
- }
455
-
456
- TerminatorKind :: Unreachable | TerminatorKind :: Call { target : None , .. }
457
- if first_block =>
458
- {
459
- // If the function always diverges, don't inline
460
- // unless the cost is zero
461
- threshold = 0 ;
462
- }
463
-
464
- TerminatorKind :: Call { func : Operand :: Constant ( ref f) , cleanup, .. } => {
465
- if let ty:: FnDef ( def_id, _) =
466
- * callsite. callee . subst_mir ( self . tcx , & f. literal . ty ( ) ) . kind ( )
467
- {
468
- // Don't give intrinsics the extra penalty for calls
469
- if tcx. is_intrinsic ( def_id) {
470
- cost += INSTR_COST ;
471
- } else {
472
- cost += CALL_PENALTY ;
473
- }
474
- } else {
475
- cost += CALL_PENALTY ;
476
- }
477
- if cleanup. is_some ( ) {
478
- cost += LANDINGPAD_PENALTY ;
479
- }
480
- }
481
- TerminatorKind :: Assert { cleanup, .. } => {
482
- cost += CALL_PENALTY ;
483
-
484
- if cleanup. is_some ( ) {
485
- cost += LANDINGPAD_PENALTY ;
486
- }
487
- }
488
- TerminatorKind :: Resume => cost += RESUME_PENALTY ,
489
- TerminatorKind :: InlineAsm { cleanup, .. } => {
490
- cost += INSTR_COST ;
491
-
492
- if cleanup. is_some ( ) {
493
- cost += LANDINGPAD_PENALTY ;
494
- }
495
- }
496
- _ => cost += INSTR_COST ,
497
- }
498
-
499
- if !is_drop {
500
- for succ in term. successors ( ) {
501
- work_list. push ( succ) ;
502
- }
503
- }
504
-
505
- first_block = false ;
413
+ if cost_info. diverges {
414
+ threshold = 0 ;
506
415
}
507
416
508
- // Count up the cost of local variables and temps, if we know the size
509
- // use that, otherwise we use a moderately-large dummy cost.
510
-
511
- let ptr_size = tcx. data_layout . pointer_size . bytes ( ) ;
512
-
513
- for v in callee_body. vars_and_temps_iter ( ) {
514
- let ty = callsite. callee . subst_mir ( self . tcx , & callee_body. local_decls [ v] . ty ) ;
515
- // Cost of the var is the size in machine-words, if we know
516
- // it.
517
- if let Some ( size) = type_size_of ( tcx, self . param_env , ty) {
518
- cost += ( ( size + ptr_size - 1 ) / ptr_size) as usize ;
519
- } else {
520
- cost += UNKNOWN_SIZE_COST ;
521
- }
522
- }
417
+ let cost = cost_info. cost ;
523
418
524
419
if let InlineAttr :: Always = callee_attrs. inline {
525
420
debug ! ( "INLINING {:?} because inline(always) [cost={}]" , callsite, cost) ;
@@ -1012,3 +907,130 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
1012
907
}
1013
908
}
1014
909
}
910
+
911
+ pub struct InlineCostInfo {
912
+ pub cost : usize ,
913
+ pub bbcount : usize ,
914
+ pub diverges : bool ,
915
+ }
916
+
917
+ pub fn body_cost < ' tcx > (
918
+ tcx : TyCtxt < ' tcx > ,
919
+ param_env : ty:: ParamEnv < ' tcx > ,
920
+ callee_body : & Body < ' tcx > ,
921
+ subst_mir : impl Fn ( Ty < ' tcx > ) -> Ty < ' tcx > ,
922
+ ) -> InlineCostInfo {
923
+ // FIXME: Give a bonus to functions with only a single caller
924
+ let mut diverges = false ;
925
+ let mut first_block = true ;
926
+ let mut cost = 0 ;
927
+
928
+ // Traverse the MIR manually so we can account for the effects of
929
+ // inlining on the CFG.
930
+ let mut work_list = vec ! [ START_BLOCK ] ;
931
+ let mut visited = BitSet :: new_empty ( callee_body. basic_blocks ( ) . len ( ) ) ;
932
+ while let Some ( bb) = work_list. pop ( ) {
933
+ if !visited. insert ( bb. index ( ) ) {
934
+ continue ;
935
+ }
936
+ let blk = & callee_body. basic_blocks ( ) [ bb] ;
937
+
938
+ for stmt in & blk. statements {
939
+ // Don't count StorageLive/StorageDead in the inlining cost.
940
+ match stmt. kind {
941
+ StatementKind :: StorageLive ( _)
942
+ | StatementKind :: StorageDead ( _)
943
+ | StatementKind :: Deinit ( _)
944
+ | StatementKind :: Nop => { }
945
+ _ => cost += INSTR_COST ,
946
+ }
947
+ }
948
+ let term = blk. terminator ( ) ;
949
+ let mut is_drop = false ;
950
+ match term. kind {
951
+ TerminatorKind :: Drop { ref place, target, unwind }
952
+ | TerminatorKind :: DropAndReplace { ref place, target, unwind, .. } => {
953
+ is_drop = true ;
954
+ work_list. push ( target) ;
955
+ // If the place doesn't actually need dropping, treat it like
956
+ // a regular goto.
957
+ let ty = subst_mir ( place. ty ( callee_body, tcx) . ty ) ;
958
+ if ty. needs_drop ( tcx, param_env) {
959
+ cost += CALL_PENALTY ;
960
+ if let Some ( unwind) = unwind {
961
+ cost += LANDINGPAD_PENALTY ;
962
+ work_list. push ( unwind) ;
963
+ }
964
+ } else {
965
+ cost += INSTR_COST ;
966
+ }
967
+ }
968
+
969
+ TerminatorKind :: Unreachable | TerminatorKind :: Call { target : None , .. }
970
+ if first_block =>
971
+ {
972
+ // If the function always diverges, don't inline
973
+ // unless the cost is zero
974
+ diverges = true ;
975
+ }
976
+
977
+ TerminatorKind :: Call { func : Operand :: Constant ( ref f) , cleanup, .. } => {
978
+ if let ty:: FnDef ( def_id, _) = * subst_mir ( f. literal . ty ( ) ) . kind ( ) {
979
+ // Don't give intrinsics the extra penalty for calls
980
+ if tcx. is_intrinsic ( def_id) {
981
+ cost += INSTR_COST ;
982
+ } else {
983
+ cost += CALL_PENALTY ;
984
+ }
985
+ } else {
986
+ cost += CALL_PENALTY ;
987
+ }
988
+ if cleanup. is_some ( ) {
989
+ cost += LANDINGPAD_PENALTY ;
990
+ }
991
+ }
992
+ TerminatorKind :: Assert { cleanup, .. } => {
993
+ cost += CALL_PENALTY ;
994
+
995
+ if cleanup. is_some ( ) {
996
+ cost += LANDINGPAD_PENALTY ;
997
+ }
998
+ }
999
+ TerminatorKind :: Resume => cost += RESUME_PENALTY ,
1000
+ TerminatorKind :: InlineAsm { cleanup, .. } => {
1001
+ cost += INSTR_COST ;
1002
+
1003
+ if cleanup. is_some ( ) {
1004
+ cost += LANDINGPAD_PENALTY ;
1005
+ }
1006
+ }
1007
+ _ => cost += INSTR_COST ,
1008
+ }
1009
+
1010
+ if !is_drop {
1011
+ for succ in term. successors ( ) {
1012
+ work_list. push ( succ) ;
1013
+ }
1014
+ }
1015
+
1016
+ first_block = false ;
1017
+ }
1018
+
1019
+ // Count up the cost of local variables and temps, if we know the size
1020
+ // use that, otherwise we use a moderately-large dummy cost.
1021
+
1022
+ let ptr_size = tcx. data_layout . pointer_size . bytes ( ) ;
1023
+
1024
+ for v in callee_body. vars_and_temps_iter ( ) {
1025
+ let ty = subst_mir ( callee_body. local_decls [ v] . ty ) ;
1026
+ // Cost of the var is the size in machine-words, if we know
1027
+ // it.
1028
+ if let Some ( size) = type_size_of ( tcx, param_env, ty) {
1029
+ cost += ( ( size + ptr_size - 1 ) / ptr_size) as usize ;
1030
+ } else {
1031
+ cost += UNKNOWN_SIZE_COST ;
1032
+ }
1033
+ }
1034
+
1035
+ InlineCostInfo { cost, diverges, bbcount : callee_body. basic_blocks ( ) . len ( ) }
1036
+ }
0 commit comments